本文分析基于Linux 0.11內(nèi)核,轉(zhuǎn)載請(qǐng)表明出處http://blog.csdn.net/yming0221/archive/2011/06/05/6527337.aspx
Linux在move_to_user_mode()之后,,進(jìn)程0通過fork()產(chǎn)生子進(jìn)程實(shí)際就是進(jìn)程1(init進(jìn)程),。
其中fork()是通過內(nèi)嵌匯編的形式給出
- #define _syscall0(type,name) /
- type name(void) /
- { /
- long __res; /
- __asm__ volatile ( "int $0x80" / // 調(diào)用系統(tǒng)中斷0x80。
- :"=a" (__res) / // 返回值??eax(__res),。
- :"0" (__NR_##name)); / // 輸入為系統(tǒng)中斷調(diào)用號(hào)__NR_name,。
- if (__res >= 0) / // 如果返回值>=0,則直接返回該值,。
- return (type) __res; errno = -__res; / // 否則置出錯(cuò)號(hào),,并返回-1。
- return -1;}
這樣使用int 0x80中斷,調(diào)用sys_fork系統(tǒng)調(diào)用來創(chuàng)建進(jìn)程,。詳細(xì)過程如下:
系統(tǒng)在sched.c中sched_init()函數(shù)最后設(shè)置系統(tǒng)調(diào)用中斷門
set_system_gate (0x80, &system_call);
設(shè)置系統(tǒng)調(diào)用的中斷號(hào),。
通過int 0x80調(diào)用sys_fork()
其使用匯編實(shí)現(xiàn)
系統(tǒng)將堆棧的內(nèi)容入棧,然后執(zhí)行call _sys_call_table(,%eax,4)
調(diào)用地址 = _sys_call_table + %eax * 4
然后真正調(diào)用sys_fork()
- _sys_fork:
- call _find_empty_process # 調(diào)用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 # 調(diào)用C 函數(shù)copy_process()(kernel/fork.c,68),。
- addl $20,%esp # 丟棄這里所有壓棧內(nèi)容。
- 1: ret
然后調(diào)用find_empty_process()
- 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++) // 任務(wù)0 排除在外,。
- if (!task[i])
- return i;
- return -EAGAIN;
- }
該函數(shù)設(shè)置last_pid為最后可用不重復(fù)的pid號(hào),,然后返回task[]數(shù)組中空閑的項(xiàng)的index,存放在EAX中。
再將相應(yīng)的寄存器 入棧,,作為C函數(shù)的參數(shù),,調(diào)用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;
- p = (struct task_struct *) get_free_page (); // 為新任務(wù)數(shù)據(jù)結(jié)構(gòu)分配內(nèi)存。
- if (!p) // 如果內(nèi)存分配出錯(cuò),,則返回出錯(cuò)碼并退出,。
- return -EAGAIN;
- task[nr] = p; // 將新任務(wù)結(jié)構(gòu)指針放入任務(wù)數(shù)組中。
- // 其中nr 為任務(wù)號(hào),,由前面find_empty_process()返回,。
- *p = *current; /* NOTE! this doesn't copy the supervisor stack */
- /* 注意!這樣做不會(huì)復(fù)制超級(jí)用戶的堆棧 */ (只復(fù)制當(dāng)前進(jìn)程內(nèi)容),。
- p->state = TASK_UNINTERRUPTIBLE; // 將新進(jìn)程的狀態(tài)先置為不可中斷等待狀態(tài),。
- p->pid = last_pid; // 新進(jìn)程號(hào)。由前面調(diào)用find_empty_process()得到,。
- p->father = current->pid; // 設(shè)置父進(jìn)程號(hào),。
- p->counter = p->priority;
- p->signal = 0; // 信號(hào)位圖置0。
- p->alarm = 0;
- p->leader = 0; /* process leadership doesn't inherit */
- /* 進(jìn)程的領(lǐng)導(dǎo)權(quán)是不能繼承的 */
- p->utime = p->stime = 0; // 初始化用戶態(tài)時(shí)間和核心態(tài)時(shí)間,。
- p->cutime = p->cstime = 0; // 初始化子進(jìn)程用戶態(tài)和核心態(tài)時(shí)間,。
- p->start_time = jiffies; // 當(dāng)前滴答數(shù)時(shí)間。
- // 以下設(shè)置任務(wù)狀態(tài)段TSS 所需的數(shù)據(jù)(參見列表后說明),。
- p->tss.back_link = 0;
- p->tss.esp0 = PAGE_SIZE + (long) p; // 堆棧指針(由于是給任務(wù)結(jié)構(gòu)p 分配了1 頁
- // 新內(nèi)存,,所以此時(shí)esp0 正好指向該頁頂端)。
- p->tss.ss0 = 0x10; // 堆棧段選擇符(內(nèi)核數(shù)據(jù)段)[??],。
- p->tss.eip = eip; // 指令代碼指針,。
- p->tss.eflags = eflags; // 標(biāo)志寄存器。
- 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; // 段寄存器僅16 位有效,。
- 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); // 該新任務(wù)nr 的局部描述符表選擇符(LDT 的描述符在GDT 中),。
- p->tss.trace_bitmap = 0x80000000;
- // 如果當(dāng)前任務(wù)使用了協(xié)處理器,,就保存其上下文,。
- if (last_task_used_math == current)
- __asm__ ("clts ; fnsave %0"::"m" (p->tss.i387));
- // 設(shè)置新任務(wù)的代碼和數(shù)據(jù)段基址、限長(zhǎng)并復(fù)制頁表。如果出錯(cuò)(返回值不是0),,則復(fù)位任務(wù)數(shù)組中
- // 相應(yīng)項(xiàng)并釋放為該新任務(wù)分配的內(nèi)存頁,。
- if (copy_mem (nr, p))
- { // 返回不為0 表示出錯(cuò)。
- task[nr] = NULL;
- free_page ((long) p);
- return -EAGAIN;
- }
- // 如果父進(jìn)程中有文件是打開的,,則將對(duì)應(yīng)文件的打開次數(shù)增1,。
- for (i = 0; i < NR_OPEN; i++)
- if (f = p->filp[i])
- f->f_count++;
- // 將當(dāng)前進(jìn)程(父進(jìn)程)的pwd, root 和executable 引用次數(shù)均增1。
- if (current->pwd)
- current->pwd->i_count++;
- if (current->root)
- current->root->i_count++;
- if (current->executable)
- current->executable->i_count++;
- // 在GDT 中設(shè)置新任務(wù)的TSS 和LDT 描述符項(xiàng),,數(shù)據(jù)從task 結(jié)構(gòu)中取,。
- // 在任務(wù)切換時(shí),任務(wù)寄存器tr 由CPU 自動(dòng)加載,。
- 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 */
- /* 最后再將新任務(wù)設(shè)置成可運(yùn)行狀態(tài),,以防萬一 */
- return last_pid; // 返回新進(jìn)程號(hào)(與任務(wù)號(hào)是不同的)。
- }
這段代碼的執(zhí)行內(nèi)容是:首先為進(jìn)程分配內(nèi)存,,然后將新任務(wù)的指針放入上步查到的空閑task[]數(shù)組項(xiàng)中,,然后復(fù)制父進(jìn)程的內(nèi)容后修改當(dāng)前
進(jìn)程的一部分屬性和tss(任務(wù)狀態(tài)段),最后設(shè)置新進(jìn)程的代碼段和數(shù)據(jù)段,,限長(zhǎng),,在GDT 中設(shè)置新任務(wù)的TSS 和LDT 描述符項(xiàng),數(shù)據(jù)從task 結(jié)構(gòu)中取,。在任務(wù)切換時(shí),,任務(wù)寄存器tr 由CPU 自動(dòng)加載。
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;
這樣,,新進(jìn)程就創(chuàng)建完畢了,。
其中復(fù)制頁表函數(shù)copy_mem()待續(xù).......
|