本文分析基于Linux 0.11内核,转载请表明出处http://blog.csdn.net/yming0221/archive/2011/06/05/6527337.aspx
Linux在move_to_user_mode()之后,进程0通过fork()产生子进程实际就是进程1(init进程)。
其中fork()是通过内嵌汇编的形式给出
[cpp] view plain copy print ?
- #define _syscall0(type,name) /
- type name(void) /
- { /
- long __res; /
- __asm__ volatile ( "int $0x80" /
- :"=a" (__res) /
- :"" (__NR_##name)); /
- if (__res >= 0) /
- return (type) __res; errno = -__res; /
- return -1;}
这样使用int 0x80中断,调用sys_fork系统调用来创建进程。详细过程如下:
系统在sched.c中sched_init()函数最后设置系统调用中断门
set_system_gate (0x80, &system_call);
设置系统调用的中断号。
通过int 0x80调用sys_fork()
其使用汇编实现
系统将堆栈的内容入栈,然后执行call _sys_call_table(,%eax,4)
调用地址 = _sys_call_table + %eax * 4
然后真正调用sys_fork()
[cpp] view plain copy print ?
- _sys_fork:
- call _find_empty_process # 调用find_empty_process()(kernel/fork.c,135)。
- testl %eax,%eax
- js 1f
- push %gs
- pushl %esi
- pushl %edi
- pushl %ebp
- pushl %eax
- call _copy_process # 调用C 函数copy_process()(kernel/fork.c,68)。
- addl $20,%esp # 丢弃这里所有压栈内容。
- 1: ret
然后调用find_empty_process()
[cpp] view plain copy print ?
- int find_empty_process (void)
- {
- int i;
- repeat:
- if ((++last_pid) < 0)
- last_pid = 1;
- for (i = 0; i < NR_TASKS; i++)
- if (task[i] && task[i]->pid == last_pid)
- goto repeat;
- for (i = 1; i < NR_TASKS; i++)
- if (!task[i])
- return i;
- return -EAGAIN;
- }
该函数设置last_pid为最后可用不重复的pid号,然后返回task[]数组中空闲的项的index,存放在EAX中。
再将相应的寄存器 入栈,作为C函数的参数,调用copy_process()
[cpp] view plain copy print ?
- 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;
- int i;
- struct file *f;
- p = (struct task_struct *) get_free_page ();
- if (!p)
- return -EAGAIN;
- task[nr] = p;
-
- *p = *current;
- (只复制当前进程内容)。
- p->state = TASK_UNINTERRUPTIBLE;
- p->pid = last_pid;
- p->father = current->pid;
- p->counter = p->priority;
- p->signal = 0;
- p->alarm = 0;
- p->leader = 0;
-
- p->utime = p->stime = 0;
- p->cutime = p->cstime = 0;
- p->start_time = jiffies;
-
- 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;
- }
-
- for (i = 0; i < NR_OPEN; i++)
- if (f = p->filp[i])
- f->f_count++;
-
- if (current->pwd)
- current->pwd->i_count++;
- if (current->root)
- current->root->i_count++;
- if (current->executable)
- current->executable->i_count++;
-
-
- 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;
-
- return last_pid;
- }
这段代码的执行内容是:首先为进程分配内存,然后将新任务的指针放入上步查到的空闲task[]数组项中,然后复制父进程的内容后修改当前
进程的一部分属性和tss(任务状态段),最后设置新进程的代码段和数据段,限长,在GDT 中设置新任务的TSS 和LDT 描述符项,数据从task 结构中取。在任务切换时,任务寄存器tr 由CPU 自动加载。
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;
这样,新进程就创建完毕了。
其中复制页表函数copy_mem()待续.......