Linux进程-进程的创建

今天学习了Linux的进程创建的基本原理,是基于0.11版本核心的。下面对其作一下简单的总结。


一、Linux进程在内存中的相关资源
   很容易理解,Linux进程的创建过程就是内存中进程相关资源产生的过程,那么Linux进程在内存中有哪些相关资源呢?
   1)task数组中的一项:一个指针指向进程的task_struct
   2)一页内存:task_struct(Linux进程控制块)和内核态堆栈
   3)页表:线型地址到物理地址的映射表
   4)页目录表项:指向页表
   5)全局描述表(GDT)中的两项:一项指向任务状态段(tss,在task_struct中),一项指向该进程的局部描述符表(ldt,在task_struct中)

   6)代码段、堆、栈、参数全局变量等数据区

二、进程控制块-task_struct
    上面提到的这几类资源中,很多都与task_struct有关,所以我想说一下task_struct。它是Linux的进程控制块,驻留在内存中,描述进程的基本信息,所以它是进程操作依据的数据结构,在进程中占有重要地位。下面列出在0.11版本中主要几类信息,对它应该就有了基本的了解。
      1)标识号:进程标识、用户标识、组标识。
      2) 信号
      3)状态: Linux的进程的四种基本状态
      4)调度信息:优先级、时间片、运行时间等
      5)进程链信息:父进程、子进程的指针
      6)文件信息:打开文件、当前目录
      7)局部描述符表(LDT):代码段、数据段、堆栈段的描述符
      8)任务状态段(tss):cpu相关寄存器的内容

三、任务状态段-tss
    在上述的task_struct中有一个结构:任务状态段(tss)。我们知道,计算机运行一个程序的时候的直接依据就是CPU的各个寄存器,TSS就是描述进程运行时候CPU的状态信息,当CPU进行进程切换的时候,会自动将这些信息更新到到乡音寄存器中,主要有下列信息:
     1)通用寄存器:eax,ecx,edx,ebx,esp,ebp,esi,edi
     2)段选择符字段:es,cs,ss,ds,fs和cs段寄存器的内容
     3)指令指针:EIP
     4)前一个任务的指针
     5)局部描述符表选择符:LDTR寄存器的内容
     6)页目录表基地址:CR3中的内容
     7)堆栈指针
     8)其它

四、进程创建过程简述
     Linux中一般进程都是由现有的一个进程创建的,也就是我们所说的父进程,子进程。具体的创建是通过fork()实现的。下面就让我们一起了解一下0.11核心中fork()的大体工作过程:
    1)在内存中申请一页内存存放进程控制块task_struct,并返回进程号nr,并在task数组的nr处存放task_struct的指针,还要将task的当前指针current指到nr处;
    2)将父进程的task_struct的内容复制到新进程的task_struct中作为模版
    3)对task_struct中的信息进行修改,主要进行一下工作:设置父进程、清除信号位图、时间片、运行时间、根据当前环境设置tss(内核态指针esp0指向task_struct所在页的顶端)、设置LDT的选择子等(根据nr指向GDT中相应的ldt描述符)。
    4)设置新进程的代码段、数据段的基地址和段长:更新task_struct中的代码开始地址:进程号(nr)×64M,更新task_struct中局部描述符表中的代码段和数据段描述符。
    5)复制父进程的页表目录项和页表:在页目录表中,复制父进程的页表目录项,目的地址由新进程的线性地址计算出来;对每个对应的页表目录项申请一个空闲页,并用页表地址更新页表目录项,最后将父进程页表中各项复制到新进程对
应的页表中,也就是说,这个时候,子进程与父进程共享物理内存。
    6)更新task_struct中的文件信息:文件打开次数加1,父进程的当前目录引用数加1。
    7)设置TSS和LDT描述符项:在全局描述符表(GDT)中设置新任务的TSS描述符项和LDT段的描述符项,使TSS描述符项和LDT描述符项分别指向task_struct的TSS结构和LDT结构。
    8)将任务设置为就绪状态,向当前进程(父进程)返回新进程号。

五、结束语
    可以看出,fork()中,内核并不立刻为新进程分配代码和数据物理内存页,新进程与父进程共同使用父进程已有的代码和数据物理内存页面。只有当以后执行过程中由一个进程一写方式访问内存时候被访问的内存页面才会在写操作之前被
复制到新申请的内存页面中。
    另外在fork的最后是将任务设置成了就绪状态,由于fork()是一个系统调用,在系统调用部分system_call.s,可以看到在系统函数返回后,会调用调度函数schedule(),在schedule()中,就会检测到新进程的就绪状态,并用switch_to()切换到新进程进行执行。
上一篇:UE4使用UMG接口操作界面


下一篇:mysql远程连接/访问速度慢的解决方案