?? exec.c
字號(hào):
/** linux/fs/exec.c** (C) 1991 Linus Torvalds*//** #!-checking implemented by tytso.*//** #!開(kāi)始的程序檢測(cè)部分是由tytso 實(shí)現(xiàn)的。*//** Demand-loading implemented 01.12.91 - no need to read anything but* the header into memory. The inode of the executable is put into* "current->executable", and page faults do the actual loading. Clean.** Once more I can proudly say that linux stood up to being changed: it* was less than 2 hours work to get demand-loading completely implemented.*//** 需求時(shí)加載是于1991.12.1 實(shí)現(xiàn)的 - 只需將執(zhí)行文件頭部分讀進(jìn)內(nèi)存而無(wú)須* 將整個(gè)執(zhí)行文件都加載進(jìn)內(nèi)存。執(zhí)行文件的i 節(jié)點(diǎn)被放在當(dāng)前進(jìn)程的可執(zhí)行字段中* ("current->executable"),而頁(yè)異常會(huì)進(jìn)行執(zhí)行文件的實(shí)際加載操作以及清理工作。** 我可以再一次自豪地說(shuō),linux 經(jīng)得起修改:只用了不到2 小時(shí)的工作時(shí)間就完全* 實(shí)現(xiàn)了需求加載處理。*/#include <errno.h> // 錯(cuò)誤號(hào)頭文件。包含系統(tǒng)中各種出錯(cuò)號(hào)。(Linus 從minix 中引進(jìn)的)。#include <string.h> // 字符串頭文件。主要定義了一些有關(guān)字符串操作的嵌入函數(shù)。#include <sys/stat.h> // 文件狀態(tài)頭文件。含有文件或文件系統(tǒng)狀態(tài)結(jié)構(gòu)stat{}和常量。#include <a.out.h> // a.out 頭文件。定義了a.out 執(zhí)行文件格式和一些宏。#include <linux/fs.h> // 文件系統(tǒng)頭文件。定義文件表結(jié)構(gòu)(file,buffer_head,m_inode 等)。#include <linux/sched.h> // 調(diào)度程序頭文件,定義了任務(wù)結(jié)構(gòu)task_struct、初始任務(wù)0 的數(shù)據(jù),// 還有一些有關(guān)描述符參數(shù)設(shè)置和獲取的嵌入式匯編函數(shù)宏語(yǔ)句。#include <linux/kernel.h> // 內(nèi)核頭文件。含有一些內(nèi)核常用函數(shù)的原形定義。#include <linux/mm.h> // 內(nèi)存管理頭文件。含有頁(yè)面大小定義和一些頁(yè)面釋放函數(shù)原型。#include <asm/segment.h> // 段操作頭文件。定義了有關(guān)段寄存器操作的嵌入式匯編函數(shù)。extern int sys_exit (int exit_code); // 程序退出系統(tǒng)調(diào)用。extern int sys_close (int fd); // 文件關(guān)閉系統(tǒng)調(diào)用。/** MAX_ARG_PAGES defines the number of pages allocated for arguments* and envelope for the new program. 32 should suffice, this gives* a maximum env+arg of 128kB !*//** MAX_ARG_PAGES 定義了新程序分配給參數(shù)和環(huán)境變量使用的內(nèi)存最大頁(yè)數(shù)。* 32 頁(yè)內(nèi)存應(yīng)該足夠了,這使得環(huán)境和參數(shù)(env+arg)空間的總合達(dá)到128kB!*/#define MAX_ARG_PAGES 32/** create_tables() parses the env- and arg-strings in new user* memory and creates the pointer tables from them, and puts their* addresses on the "stack", returning the new stack pointer value.*//** create_tables()函數(shù)在新用戶內(nèi)存中解析環(huán)境變量和參數(shù)字符串,由此* 創(chuàng)建指針表,并將它們的地址放到"堆棧"上,然后返回新棧的指針值。*///// 在新用戶堆棧中創(chuàng)建環(huán)境和參數(shù)變量指針表。// 參數(shù):p - 以數(shù)據(jù)段為起點(diǎn)的參數(shù)和環(huán)境信息偏移指針;argc - 參數(shù)個(gè)數(shù);envc -環(huán)境變量數(shù)。// 返回:堆棧指針。static unsigned long *create_tables (char *p, int argc, int envc){ unsigned long *argv, *envp; unsigned long *sp;// 堆棧指針是以4 字節(jié)(1 節(jié))為邊界尋址的,因此這里讓sp 為4 的整數(shù)倍。 sp = (unsigned long *) (0xfffffffc & (unsigned long) p);// sp 向下移動(dòng),空出環(huán)境參數(shù)占用的空間個(gè)數(shù),并讓環(huán)境參數(shù)指針envp 指向該處。 sp -= envc + 1; envp = sp;// sp 向下移動(dòng),空出命令行參數(shù)指針占用的空間個(gè)數(shù),并讓argv 指針指向該處。// 下面指針加1,sp 將遞增指針寬度字節(jié)值。 sp -= argc + 1; argv = sp;// 將環(huán)境參數(shù)指針envp 和命令行參數(shù)指針以及命令行參數(shù)個(gè)數(shù)壓入堆棧。 put_fs_long ((unsigned long) envp, --sp); put_fs_long ((unsigned long) argv, --sp); put_fs_long ((unsigned long) argc, --sp);// 將命令行各參數(shù)指針?lè)湃肭懊婵粘鰜?lái)的相應(yīng)地方,最后放置一個(gè)NULL 指針。 while (argc-- > 0) { put_fs_long ((unsigned long) p, argv++); while (get_fs_byte (p++)) /* nothing */ ; // p 指針前移4 字節(jié)。 } put_fs_long (0, argv);// 將環(huán)境變量各指針?lè)湃肭懊婵粘鰜?lái)的相應(yīng)地方,最后放置一個(gè)NULL 指針。 while (envc-- > 0) { put_fs_long ((unsigned long) p, envp++); while (get_fs_byte (p++)) /* nothing */ ; } put_fs_long (0, envp); return sp; // 返回構(gòu)造的當(dāng)前新堆棧指針。}/** count() counts the number of arguments/envelopes*//** count()函數(shù)計(jì)算命令行參數(shù)/環(huán)境變量的個(gè)數(shù)。*///// 計(jì)算參數(shù)個(gè)數(shù)。// 參數(shù):argv - 參數(shù)指針數(shù)組,最后一個(gè)指針項(xiàng)是NULL。// 返回:參數(shù)個(gè)數(shù)。static intcount (char **argv){ int i = 0; char **tmp; if (tmp = argv) while (get_fs_long ((unsigned long *) (tmp++))) i++; return i;}/** 'copy_string()' copies argument/envelope strings from user* memory to free pages in kernel mem. These are in a format ready* to be put directly into the top of new user memory.** Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies* whether the string and the string array are from user or kernel segments:** from_kmem argv * argv *** 0 user space user space* 1 kernel space user space* 2 kernel space kernel space** We do this by playing games with the fs segment register. Since it* it is expensive to load a segment register, we try to avoid calling* set_fs() unless we absolutely have to.*//** 'copy_string()'函數(shù)從用戶內(nèi)存空間拷貝參數(shù)和環(huán)境字符串到內(nèi)核空閑頁(yè)面內(nèi)存中。* 這些已具有直接放到新用戶內(nèi)存中的格式。** 由TYT(Tytso)于1991.12.24 日修改,增加了from_kmem 參數(shù),該參數(shù)指明了字符串或* 字符串?dāng)?shù)組是來(lái)自用戶段還是內(nèi)核段。** from_kmem argv * argv *** 0 用戶空間 用戶空間* 1 內(nèi)核空間 用戶空間* 2 內(nèi)核空間 內(nèi)核空間** 我們是通過(guò)巧妙處理fs 段寄存器來(lái)操作的。由于加載一個(gè)段寄存器代價(jià)太大,所以* 我們盡量避免調(diào)用set_fs(),除非實(shí)在必要。*///// 復(fù)制指定個(gè)數(shù)的參數(shù)字符串到參數(shù)和環(huán)境空間。// 參數(shù):argc - 欲添加的參數(shù)個(gè)數(shù);argv - 參數(shù)指針數(shù)組;page - 參數(shù)和環(huán)境空間頁(yè)面指針數(shù)組。// p -在參數(shù)表空間中的偏移指針,始終指向已復(fù)制串的頭部;from_kmem - 字符串來(lái)源標(biāo)志。// 在do_execve()函數(shù)中,p 初始化為指向參數(shù)表(128kB)空間的最后一個(gè)長(zhǎng)字處,參數(shù)字符串// 是以堆棧操作方式逆向往其中復(fù)制存放的,因此p 指針會(huì)始終指向參數(shù)字符串的頭部。// 返回:參數(shù)和環(huán)境空間當(dāng)前頭部指針。static unsigned longcopy_strings (int argc, char **argv, unsigned long *page, unsigned long p, int from_kmem){ char *tmp, *pag; int len, offset = 0; unsigned long old_fs, new_fs; if (!p) return 0; /* bullet-proofing *//* 偏移指針驗(yàn)證 */// 取ds 寄存器值到new_fs,并保存原fs 寄存器值到old_fs。 new_fs = get_ds (); old_fs = get_fs ();// 如果字符串和字符串?dāng)?shù)組來(lái)自內(nèi)核空間,則設(shè)置fs 段寄存器指向內(nèi)核數(shù)據(jù)段(ds)。 if (from_kmem == 2) set_fs (new_fs);// 循環(huán)處理各個(gè)參數(shù),從最后一個(gè)參數(shù)逆向開(kāi)始復(fù)制,復(fù)制到指定偏移地址處。 while (argc-- > 0) {// 如果字符串在用戶空間而字符串?dāng)?shù)組在內(nèi)核空間,則設(shè)置fs 段寄存器指向內(nèi)核數(shù)據(jù)段(ds)。 if (from_kmem == 1) set_fs (new_fs);// 從最后一個(gè)參數(shù)開(kāi)始逆向操作,取fs 段中最后一參數(shù)指針到tmp,如果為空,則出錯(cuò)死機(jī)。 if (!(tmp = (char *) get_fs_long (((unsigned long *) argv) + argc))) panic ("argc is wrong");// 如果字符串在用戶空間而字符串?dāng)?shù)組在內(nèi)核空間,則恢復(fù)fs 段寄存器原值。 if (from_kmem == 1) set_fs (old_fs);// 計(jì)算該參數(shù)字符串長(zhǎng)度len,并使tmp 指向該參數(shù)字符串末端。 len = 0; /* remember zero-padding */ do { /* 我們知道串是以NULL 字節(jié)結(jié)尾的 */ len++; } while (get_fs_byte (tmp++));// 如果該字符串長(zhǎng)度超過(guò)此時(shí)參數(shù)和環(huán)境空間中還剩余的空閑長(zhǎng)度,則恢復(fù)fs 段寄存器并返回0。 if (p - len < 0) { /* this shouldn't happen - 128kB */ set_fs (old_fs); /* 不會(huì)發(fā)生-因?yàn)橛?28kB 的空間 */ return 0; }// 復(fù)制fs 段中當(dāng)前指定的參數(shù)字符串,是從該字符串尾逆向開(kāi)始復(fù)制。 while (len) { --p; --tmp; --len;// 函數(shù)剛開(kāi)始執(zhí)行時(shí),偏移變量offset 被初始化為0,因此若offset-1<0,說(shuō)明是首次復(fù)制字符串,// 則令其等于p 指針在頁(yè)面內(nèi)的偏移值,并申請(qǐng)空閑頁(yè)面。 if (--offset < 0) { offset = p % PAGE_SIZE;// 如果字符串和字符串?dāng)?shù)組在內(nèi)核空間,則恢復(fù)fs 段寄存器原值。 if (from_kmem == 2) set_fs (old_fs);// 如果當(dāng)前偏移值p 所在的串空間頁(yè)面指針數(shù)組項(xiàng)page[p/PAGE_SIZE]==0,表示相應(yīng)頁(yè)面還不存在,// 則需申請(qǐng)新的內(nèi)存空閑頁(yè)面,將該頁(yè)面指針填入指針數(shù)組,并且也使pag 指向該新頁(yè)面,若申請(qǐng)不// 到空閑頁(yè)面則返回0。 if (!(pag = (char *) page[p / PAGE_SIZE]) && !(pag = (char *) page[p / PAGE_SIZE] = (unsigned long *) get_free_page ())) return 0;// 如果字符串和字符串?dāng)?shù)組來(lái)自內(nèi)核空間,則設(shè)置fs 段寄存器指向內(nèi)核數(shù)據(jù)段(ds)。 if (from_kmem == 2) set_fs (new_fs); }// 從fs 段中復(fù)制參數(shù)字符串中一字節(jié)到pag+offset 處。 *(pag + offset) = get_fs_byte (tmp); } }// 如果字符串和字符串?dāng)?shù)組在內(nèi)核空間,則恢復(fù)fs 段寄存器原值。 if (from_kmem == 2) set_fs (old_fs);// 最后,返回參數(shù)和環(huán)境空間中已復(fù)制參數(shù)信息的頭部偏移值。 return p;}//// 修改局部描述符表中的描述符基址和段限長(zhǎng),并將參數(shù)和環(huán)境空間頁(yè)面放置在數(shù)據(jù)段末端。// 參數(shù):text_size - 執(zhí)行文件頭部中a_text 字段給出的代碼段長(zhǎng)度值;// page - 參數(shù)和環(huán)境空間頁(yè)面指針數(shù)組。// 返回:數(shù)據(jù)段限長(zhǎng)值(64MB)。static unsigned longchange_ldt (unsigned long text_size, unsigned long *page){ unsigned long code_limit, data_limit, code_base, data_base; int i;// 根據(jù)執(zhí)行文件頭部a_text 值,計(jì)算以頁(yè)面長(zhǎng)度為邊界的代碼段限長(zhǎng)。并設(shè)置數(shù)據(jù)段長(zhǎng)度為64MB。 code_limit = text_size + PAGE_SIZE - 1; code_limit &= 0xFFFFF000; data_limit = 0x4000000;// 取當(dāng)前進(jìn)程中局部描述符表代碼段描述符中代碼段基址,代碼段基址與數(shù)據(jù)段基址相同。 code_base = get_base (current->ldt[1]); data_base = code_base;// 重新設(shè)置局部表中代碼段和數(shù)據(jù)段描述符的基址和段限長(zhǎng)。 set_base (current->ldt[1], code_base); set_limit (current->ldt[1], code_limit); set_base (current->ldt[2], data_base); set_limit (current->ldt[2], data_limit);/* make sure fs points to the NEW data segment *//* 要確信fs 段寄存器已指向新的數(shù)據(jù)段 */// fs 段寄存器中放入局部表數(shù)據(jù)段描述符的選擇符(0x17)。 __asm__ ("pushl $0x17\n\tpop %%fs"::);// 將參數(shù)和環(huán)境空間已存放數(shù)據(jù)的頁(yè)面(共可有MAX_ARG_PAGES 頁(yè),128kB)放到數(shù)據(jù)段線性地址的// 末端。是調(diào)用函數(shù)put_page()進(jìn)行操作的(mm/memory.c, 197)。 data_base += data_limit; for (i = MAX_ARG_PAGES - 1; i >= 0; i--) {
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -