linux系统的1号进程,Linux中1号进程的创建剖析

在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

if (task[i] && task[i]->pid == last_pid) goto repeat;

//看看task_struct中有没有空闲的PCB块,如果有的话就返回其索引号

for(i=1 ; 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

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的啦。

0b1331709591d260c1c78e86d0c51c18.png

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值