?? fork.c
字號(hào):
/** linux/kernel/fork.c** (C) 1991 Linus Torvalds*/
/*
注意:signal.c和fork.c文件的編譯選項(xiàng)內(nèi)不能有vc變量?jī)?yōu)化選項(xiàng)/Og,因?yàn)檫@兩個(gè)文件
內(nèi)的函數(shù)參數(shù)內(nèi)包含了函數(shù)返回地址等內(nèi)容。如果加了/Og選項(xiàng),編譯器就會(huì)在認(rèn)為
這些參數(shù)不再使用后占用該內(nèi)存,導(dǎo)致函數(shù)返回時(shí)出錯(cuò)。
math/math_emulate.c照理也應(yīng)該這樣,不過好像它沒有把eip等參數(shù)優(yōu)化掉:)*/
#include <set_seg.h>
/** 'fork.c'中含有系統(tǒng)調(diào)用'fork'的輔助子程序(參見system_call.s),以及一些其它函數(shù)* ('verify_area')。一旦你了解了fork,就會(huì)發(fā)現(xiàn)它是非常簡(jiǎn)單的,但內(nèi)存管理卻有些難度。* 參見'mm/mm.c'中的'copy_page_tables()'。*/#include <errno.h> // 錯(cuò)誤號(hào)頭文件。包含系統(tǒng)中各種出錯(cuò)號(hào)。(Linus 從minix 中引進(jìn)的)。#include <linux/sched.h> // 調(diào)度程序頭文件,定義了任務(wù)結(jié)構(gòu)task_struct、初始任務(wù)0 的數(shù)據(jù),// 還有一些有關(guān)描述符參數(shù)設(shè)置和獲取的嵌入式匯編函數(shù)宏語句。#include <linux/kernel.h> // 內(nèi)核頭文件。含有一些內(nèi)核常用函數(shù)的原形定義。#include <asm/segment.h> // 段操作頭文件。定義了有關(guān)段寄存器操作的嵌入式匯編函數(shù)。#include <asm/system.h> // 系統(tǒng)頭文件。定義了設(shè)置或修改描述符/中斷門等的嵌入式匯編宏。extern void write_verify (unsigned long address);long last_pid = 0;//// 進(jìn)程空間區(qū)域?qū)懬膀?yàn)證函數(shù)。// 對(duì)當(dāng)前進(jìn)程的地址addr 到addr+size 這一段進(jìn)程空間以頁(yè)為單位執(zhí)行寫操作前的檢測(cè)操作。// 若頁(yè)面是只讀的,則執(zhí)行共享檢驗(yàn)和復(fù)制頁(yè)面操作(寫時(shí)復(fù)制)。void verify_area (void *addr, int size){ unsigned long start; start = (unsigned long) addr;// 將起始地址start 調(diào)整為其所在頁(yè)的左邊界開始位置,同時(shí)相應(yīng)地調(diào)整驗(yàn)證區(qū)域大小。// 此時(shí)start 是當(dāng)前進(jìn)程空間中的線性地址。 size += start & 0xfff; start &= 0xfffff000; start += get_base (current->ldt[2]);// 此時(shí)start 變成系統(tǒng)整個(gè)線性空間中的地址位置。 while (size > 0) { size -= 4096;// 寫頁(yè)面驗(yàn)證。若頁(yè)面不可寫,則復(fù)制頁(yè)面。(mm/memory.c,261 行) write_verify (start); start += 4096; }}// 設(shè)置新任務(wù)的代碼和數(shù)據(jù)段基址、限長(zhǎng)并復(fù)制頁(yè)表。// nr 為新任務(wù)號(hào);p 是新任務(wù)數(shù)據(jù)結(jié)構(gòu)的指針。int copy_mem (int nr, struct task_struct *p){ unsigned long old_data_base, new_data_base, data_limit; unsigned long old_code_base, new_code_base, code_limit; code_limit = get_limit (0x0f); // 取局部描述符表中代碼段描述符項(xiàng)中段限長(zhǎng)。 data_limit = get_limit (0x17); // 取局部描述符表中數(shù)據(jù)段描述符項(xiàng)中段限長(zhǎng)。 old_code_base = get_base (current->ldt[1]); // 取原代碼段基址。 old_data_base = get_base (current->ldt[2]); // 取原數(shù)據(jù)段基址。 if (old_data_base != old_code_base) // 0.11 版不支持代碼和數(shù)據(jù)段分立的情況。 panic ("We don't support separate I&D"); if (data_limit < code_limit) // 如果數(shù)據(jù)段長(zhǎng)度 < 代碼段長(zhǎng)度也不對(duì)。 panic ("Bad data_limit"); new_data_base = new_code_base = nr * 0x4000000; // 新基址=任務(wù)號(hào)*64Mb(任務(wù)大小)。 p->start_code = new_code_base; set_base (p->ldt[1], new_code_base); // 設(shè)置代碼段描述符中基址域。 set_base (p->ldt[2], new_data_base); // 設(shè)置數(shù)據(jù)段描述符中基址域。 if (copy_page_tables (old_data_base, new_data_base, data_limit)) { // 復(fù)制代碼和數(shù)據(jù)段。 free_page_tables (new_data_base, data_limit); // 如果出錯(cuò)則釋放申請(qǐng)的內(nèi)存。 return -ENOMEM; } return 0;}/** OK,下面是主要的fork 子程序。它復(fù)制系統(tǒng)進(jìn)程信息(task[n])并且設(shè)置必要的寄存器。* 它還整個(gè)地復(fù)制數(shù)據(jù)段。*/// 復(fù)制進(jìn)程。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; struct i387_struct *p_i387;
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 頁(yè)// 新內(nèi)存,所以此時(shí)esp0 正好指向該頁(yè)頂端)。 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é)處理器,就保存其上下文。
p_i387 = &p->tss.i387;
if (last_task_used_math == current)
_asm{
mov ebx, p_i387
clts
fnsave [p_i387]
}
// __asm__ ("clts ; fnsave %0"::"m" (p->tss.i387));// 設(shè)置新任務(wù)的代碼和數(shù)據(jù)段基址、限長(zhǎng)并復(fù)制頁(yè)表。如果出錯(cuò)(返回值不是0),則復(fù)位任務(wù)數(shù)組中// 相應(yīng)項(xiàng)并釋放為該新任務(wù)分配的內(nèi)存頁(yè)。 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)是不同的)。}// 為新進(jìn)程取得不重復(fù)的進(jìn)程號(hào)last_pid,并返回在任務(wù)數(shù)組中的任務(wù)號(hào)(數(shù)組index)。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;}
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -