linux 进程状态数值,linux0.11相关进程数据结构

Task_struct内核态堆栈pagePage+4K

2 task 数组

struct task_struct * task[NR_TASKS] = {&(init_task.task), };

这个数组中存储的是task_struct 结构的指针,但是实际上数组中的每一项都指着一块内存页。

3 任务段数据

struct tss_struct {

longback_link;/* 16 high bits zero */

longesp0;

longss0;/* 16 high bits zero */

longesp1;

longss1;/* 16 high bits zero */

longesp2;

longss2;/* 16 high bits zero */

longcr3;

longeip;

longeflags;

longeax,ecx,edx,ebx;

longesp;

longebp;

longesi;

longedi;

longes;/* 16 high bits zero */

longcs;/* 16 high bits zero */

longss;/* 16 high bits zero */

longds;/* 16 high bits zero */

longfs;/* 16 high bits zero */

longgs;/* 16 high bits zero */

longldt;/* 16 high bits zero */

longtrace_bitmap;/* bits: trace 0, bitmap 16-31 */

struct i387_struct i387;

};

4  进程控制块

struct task_struct {

/*----------------------- these are hardcoded - don't touch -----------------------*/

long state;//进程运行状态(-1不可运行,0可运行,>0以停止)

long counter;//任务运行时间片,递减到0是说明时间片用完

long priority;//任务运行优先数,刚开始是counter=priority

long signal;//任务的信号位图,信号值=偏移+1

struct sigaction sigaction[32];//信号执行属性结构,对应信号将要执行的操作和标志信息

long blocked;//信号屏蔽码

/*----------------------------------- various fields--------------------------------- */

int exit_code;//任务退出码,当任务结束时其父进程会读取

unsigned long start_code,end_code,end_data,brk,start_stack;

// start_code代码段起始的线性地址

// end_code代码段长度

// end_data代码段长度+数据段长度

// brk代码段长度+数据段长度+bss段长度

// start_stack堆栈段起始线性地址

long pid,father,pgrp,session,leader;

// pid进程号

// father父进程号

// pgrp父进程组号

// session会话号

// leader会话首领

unsigned short uid,euid,suid;

// uid用户标id

// euid有效用户id

// suid保存的用户id

unsigned short gid,egid,sgid;

// gid组id

// egid有效组id

// sgid保存组id

long alarm;//报警定时值

long utime,stime,cutime,cstime,start_time;

// utime用户态运行时间

// stime内核态运行时间

// cutime子进程用户态运行时间

// cstime子进程内核态运行时间

// start_time进程开始运行时刻

unsigned short used_math;//标志,是否使用了387协处理器

/* ----------------------------------file system info-------------------------------- */

int tty;//进程使用tty的子设备号,-1表示没有使用

unsigned short umask;//文件创建属性屏蔽码

struct m_inode * pwd;//当前工作目录的i节点

struct m_inode * root;//根目录的i节点

struct m_inode * executable;//可执行文件的i节点

unsigned long close_on_exec; //执行时关闭文件句柄位图标志

struct file * filp[NR_OPEN];//进程使用的文件

/*------------------ ldt for this task 0 - zero 1 - cs 2 - ds&ss -------------------*/

struct desc_struct ldt[3];//本任务的ldt表,0-空,1-代码段,2-数据和堆栈段

/* ---------------------------------tss for this task ---------------------------------*/

struct tss_struct tss;//本任务的tss段

};

5   linux进程结构

(1)  在linux中gdt中的每一项,都有两个表项,一个是ldt描述符,另一个是tss描述符。

(2)  在task数组中占有一项,每一项是一个物理页面,物理内存页面底端是进程控制块,内存页面的其余部分是内核态堆栈。

(3)  task数组中的表项和gdt中的表项是一一对应的。 对于一个在task数组中的任务项是nr的任务来说,它的tss描述符在gdt中描述符

的位置是,gdtr + 3*8 + 16 * nr ,ldt描述符在gdt中的描述符的位置是 gdtr + 3 * 8 + 16 * nr + 8 。

(4) 对应于表项为nr的进程,它对应的页目录项是16 * nr --------16 * (nr + 1) 。

6 进程0

进程0是一个特殊的进程,它是所有进程的祖先进程,所有其他的进程都是复制进程0或者其后代进程产生的。 但是进程0不是。

下面主要讲一下 进程0的创建顺序:

