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调用:
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中的位置。
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();
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;
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;
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
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中相应的索引(以字节为单位)。