fork源码分析

本文详细解析了Linux 0.11中fork系统调用的实现过程,从kernel/system_call.s的中断处理到_copy_process函数,涉及内核堆栈、进程复制和系统调用处理函数。在中断处理后,调用_find_empty_process分配新进程号,并通过_copy_process复制父进程的内存、数据段和代码段,最后返回子进程PID。
摘要由CSDN通过智能技术生成

1.kernel/system_call.s


207 .align 2
208 _sys_fork:
209     call _find_empty_process
210     testl %eax,%eax
211      js 1f
212     push %gs
213     pushl %esi
214     pushl %edi
215     pushl %ebp
216     pushl %eax
217     call _copy_process
218     addl $20,%esp
219 1:  ret
先看一下 _find_empty_process调用:
kernel/fork.c
135 int find_empty_process(void)
136 {
137     int i;
138
139     repeat:
140        if ((++last_pid)<0) last_pid=1;
141         for(i=0 ; i<NR_TASKS ; i++)
142             if (task[i] && task[i]->pid == last_pid) goto repeat;
143     for(i=1 ; i<NR_TASKS ; i++)
144         if (!task[i])
145             return i;
146     return -EAGAIN;
147 }
可以看到last_pid记录了最近使用的一个pid的下一个位置,140-142行的for循环是用来维护last_pid的,143-145是找一个目前的task没有用过的最小的pid,并返回它它在task中的位置。
我们继续:
212-216行进行参数压栈操作:
然后调用copy_process

 63 /*
 64  *  Ok, this is the main fork-routine. It copies the system process
 65  * information (task[nr]) and sets up the necessary registers. It
 66  * also copies the data segment in it's entirety.
 67  */
 68 int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
 69         long ebx,long ecx,long edx,
 70         long fs,long es,long ds,
 71         long eip,long cs,long eflags,long esp,long ss)
 72 {
 73     struct task_struct *p;
 74     int i;
 75     struct file *f;
 76
 77     p = (struct task_struct *) get_free_page();
获取一个新的内存页用于保存task_struct数据结构和作为内核栈使用,而且我们可以知道get_free_page得到的是一个页的起始地址,因此task_struct保存在一个页的起始位置处。

 78     if (!p)
 79         return -EAGAIN;

没有内存页可以分配,直接返回。

继续
 80     task[nr] = p;
 81     *p = *current;  /* NOTE! this doesn't copy the supervisor stack */
 82     p->state = TASK_UNINTERRUPTIBLE;
 83     p->pid = last_pid;
 84     p->father = current->pid;
 85     p->counter = p->priority;
 86     p->signal = 0;
 87     p->alarm = 0;
 88     p->leader = 0;      /* process leadership doesn't inherit */
 89     p->utime = p->stime = 0;
 90     p->cutime = p->cstime = 0;
 91     p->start_time = jiffies;
 92     p->tss.back_link = 0;
 93     p->tss.esp0 = PAGE_SIZE + (long) p;
这里可以看到把栈指针设置到了task_struct所在页的顶端。

 94     p->tss.ss0 = 0x10;
特权级0级,全局描述符表,索引2,也就是数据段。

 95     p->tss.eip = eip;
eip直接继承,所以返回后会执行与父进程相同的代码(调用fork的下一条指令)

 96     p->tss.eflags = eflags;
 97     p->tss.eax = 0;                   //所以子进程返回0?
 98     p->tss.ecx = ecx;
 99     p->tss.edx = edx;

100     p->tss.ebx = ebx;
101     p->tss.esp = esp;
102     p->tss.ebp = ebp;
103     p->tss.esi = esi;
104     p->tss.edi = edi;
105     p->tss.es = es & 0xffff;
106     p->tss.cs = cs & 0xffff;
107     p->tss.ss = ss & 0xffff;
108     p->tss.ds = ds & 0xffff;
109     p->tss.fs = fs & 0xffff;
110     p->tss.gs = gs & 0xffff;
以上是拷贝寄存器的操作。

111     p->tss.ldt = _LDT(nr);
看一下
include/linux/sched.h:156:#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
include/linux/sched.h:154:#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
include/linux/sched.h:153:#define FIRST_TSS_ENTRY 4
可见这里是为p->tss.ldt设置了在GDT中相应的索引(以字节为单位)。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值