Linux系统并没有对线程和进程进行特殊的区分。 对于Linux来说,线程无非就是一个特殊的进程。 后面我们会看到,它们都是由函数创建的,只是传入的参数不同。 当线程被创建时,它们共享内核资源。
在内核中,各个进程存储在其内核堆栈的末尾。 这样做是为了那些像x86这样寄存器较少的硬件架构可以只通过堆栈指针来计算他的位置,避免额外的寄存器进行特殊记录。 寄存器较弱的体系结构并不是引入结构的唯一原因。 这种新结构使得在汇编代码中计算其偏移变量变得容易。 由于slab分配器现在是动态生成的,因此只需要在堆栈底部(对于向下增长的堆栈)或堆栈顶部(对于向上增长的堆栈)创建一个新的结构。
[cpp] view plaincopy
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
从这个union可以看出,结构体和栈都是用两个页来存储的,而栈的大小正好是两个页,这也证明了上面的内容。
我们看看如何获取当前进程的指针
[cpp]view plaincopy
#define current get_current()
#define get_current() (current_thread_info()->task)
static inline struct thread_info *current_thread_info(void)
{
struct thread_info *ti;
ti = (void *)(percpu_read_stable(kernel_stack) +
KERNEL_STACK_OFFSET - THREAD_SIZE);
return ti;
}
其中是 per CPU变量
[cpp]view plaincopy
DEFINE_PER_CPU(unsigned long, kernel_stack) =
(unsigned long)&init_thread_union - KERNEL_STACK_OFFSET + THREAD_SIZE;
它是一个联合,定义了结构体和栈结构。 在32位平台上一般定义为4K,所以栈的大小实际上是4KB。 这是初始任务在核心中拥有的所有空间。 去掉占用空间后,就是任务在核心中实际拥有的栈的大小。 定义为5*8。 由于长,所以在程序运行时,栈底以上还有5*8*4B=200B的空间用来存放相关的环境参数。
内核栈采用了每个CPU data()的概念,每个处理器维护了一个之前在多个CPU之间共享的数据,比如当前运行的任务结构,这是之前在多个CPU之间共享的。 的。
关于内核进程初始化
首先我们要知道linux中的第一个进程是内核进程,pid为0,他是所有进程的父进程。 这个进程也被称为,或者空闲。 该过程被静态初始化并定义为:
view plaincopy
union thread_union init_thread_union __init_task_data =
{ INIT_THREAD_INFO(init_task) };
#define __init_task_data __attribute__((__section__(".data.init_task")))
这个进程的主要目的是保证至少有一个进程还在运行,也就是说,当没有进程在运行时,就是最后一个进程,也就是进程。 init进程就是从它派生出来的。
然后看相应的初始化。 在.s中,这里是地址加上(栈的大小)加载到esp中,然后加载到ss中。 32是x86默认的栈大小是8k,所以esp开头的值就是地址+栈大小。
[cpp] view plaincopy
lss stack_start,%esp
.data
ENTRY(stack_start)
.long init_thread_union+THREAD_SIZE
.long __BOOT_DS
需要C/C++ Linux服务器架构师学习资料私信《资料》(资料包括C/C++、Linux、技术、Nginx、、MySQL、Redis、、、ZK、流媒体、CDN、P2P、K8S、、TCP/IP、协程、DPDK等),免费分享