Linux中1号进程的创建详解
发布时间:2021-11-21 15:54:55 所属栏目:教程 来源:互联网
导读:在init/main.c中的main函数中可以发现如下语句: if (!fork()) { init();//1号进程要运行的代码 } for(;;) pause();//0号进程要运行的代码 上面的注释中已经写的很清楚了,1号进程的创建是通过调用fork函数创建的,然后运行相应的init()函数,init函数即为进
|
在init/main.c中的main函数中可以发现如下语句: if (!fork()) { init();//1号进程要运行的代码 } for(;;) pause();//0号进程要运行的代码 上面的注释中已经写的很清楚了,1号进程的创建是通过调用fork函数创建的,然后运行相应的init()函数,init函数即为进程1的主体,fork函数的声明位于include/unistd.h中 int fork(void); 可知fork函数是一个系统调用,其实现是通过相应的汇编代码来实现的(linux 0.11/0.12) _sys_fork: call _find_empty_process//为进程找到一个空进程号 testl %eax,%eax js 1f push %gs pushl %esi pushl %edi pushl %ebp pushl %eax call _copy_process//开始拷贝一个进程 addl %20,%esp addl $20,%esp 1: ret 看到了吧,其实汇编代码还是很好理解的,为了理解上述代码,我们还必须要看的一个文件就是kernel/fork.c这个文件,这个文件主要涉及的是进程相关的创建,拷贝操作 //先找到一个目前没有使用的PID,然后任务表中找到一个目前没有使用的空间 //将该空间的索引号返回,如果没有找到空间就返回-EAGAIN(-11) //last_pid是一个全局变量被设置成0 int find_empty_process(void) { int i; repeat: if ((++last_pid)<0) last_pid=1;//只要在last_pid超过了数据的表示范围,才会出现last_pid<0 //找到没有使用的pid号,个人觉的写的不好,浪费时间 for(i=0 ; i<NR_TASKS ; i++) if (task[i] && task[i]->pid == last_pid) goto repeat; //看看task_struct中有没有空闲的PCB块,如果有的话就返回其索引号 for(i=1 ; i<NR_TASKS ; i++) if (!task[i]) return i; return -EAGAIN; } 下面是另一个比较关键的函数copy_process //这个函数可以说是fork的核心,复制父进程的操作是由它完成的,按照执行顺序可以将其分解成 //5个部份,进程常规变量的设定,TSS的设置 ,分配内存,文件设定及其它 //具有17个参数 int copy_process(int nr,long ebp,long edi,long esi,long gs,long none, long ebx,long ecx,long edx, long fs,long es,long ds, long eip,long cs,long eflags,long esp,long ss) { struct task_struct *p; //其中p是任务数据结构指针,tss是任务状态段结构,内核为新任务申请内存用作保存其 //task_struct结构数据,而tss结构段是task_struct中的一个段,该任务的内核栈段值也被设置成为 //0x10,即内核数据段选择符,而tss.esp0则指向保存task_struct结构页面的未端。 int i; struct file *f; //为新的进程分配一页的内存空间 p = (struct task_struct *) get_free_page(); //如果分配失败就返回错误码 if (!p) return -EAGAIN; //将新进程空间的首地址赋给任务表 task[nr] = p; //由于当前进程就是父进程,所以这里就是对父进程的完整copy *p = *current; /* NOTE! this doesn't copy the supervisor stack */ //新进程的状态是不可中断的等待状态 p->state = TASK_UNINTERRUPTIBLE; //设置子进程的ID p->pid = last_pid; //设置子进程的父进程ID p->father = current->pid; //设置子进程与父进程的优先级相同 p->counter = p->priority; p->signal = 0; p->alarm = 0; p->leader = 0; /* process leadership doesn't inherit */ p->utime = p->stime = 0; p->cutime = p->cstime = 0; //设置进程的开始时间为当前的滴答数 p->start_time = jiffies; //设置新进程的TSS内容 //设置新进程的TSS的内容 p->tss.back_link = 0; p->tss.esp0 = PAGE_SIZE + (long) p;//栈底指针 //段选择符,表示的是段基地址 p->tss.ss0 = 0x10; p->tss.eip = eip; p->tss.eflags = eflags; p->tss.eax = 0; p->tss.ecx = ecx; p->tss.edx = edx; p->tss.ebx = ebx; p->tss.esp = esp; p->tss.ebp = ebp; p->tss.esi = esi; p->tss.edi = edi; p->tss.es = es & 0xffff; p->tss.cs = cs & 0xffff; p->tss.ss = ss & 0xffff; p->tss.ds = ds & 0xffff; p->tss.fs = fs & 0xffff; p->tss.gs = gs & 0xffff; p->tss.ldt = _LDT(nr); p->tss.trace_bitmap = 0x80000000; //如果当前任务使用了协处理器,就保存其上下文 if (last_task_used_math == current) __asm__("clts ; fnsave %0"::"m" (p->tss.i387)); if (copy_mem(nr,p)) { task[nr] = NULL; free_page((long) p); return -EAGAIN; } //如果父进程中有文件 是打开的,则将对应的文件打开次数增1 for (i=0; i<NR_OPEN;i++) if (f=p->filp[i]) f->f_count++; //当前进程的pwd,root,和executable引用次数均增1 if (current->pwd) current->pwd->i_count++; if (current->root) current->root->i_count++; if (current->executable) current->executable->i_count++; //在内存的TSS描述符表和LDT描述符表中建立新进程的描述符 //新进程的描述符对应的TSS与LDT是从进程中取得的 //目前的LDT与父进程的LDT是相同的 set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt)); //将进程设置进入就绪状态 p->state = TASK_RUNNING; /* do this last, just in case */ return last_pid; } 从这里应该可以在往回看,就应该明白了sys_fork是如何创建进程1的啦。 ![]() (编辑:开发网_郴州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |



浙公网安备 33038102330466号