(1) 进程控制块和页目录页表的手动创建

以下就是一个任务的初始过程

cbef093dcc044b2793832001e2365e43.png#defineINIT_TASK \2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*state etc*/918e8df969f9f8c8d002f25cda86cade.png{0,15,15, \    

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*signals*/0,918e8df969f9f8c8d002f25cda86cade.png{918e8df969f9f8c8d002f25cda86cade.png{},},0, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*ec,brk

918e8df969f9f8c8d002f25cda86cade.png*/0,0,0,0,0,0, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*pid etc..*/0,-1,0,0,0, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*uid etc*/0,0,0,0,0,0, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*alarm*/0,0,0,0,0,0, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*math*/0, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*fs info*/-1,0022,NULL,NULL,NULL,0, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*filp*/918e8df969f9f8c8d002f25cda86cade.png{NULL,}, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png918e8df969f9f8c8d002f25cda86cade.png{ \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png918e8df969f9f8c8d002f25cda86cade.png{0,0}, \//ldt第0项是空f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*ldt*/918e8df969f9f8c8d002f25cda86cade.png{0x9f,0xc0fa00}, \//代码段长640K,基地0,G=1,D=1,DPL=3,P=1,TYPE=0x0af70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png918e8df969f9f8c8d002f25cda86cade.png{0x9f,0xc0f200}, \//数据段长640K,基地0,G=1, D=1, DPL=3,P=1, TYPE=0x024a5daaec04350a363f186a4d2c5ed6ce.png}, \

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*tss*/918e8df969f9f8c8d002f25cda86cade.png{0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\

df37983f39daa189b8c814e01a6a9011.png//esp0 = PAGE_SIZE+(long)&init_task    内核态堆栈指针初始化为页面最后

df37983f39daa189b8c814e01a6a9011.png//ss0 = 0x10    内核态堆栈的段选择符,指向系统数据段描述符,进程0的进程控制

df37983f39daa189b8c814e01a6a9011.png//块和内核态堆栈都在system模块中

df37983f39daa189b8c814e01a6a9011.png//cr3 = (long)&pg_dir 页目录表,其实linux0.11所有进程共享一个页目录表df37983f39daa189b8c814e01a6a9011.png0,0,0,0,0,0,0,0, \

df37983f39daa189b8c814e01a6a9011.png0,0,0x17,0x17,0x17,0x17,0x17,0x17, \

df37983f39daa189b8c814e01a6a9011.png     _LDT(0),0x80000000, \//ldt表选择符指向gdt中的LDT0处f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png918e8df969f9f8c8d002f25cda86cade.png{}\

4a5daaec04350a363f186a4d2c5ed6ce.png    }, \

0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

进程0的数据段基址为0,段限长为640KB ,代码段基址为0,段限长为640KB。任务0的数据段和代码段 和系统的代码段和数据段是重合的。

进程0的内核态堆栈和进程控制块都是位于系统模块内。

(2)在main模块中调用了,sched_init()函数加载了 进程0的进程0的tss段描述符,ldt段描述符,并且加载TR寄存器,使它指向进程0的tss段,这时候         进程0才完成了启动。

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*****************************************************************************/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*功能:    1.    初始化task数组和GDT(包括设置进程1的LDT和TSS)*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*2.    加载TR和IDTR寄存器*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*3.    设置时钟中断门和系统调用中断门*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*参数:    (无)*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*返回:    (无)*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*****************************************************************************/cbef093dcc044b2793832001e2365e43.pngvoidsched_init(void)

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.pnginti;

df37983f39daa189b8c814e01a6a9011.pngstructdesc_struct*p;

df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.pngif(sizeof(structsigaction)!=16)

df37983f39daa189b8c814e01a6a9011.png        panic("Struct sigaction MUST be 16 bytes");

df37983f39daa189b8c814e01a6a9011.png//在gdt中设置进程0的tss段描述符df37983f39daa189b8c814e01a6a9011.pngset_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));

df37983f39daa189b8c814e01a6a9011.png//在gdt中设置进程0的ldt段描述符df37983f39daa189b8c814e01a6a9011.pngset_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));

df37983f39daa189b8c814e01a6a9011.png//下面的循环把gdt和task中其他的项清空df37983f39daa189b8c814e01a6a9011.pngp=gdt+2+FIRST_TSS_ENTRY;

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngfor(i=1;i{

df37983f39daa189b8c814e01a6a9011.png        task[i]=NULL;

df37983f39daa189b8c814e01a6a9011.png        p->a=p->b=0;

df37983f39daa189b8c814e01a6a9011.png        p++;

df37983f39daa189b8c814e01a6a9011.png        p->a=p->b=0;

df37983f39daa189b8c814e01a6a9011.png        p++;

4a5daaec04350a363f186a4d2c5ed6ce.png    }f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png/**//*Clear NT, so that we won't have troubles with that later on*/df37983f39daa189b8c814e01a6a9011.png    __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");

df37983f39daa189b8c814e01a6a9011.png    ltr(0);//把进程0的tss段加载到TR寄存器df37983f39daa189b8c814e01a6a9011.pnglldt(0);//把进程0的ldt段加载到IDTR寄存器。

df37983f39daa189b8c814e01a6a9011.png//这是将gdt中进程0的ldt描述符对应的选择符加载到TR中。CPU将

df37983f39daa189b8c814e01a6a9011.png//选择符加载到可见部分,将tss的基地址和段长等加载到不可见部分。

df37983f39daa189b8c814e01a6a9011.png//TR寄存器只在这里明确加载一次,以后新任务ldt的加载是CPU根据

df37983f39daa189b8c814e01a6a9011.png//TSS段中LDT字段自动加载。

df37983f39daa189b8c814e01a6a9011.png//初始化8253定时器f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngoutb_p(0x36,0x43);/**//*binary, mode 3, LSB/MSB, ch 0*/f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png    outb_p(LATCH&0xff,0x40);/**//*LSB*/f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png    outb(LATCH>>8,0x40);/**//*MSB*/df37983f39daa189b8c814e01a6a9011.png    set_intr_gate(0x20,&timer_interrupt);//设置时钟中断门df37983f39daa189b8c814e01a6a9011.pngoutb(inb_p(0x21)&~0x01,0x21);

df37983f39daa189b8c814e01a6a9011.png    set_system_gate(0x80,&system_call);//设置系统调用中断门0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

(3)切换回用户态。

cbef093dcc044b2793832001e2365e43.png//把进程0从内核态切换到用户态去执行,使用的方法是模拟中断调用返回

cbef093dcc044b2793832001e2365e43.png//利用指令iret完成特权级的转变。cbef093dcc044b2793832001e2365e43.png#definemove_to_user_mode() \cbef093dcc044b2793832001e2365e43.png__asm__ ("movl %%esp,%%eax\n\t"\//当前堆栈指针保存到eax中cbef093dcc044b2793832001e2365e43.png"pushl $0x17\n\t"\//当前堆栈段选择符0x17入栈,它指向进程0的数据段描述符//因为进程0的代码段、数据段、内核代码段、数据段4者重

cbef093dcc044b2793832001e2365e43.png//合,所以它指向的仍然是内核模块区域。cbef093dcc044b2793832001e2365e43.png"pushl %%eax\n\t"\//把当前堆栈指针入栈。这样模拟外层堆栈的SS:ESP。

cbef093dcc044b2793832001e2365e43.png//由于进程0数据段选择符0x17对应的还是内核模块,和

cbef093dcc044b2793832001e2365e43.png//内核数据段选择符0x10的差别仅在与对应描述符的dpl和

cbef093dcc044b2793832001e2365e43.png//本身rpl的不同,所以外层堆栈指针指向的还是原来的堆栈

cbef093dcc044b2793832001e2365e43.png//即user_stackcbef093dcc044b2793832001e2365e43.png"pushfl\n\t"\//eflags入栈cbef093dcc044b2793832001e2365e43.png"pushl $0x0f\n\t"\//进程0代码段选择符入栈,模拟返回的CScbef093dcc044b2793832001e2365e43.png"pushl $1f\n\t"\//下面标号1的偏移地址入栈,模拟返回的EIP

cbef093dcc044b2793832001e2365e43.png//也是由于4段重合,所以这里返回的CS对应的段的基地址与

cbef093dcc044b2793832001e2365e43.png//内核代码段基地址一样,都是0,故将返回的CS:EIP就是下

cbef093dcc044b2793832001e2365e43.png//面标号1处。cbef093dcc044b2793832001e2365e43.png"iret\n"\//中断返回。由于当前CPL=0,将返回的CS的RPL=3,所以

cbef093dcc044b2793832001e2365e43.png//不仅仅要改变CS,EIP,还要发生堆栈切换(但实际上堆栈

cbef093dcc044b2793832001e2365e43.png//还是user_stack),同时CPL变成3。cbef093dcc044b2793832001e2365e43.png"1:\tmovl $0x17,%%eax\n\t"\//把数据段寄存器的值设为进程0的数据段cbef093dcc044b2793832001e2365e43.png"movw %%ax,%%ds\n\t"\

cbef093dcc044b2793832001e2365e43.png"movw %%ax,%%es\n\t"\

cbef093dcc044b2793832001e2365e43.png"movw %%ax,%%fs\n\t"\

cbef093dcc044b2793832001e2365e43.png"movw %%ax,%%gs"\

cbef093dcc044b2793832001e2365e43.png    :::"ax")

cbef093dcc044b2793832001e2365e43.png

6 用fork创建进程

除了进程0,所有其他的进程都是由fork()系统调用创建的,子进程是通过复制父进程的数据和代码而产生的。

创建结束之后,子进程与父进程的代码和数据共享,但是子进程有自己的进程控制块,内核堆栈和页表。

一个进程需要以下三中数据结构

(1) 进程控制块 task__struct 。

(2) gdt中的tss 和ldt描述符。

(3)页目录项和页表项。

所以fork系统调用的任务就是创建进程的上述三个部分。

sys_fork()函数分两步实现,第一步 首先调用,find_empty_process() 函数,第二步调用 copy_process()函数,复制进程。

cbef093dcc044b2793832001e2365e43.png_sys_fork:

cbef093dcc044b2793832001e2365e43.png//第一步,调用find_empty_process()函数,找task[]中的空闲项。

cbef093dcc044b2793832001e2365e43.png//找到后数组下标放在eax中。如果没找到直接跳转到ret指令。cbef093dcc044b2793832001e2365e43.pngcall _find_empty_process

cbef093dcc044b2793832001e2365e43.png    testl%eax,%eax

cbef093dcc044b2793832001e2365e43.png    js 1f

cbef093dcc044b2793832001e2365e43.png    push%gs//中断时没有入栈的寄存器入栈,

cbef093dcc044b2793832001e2365e43.png//作为copy_process() 函数的参数cbef093dcc044b2793832001e2365e43.pngpushl%esi

cbef093dcc044b2793832001e2365e43.png    pushl%edi

cbef093dcc044b2793832001e2365e43.png    pushl%ebp

cbef093dcc044b2793832001e2365e43.png    pushl%eax

cbef093dcc044b2793832001e2365e43.png//第二步,调用copy_process() 函数复制进程。cbef093dcc044b2793832001e2365e43.pngcall _copy_process    

cbef093dcc044b2793832001e2365e43.png    addl $20,%esp

cbef093dcc044b2793832001e2365e43.png1:    ret

cbef093dcc044b2793832001e2365e43.png

内存复制函数

cbef093dcc044b2793832001e2365e43.pngcopy_mem

cbef093dcc044b2793832001e2365e43.png

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*****************************************************************************/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*功能:设置新进程的LDT项(数据段描述符和代码段描述符)中的基地址部分*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*并且复制父进程(也就是当前进程)的页目录和页表,*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*实现父子进程数据代码共享*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*参数:    nr    新进程任务数组下标*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*p    新进程的进程控制块*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*返回:    0 (成功),    -ENOMEM(出错)*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*****************************************************************************/cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.pngintcopy_mem(intnr,structtask_struct*p)

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png    unsignedlongold_data_base,new_data_base,data_limit;

df37983f39daa189b8c814e01a6a9011.png    unsignedlongold_code_base,new_code_base,code_limit;

df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.png    code_limit=get_limit(0x0f);//取当前进程代码段长度df37983f39daa189b8c814e01a6a9011.pngdata_limit=get_limit(0x17);//取当前进程数据段长度df37983f39daa189b8c814e01a6a9011.pngold_code_base=get_base(current->ldt[1]);//取当前进程代码段基地址,这是线性地址df37983f39daa189b8c814e01a6a9011.pngold_data_base=get_base(current->ldt[2]);//取当前进程数据段基地址,这是线性地址

df37983f39daa189b8c814e01a6a9011.png//0.11进程代码段和数据段基地址必须重合df37983f39daa189b8c814e01a6a9011.pngif(old_data_base!=old_code_base)

df37983f39daa189b8c814e01a6a9011.png        panic("We don't support separate I&D");

df37983f39daa189b8c814e01a6a9011.png//0.11中数据段代码段的基地址是重合的,都是nr*64M(nr是task[]数组下标),所以

df37983f39daa189b8c814e01a6a9011.png//数据段的长度肯定大于代码段长度。而且 copy_page_tables()传入的是data_limit,这

df37983f39daa189b8c814e01a6a9011.png//把代码和数据都包含进去了。df37983f39daa189b8c814e01a6a9011.pngif(data_limit

df37983f39daa189b8c814e01a6a9011.png        panic("Bad data_limit");

df37983f39daa189b8c814e01a6a9011.png//新进程的代码段基地址 = 数据段基地址 = 64M*nrdf37983f39daa189b8c814e01a6a9011.pngnew_data_base=new_code_base=nr*0x4000000;

df37983f39daa189b8c814e01a6a9011.png//设置进程的起始线性地址df37983f39daa189b8c814e01a6a9011.pngp->start_code=new_code_base;

df37983f39daa189b8c814e01a6a9011.png//设置新进程的ldt项。在copy_process()中完全复制父进程的ldt,所以

df37983f39daa189b8c814e01a6a9011.png//只需重新设置ldt的基地址字段,其他字段和父进程一样df37983f39daa189b8c814e01a6a9011.pngset_base(p->ldt[1],new_code_base);

df37983f39daa189b8c814e01a6a9011.png    set_base(p->ldt[2],new_data_base);

df37983f39daa189b8c814e01a6a9011.png//把线性地址old_data_base处开始,一共data_limit个字节的内存对应的页目录、

df37983f39daa189b8c814e01a6a9011.png//页表复制到线性地址new_data_base。这里仅仅复制相关的页目录和页表,使它们

df37983f39daa189b8c814e01a6a9011.png//指向同一个物理页面,实现父子进程数据代码共享。f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngif(copy_page_tables(old_data_base,new_data_base,data_limit))918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png        free_page_tables(new_data_base,data_limit);

df37983f39daa189b8c814e01a6a9011.pngreturn-ENOMEM;

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.pngreturn0;

0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

复制进程

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*****************************************************************************/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*功能:复制进程,把当前进程current复制到task[nr]*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*参数:当前进程(current)内核堆栈的所有内容*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*当前进程内核堆栈保存了所有寄存器的值,在程序中要把这些寄存器的值*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*全部复制给子进程,从而给子进程创造和父进程一样的运行环境*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*返回:子进程pid*/2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*****************************************************************************/cbef093dcc044b2793832001e2365e43.pngintcopy_process(intnr,longebp,longedi,longesi,longgs,longnone,

cbef093dcc044b2793832001e2365e43.pnglongebx,longecx,longedx,

cbef093dcc044b2793832001e2365e43.pnglongfs,longes,longds,

cbef093dcc044b2793832001e2365e43.pnglongeip,longcs,longeflags,longesp,longss)

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.pngstructtask_struct*p;

df37983f39daa189b8c814e01a6a9011.pnginti;

df37983f39daa189b8c814e01a6a9011.pngstructfile*f;

df37983f39daa189b8c814e01a6a9011.png//在主内存区申请一页新的内存,用来放置子进程的task_struct和内核堆栈

df37983f39daa189b8c814e01a6a9011.png//get_free_page()返回的是物理地址df37983f39daa189b8c814e01a6a9011.pngp=(structtask_struct*) get_free_page();

df37983f39daa189b8c814e01a6a9011.pngif(!p)

df37983f39daa189b8c814e01a6a9011.pngreturn-EAGAIN;

df37983f39daa189b8c814e01a6a9011.png//设置task数组中相关项df37983f39daa189b8c814e01a6a9011.pngtask[nr]=p;

df37983f39daa189b8c814e01a6a9011.png//下面的赋值语句仅仅把父基础的task_struct部分全部复制给子进程

df37983f39daa189b8c814e01a6a9011.png//注意:仅仅复制task_struct部分,内核堆栈不复制,因此子程序的内核堆栈

df37983f39daa189b8c814e01a6a9011.png//是空的,这也是我们希望的f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png*p=*current;/**//*NOTE! this doesn't copy the supervisor stack*/df37983f39daa189b8c814e01a6a9011.png//下面的很多赋值语句修改子进程的task_struct中若干字段

df37983f39daa189b8c814e01a6a9011.png//这些字段跟父进程是有差别的df37983f39daa189b8c814e01a6a9011.pngp->state=TASK_UNINTERRUPTIBLE;//子进程设为不可中断状态df37983f39daa189b8c814e01a6a9011.pngp->pid=last_pid;//设置子进程piddf37983f39daa189b8c814e01a6a9011.pngp->father=current->pid;//把当前进程pid舍为子进程的fatherdf37983f39daa189b8c814e01a6a9011.pngp->counter=p->priority;//继承父亲的优先级df37983f39daa189b8c814e01a6a9011.pngp->signal=0;

df37983f39daa189b8c814e01a6a9011.png    p->alarm=0;

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png    p->leader=0;/**//*process leadership doesn't inherit*/df37983f39daa189b8c814e01a6a9011.png    p->utime=p->stime=0;

df37983f39daa189b8c814e01a6a9011.png    p->cutime=p->cstime=0;

df37983f39daa189b8c814e01a6a9011.png    p->start_time=jiffies;//子进程开始时间df37983f39daa189b8c814e01a6a9011.pngp->tss.back_link=0;

df37983f39daa189b8c814e01a6a9011.png//子进程的内核堆栈指针设置为task_struct所在页面的最高端df37983f39daa189b8c814e01a6a9011.pngp->tss.esp0=PAGE_SIZE+(long) p;    

df37983f39daa189b8c814e01a6a9011.png//子进程的内核堆栈选择符为0x10,指向GDT中系统数据段。

df37983f39daa189b8c814e01a6a9011.png//注意 虽然子进程的内核堆栈位于内核system模块外,在主内存区,但是因为系统数据段

df37983f39daa189b8c814e01a6a9011.png//基地址为0,限长为16M,函概了所有物理内存,故子进程内核堆栈也位于系统数

df37983f39daa189b8c814e01a6a9011.png//段内。esp0要的是段内偏移,也是因为系统数据段基地址为0,物理地址

df37983f39daa189b8c814e01a6a9011.png//PAGE_SIZE + (long) p 也是段内偏移。df37983f39daa189b8c814e01a6a9011.pngp->tss.ss0=0x10;

df37983f39daa189b8c814e01a6a9011.png//把父进程系统调用返回地址赋给子进程当前运行的eip。这样当子进程被调度程序选中

df37983f39daa189b8c814e01a6a9011.png//后他从fork返回地址处开始执行。df37983f39daa189b8c814e01a6a9011.pngp->tss.eip=eip;

df37983f39daa189b8c814e01a6a9011.png    p->tss.eflags=eflags;

df37983f39daa189b8c814e01a6a9011.png//eax是函数返回值存放的地方,把子进程的eax设置为0,这样fork在子进程中返回的是0。

df37983f39daa189b8c814e01a6a9011.png//注意 子进程并没有执行fork()函数,子进程的系统堆栈没有进行过操作,当然不会有像

df37983f39daa189b8c814e01a6a9011.png//父进程那样的fork函数调用。但是当子进程开始运行时,就好像它从fork中返回。df37983f39daa189b8c814e01a6a9011.pngp->tss.eax=0;        

df37983f39daa189b8c814e01a6a9011.png    p->tss.ecx=ecx;

df37983f39daa189b8c814e01a6a9011.png    p->tss.edx=edx;

df37983f39daa189b8c814e01a6a9011.png    p->tss.ebx=ebx;

df37983f39daa189b8c814e01a6a9011.png    p->tss.esp=esp;//用户堆栈指针和父进程一样,子进程完全复制父进程的用户堆栈df37983f39daa189b8c814e01a6a9011.pngp->tss.ebp=ebp;

df37983f39daa189b8c814e01a6a9011.png    p->tss.esi=esi;

df37983f39daa189b8c814e01a6a9011.png    p->tss.edi=edi;

df37983f39daa189b8c814e01a6a9011.png    p->tss.es=es&0xffff;

df37983f39daa189b8c814e01a6a9011.png    p->tss.cs=cs&0xffff;

df37983f39daa189b8c814e01a6a9011.png    p->tss.ss=ss&0xffff;

df37983f39daa189b8c814e01a6a9011.png    p->tss.ds=ds&0xffff;

df37983f39daa189b8c814e01a6a9011.png    p->tss.fs=fs&0xffff;

df37983f39daa189b8c814e01a6a9011.png    p->tss.gs=gs&0xffff;

df37983f39daa189b8c814e01a6a9011.png//设置子进程的ldt。从这里可以看到,task下标为nr的进程在GDT中的2项一定是

df37983f39daa189b8c814e01a6a9011.png//_LDT(nr)和_TSS(nr)。task[]中的项和GDT中的2项一一对应。df37983f39daa189b8c814e01a6a9011.pngp->tss.ldt=_LDT(nr);

df37983f39daa189b8c814e01a6a9011.png    p->tss.trace_bitmap=0x80000000;

df37983f39daa189b8c814e01a6a9011.pngif(last_task_used_math==current)

df37983f39daa189b8c814e01a6a9011.png        __asm__("clts ; fnsave %0"::"m"(p->tss.i387));

df37983f39daa189b8c814e01a6a9011.png//在copy_mem函数中设置子进程的代码段描述符,数据段描述符,并且复制父进程的

df37983f39daa189b8c814e01a6a9011.png//页目录、页表。实现和父进程代码数据的共享。f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngif(copy_mem(nr,p))918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png        task[nr]=NULL;

df37983f39daa189b8c814e01a6a9011.png        free_page((long) p);

df37983f39daa189b8c814e01a6a9011.pngreturn-EAGAIN;

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.png//子进程继承父进程打开的文件,所以文件引用数目要加一df37983f39daa189b8c814e01a6a9011.pngfor(i=0; i

df37983f39daa189b8c814e01a6a9011.pngif(f=p->filp[i])

df37983f39daa189b8c814e01a6a9011.png            f->f_count++;

df37983f39daa189b8c814e01a6a9011.png//子进程继承父进程的工作目录、根目录和可执行文件,所以引用数目加一df37983f39daa189b8c814e01a6a9011.pngif(current->pwd)

df37983f39daa189b8c814e01a6a9011.png        current->pwd->i_count++;

df37983f39daa189b8c814e01a6a9011.pngif(current->root)

df37983f39daa189b8c814e01a6a9011.png        current->root->i_count++;

df37983f39daa189b8c814e01a6a9011.pngif(current->executable)

df37983f39daa189b8c814e01a6a9011.png        current->executable->i_count++;

df37983f39daa189b8c814e01a6a9011.png//GDT中对应位置(和nr对应)放入子进程的TSS描述符、LDT描述符df37983f39daa189b8c814e01a6a9011.pngset_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));

df37983f39daa189b8c814e01a6a9011.png    set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));

df37983f39daa189b8c814e01a6a9011.png//最后把子进程的状态设置为可运行状态,这样子进程可以被调度f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngp->state=TASK_RUNNING;/**//*do this last, just in case*/df37983f39daa189b8c814e01a6a9011.png//父进程返回子进程的piddf37983f39daa189b8c814e01a6a9011.pngreturnlast_pid;

0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

7 进程的结束

进程结束的时候,需要关闭的资源主要有:

(1)  释放所有的物理页面。(子进程自己清除)

(2)  关闭所有打开的文件。(子进程自己清除)

(3)  清除task[] 中的相应的项。(父进程自己清除)

子进程通过exit()清除前面两个选项,将自身的状态变为TASK_ZOMBIE 。

父进程通过调用waitpid() 将task[] 数组清空。

一个进程的经过exit()之后,物理页表被清除 , 页表页目录项也被清除,但是它的进程控制块和内核堆栈还在,,

此时进程的状态变为TASK_ZOMBIE ,不会再被处理器处理。不被处理但是还占用着task数组中的一个表项,这

就成为了僵尸进程。

子进程调用了exit()函数之后,就通知父进程,父进程调用waitpid() 来清除 task数组中的表项。但是很有可能,

父进程没有执行waitpid()操作,情况如下:

(1) 父进程早于子进程执行exit()函数。

(2) 子进程僵死,但是父进程没有调用waitpid()操作。

(3) 父进程调用了waitpid(),但是因为某种愿意没有释放资源。

解决方法:

如果父进程无法释放资源,那么就让进程1来释放资源。

当一个父进程早于子进程exit()的时候,它把所有的子进程过继给父进程。

posted on 2010-11-13 20:50 kahn 阅读(1895) 评论(0)  编辑 收藏 引用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值