Linux把每一个进程抽象为一个task_struct结构体

linux woker process是什么 linux kworker进程_初始化

在结构体的开端定义了每个进程的状态(包括可执行状态、可中断等待状态、不可中断等待、被追踪状态、停止状态)、时间片、优先级和信号量等重要的信息

操作系统中有一个task链表,链表的节点内就是每一个进程的task_struct结构体,链表的长度定义是64位,具体排列结构如下图:

linux woker process是什么 linux kworker进程_子进程_02

 

每个进程的结构可以视为如下的结构图:

linux woker process是什么 linux kworker进程_初始化_03

进程的结构有所了解后,来看一下进程是如何创建的

首先Linux在初始化的过程中会进行0号进程的创建,它是所有进程的父进程,创建过程的核心是fork函数,想要查看这个函数需要先找到它在哪里运行,由于CPU在启动的时候就会创建0号进程,所以先找到CPU启动的函数,它是在main.c文件,找到void main()函数

         

linux woker process是什么 linux kworker进程_linux_04

         

linux woker process是什么 linux kworker进程_linux_05

前面几行是在完成Linux系统到内存的拷贝,后面是在完成各项内容的初始化,和进程初始化相关的就是sched_init(),由于Linux系统是分为内核态和用户态,两种状态的区别就是内核态不能做进程的切换,也就是说处于内核态的进程是不可抢占的,而处于用户态的进程是可以做这种切换的,也就是可以抢占的。所以上图中155~163行都是在内核态完成初始化的,直到move_to_user_mode()再切换到用户态运行,之后通过fork()函数创建0号进程,最后通过跳转init函数完成创建0号进程后续相关工作。

一、进程的初始化

接下来查看在init()函数中做了哪些事情

linux woker process是什么 linux kworker进程_初始化_06

首先设置了一些驱动的信息,接下来需要先讲一下open函数,图中可以看到它的三个参数,第一个参数是某个文件路径,第二个参数表示以读写的方式打开该文件,第三个参数表示文件的句柄(也可以是文件的存取权限),每个文件都有自己的句柄,可用数字代表这个文件,0代表标准输入,1代表标准输出,2代表标准出错,其他文件都只能从3开始往后的数字作为自己的句柄。

linux woker process是什么 linux kworker进程_链表_07

打开标准输入控制台、标准输出控制台和标准错误控制台之后,先判断fork()函数的返回值,fork()函数的特点是会创建一个与父进程一样的子进程,唯一不同就是fork()的返回值,父进程返回的是子进程的进程ID,而子进程返回的是0,上图的代码意思就是只有成功创建出来了一个子进程,返回值才会为0。创建出来了1号子进程之后,先打开“/etc/rc”文件,然后执行Shell程序“/bin/sh”;如果返回值大于0,表示是一个父进程,会先等待父进程的退出,进入while循环后,先关掉0、1、2三个句柄文件控制台,然后重新set进程的ID,

然后再把那三个控制台打开。fork()函数核心在于这两个判断,后面这段代码就是在第一种判断中创建进程失败的时候它才会发挥出它的作用,但是二者对于进程创建的代码实现过程是完全一样的。以上就是进程的初始化过程。

0号进程有一个特点就是永远不会结束,它会在没有进程的时候调用,它只会在init()函数的后面执行pause()暂停函数

linux woker process是什么 linux kworker进程_初始化_08

  • 进程的创建

首先在task链表中找到一个进程空位存放当前的进程,创建出一个进程的task_struct,然后设置task_struct。找到空位这步操作是在fork.c中的find_empty_process函数实现的:

linux woker process是什么 linux kworker进程_初始化_09

NR_TASKS是64,也就是上面所提到的链表容量就是64,如果当前有空位就将空位的ID分配给即将创建的进程,如果没有空位就返回EAGAIN

find_empty_process函数率先在sys_call.s文件中被调用:

linux woker process是什么 linux kworker进程_子进程_10

可以看到在调用完find_empty_process函数之后又调用了copy_process函数,copy_process函数会创建一个子进程task_struct结构体,并获取到一块空闲的内存页:

linux woker process是什么 linux kworker进程_linux_11

创建好结构体,后面就是设置这段结构体,很长一段,这里就不放截图了。接下来调用了一个copy_mem函数:

linux woker process是什么 linux kworker进程_链表_12

这里和上面的LDT局部描述符就可以产生关联了,主要是做老进程向新进程复制代码段和数据段的工作,设置进程的TSS段和LDT段,并且结合前面拷贝过来的数据,组装成一个进程,然后给当前的进程赋予TASK_RUNNING状态,并返回进程ID。