linux 0.11 fork

當調用fork的時候,因爲是系統調用,所以會調用_syscall0這個函數

#define _syscall0(type,name) \
  type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name)); \
if (__res >= 0) \
    return (type) __res; \
errno = -__res; \
return -1; \
}

這裏是一段彙編代碼,其中 int $0x80 是通過系統軟中斷陷入到內核,然後跳轉到system_call裏面執行。system_call是一段彙編代碼:

system_call:
    cmpl $nr_system_calls-1,%eax
    ja bad_sys_call
    push %ds
    push %es
    push %fs
    pushl %edx
    pushl %ecx      # push %ebx,%ecx,%edx as parameters
    pushl %ebx      # to the system call
    movl $0x10,%edx        # set up ds,es to kernel space
    mov %dx,%ds
    mov %dx,%es
    movl $0x17,%edx        # fs points to local data space
    mov %dx,%fs
    call *sys_call_table(,%eax,4)
    pushl %eax
    movl current,%eax
    cmpl $0,state(%eax)        # state
    jne reschedule
    cmpl $0,counter(%eax)      # counter
    je reschedule

這段彙編代碼的意思是_syscall0 調用的時候把參數壓入堆棧,然後在sys_call_table找到對應的函數,然後調用對應的函數.這裏是調用了對應的sys_fork.

.align 2
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
1:  ret

在sys_fork裏面先調用了find_empty_process,
find_empty_process的目的是找到最大的不重複的進程PID,用在copy_process用,
實現是使用一個全局變量last_pid保存最大的PID,當需要產生一個進程的時候,先遍歷進程鏈表
task,找出最大的PID,然後跳轉到repeat,進行+1操作,此時lastID就是最大的了。

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;
}

接下來把參數都壓入堆棧,然後調用copy_process這個函數,具體的實現:

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;
    //獲得空閒的頁表,一頁爲4k
    p = (struct task_struct *) get_free_page();
    if (!p)
        return -EAGAIN;
    //nr爲find_empty_process返回來的可用的任務號,把新的進程結構賦值給新的任務
    task[nr] = p;

    // NOTE!: the following statement now work with gcc 4.3.2 now, and you
    // must compile _THIS_ memcpy without no -O of gcc.#ifndef GCC4_3
    *p = *current;  /* NOTE! this doesn't copy the supervisor stack */
    //先置爲不可中斷休眠,避免不小心切換進來
    p->state = TASK_UNINTERRUPTIBLE; 
    //新子進程的pid 
    p->pid = last_pid;
    //父進程
    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;
    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));
    //調用copy_mem()函數根據父進程的代碼端和數據段的地址加上偏移量(nr * 0x4000000)重            //新設置子進程代碼段和數據段,並複製頁表
    if (copy_mem(nr,p)) {
        task[nr] = NULL;
        free_page((long) p);
        return -EAGAIN;
    }
    //假如文件是打開的,那該文件的系統文件打開表增一,父子進程能共享這些文件打開表,另外pwd等等這些引用都要增一
    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++;

    /*gdt的前四個分別是null,內核代碼段,內核數據段,系統調用段,加上FIRST_TSS_ENTRY 就是跳過前面四個段,接下來就是任務0TSS段和LDT段,任務1TSSLDT,以此類推,所以加上(nr<<1)就是指向了第nr個任務在GDT中的內容,第一句話設置了該任務的tss在GDT中的描述符,第二句設置了ldt在GDT的描述符,之後使任務變爲可運行,然後退出*/   
    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 */
    //返回進程ID
    return last_pid;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章