?? exec.c
字號:
/** linux/fs/exec.c** (C) 1991 Linus Torvalds*//** #!-checking implemented by tytso.*//** #!開始的程序檢測部分是由tytso 實現(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.*//** 需求時加載是于1991.12.1 實現(xiàn)的 - 只需將執(zhí)行文件頭部分讀進內(nèi)存而無須* 將整個執(zhí)行文件都加載進內(nèi)存。執(zhí)行文件的i 節(jié)點被放在當前進程的可執(zhí)行字段中* ("current->executable"),而頁異常會進行執(zhí)行文件的實際加載操作以及清理工作。** 我可以再一次自豪地說,linux 經(jīng)得起修改:只用了不到2 小時的工作時間就完全* 實現(xiàn)了需求加載處理。*/#include <errno.h> // 錯誤號頭文件。包含系統(tǒng)中各種出錯號。(Linus 從minix 中引進的)。#include <string.h> // 字符串頭文件。主要定義了一些有關字符串操作的嵌入函數(shù)。#include <sys/stat.h> // 文件狀態(tài)頭文件。含有文件或文件系統(tǒng)狀態(tài)結構stat{}和常量。#include <a.out.h> // a.out 頭文件。定義了a.out 執(zhí)行文件格式和一些宏。#include <linux/fs.h> // 文件系統(tǒng)頭文件。定義文件表結構(file,buffer_head,m_inode 等)。#include <linux/sched.h> // 調度程序頭文件,定義了任務結構task_struct、初始任務0 的數(shù)據(jù),// 還有一些有關描述符參數(shù)設置和獲取的嵌入式匯編函數(shù)宏語句。#include <linux/kernel.h> // 內(nèi)核頭文件。含有一些內(nèi)核常用函數(shù)的原形定義。#include <linux/mm.h> // 內(nèi)存管理頭文件。含有頁面大小定義和一些頁面釋放函數(shù)原型。#include <asm/segment.h> // 段操作頭文件。定義了有關段寄存器操作的嵌入式匯編函數(shù)。extern int sys_exit (int exit_code); // 程序退出系統(tǒng)調用。extern int sys_close (int fd); // 文件關閉系統(tǒng)調用。/** 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)存最大頁數(shù)。* 32 頁內(nèi)存應該足夠了,這使得環(huán)境和參數(shù)(env+arg)空間的總合達到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ù)段為起點的參數(shù)和環(huán)境信息偏移指針;argc - 參數(shù)個數(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 向下移動,空出環(huán)境參數(shù)占用的空間個數(shù),并讓環(huán)境參數(shù)指針envp 指向該處。 sp -= envc + 1; envp = sp;// sp 向下移動,空出命令行參數(shù)指針占用的空間個數(shù),并讓argv 指針指向該處。// 下面指針加1,sp 將遞增指針寬度字節(jié)值。 sp -= argc + 1; argv = sp;// 將環(huán)境參數(shù)指針envp 和命令行參數(shù)指針以及命令行參數(shù)個數(shù)壓入堆棧。 put_fs_long ((unsigned long) envp, --sp); put_fs_long ((unsigned long) argv, --sp); put_fs_long ((unsigned long) argc, --sp);// 將命令行各參數(shù)指針放入前面空出來的相應地方,最后放置一個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)境變量各指針放入前面空出來的相應地方,最后放置一個NULL 指針。 while (envc-- > 0) { put_fs_long ((unsigned long) p, envp++); while (get_fs_byte (p++)) /* nothing */ ; } put_fs_long (0, envp); return sp; // 返回構造的當前新堆棧指針。}/** count() counts the number of arguments/envelopes*//** count()函數(shù)計算命令行參數(shù)/環(huán)境變量的個數(shù)。*///// 計算參數(shù)個數(shù)。// 參數(shù):argv - 參數(shù)指針數(shù)組,最后一個指針項是NULL。// 返回:參數(shù)個數(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)核空閑頁面內(nèi)存中。* 這些已具有直接放到新用戶內(nèi)存中的格式。** 由TYT(Tytso)于1991.12.24 日修改,增加了from_kmem 參數(shù),該參數(shù)指明了字符串或* 字符串數(shù)組是來自用戶段還是內(nèi)核段。** from_kmem argv * argv *** 0 用戶空間 用戶空間* 1 內(nèi)核空間 用戶空間* 2 內(nèi)核空間 內(nèi)核空間** 我們是通過巧妙處理fs 段寄存器來操作的。由于加載一個段寄存器代價太大,所以* 我們盡量避免調用set_fs(),除非實在必要。*///// 復制指定個數(shù)的參數(shù)字符串到參數(shù)和環(huán)境空間。// 參數(shù):argc - 欲添加的參數(shù)個數(shù);argv - 參數(shù)指針數(shù)組;page - 參數(shù)和環(huán)境空間頁面指針數(shù)組。// p -在參數(shù)表空間中的偏移指針,始終指向已復制串的頭部;from_kmem - 字符串來源標志。// 在do_execve()函數(shù)中,p 初始化為指向參數(shù)表(128kB)空間的最后一個長字處,參數(shù)字符串// 是以堆棧操作方式逆向往其中復制存放的,因此p 指針會始終指向參數(shù)字符串的頭部。// 返回:參數(shù)和環(huán)境空間當前頭部指針。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 *//* 偏移指針驗證 */// 取ds 寄存器值到new_fs,并保存原fs 寄存器值到old_fs。 new_fs = get_ds (); old_fs = get_fs ();// 如果字符串和字符串數(shù)組來自內(nèi)核空間,則設置fs 段寄存器指向內(nèi)核數(shù)據(jù)段(ds)。 if (from_kmem == 2) set_fs (new_fs);// 循環(huán)處理各個參數(shù),從最后一個參數(shù)逆向開始復制,復制到指定偏移地址處。 while (argc-- > 0) {// 如果字符串在用戶空間而字符串數(shù)組在內(nèi)核空間,則設置fs 段寄存器指向內(nèi)核數(shù)據(jù)段(ds)。 if (from_kmem == 1) set_fs (new_fs);// 從最后一個參數(shù)開始逆向操作,取fs 段中最后一參數(shù)指針到tmp,如果為空,則出錯死機。 if (!(tmp = (char *) get_fs_long (((unsigned long *) argv) + argc))) panic ("argc is wrong");// 如果字符串在用戶空間而字符串數(shù)組在內(nèi)核空間,則恢復fs 段寄存器原值。 if (from_kmem == 1) set_fs (old_fs);// 計算該參數(shù)字符串長度len,并使tmp 指向該參數(shù)字符串末端。 len = 0; /* remember zero-padding */ do { /* 我們知道串是以NULL 字節(jié)結尾的 */ len++; } while (get_fs_byte (tmp++));// 如果該字符串長度超過此時參數(shù)和環(huán)境空間中還剩余的空閑長度,則恢復fs 段寄存器并返回0。 if (p - len < 0) { /* this shouldn't happen - 128kB */ set_fs (old_fs); /* 不會發(fā)生-因為有128kB 的空間 */ return 0; }// 復制fs 段中當前指定的參數(shù)字符串,是從該字符串尾逆向開始復制。 while (len) { --p; --tmp; --len;// 函數(shù)剛開始執(zhí)行時,偏移變量offset 被初始化為0,因此若offset-1<0,說明是首次復制字符串,// 則令其等于p 指針在頁面內(nèi)的偏移值,并申請空閑頁面。 if (--offset < 0) { offset = p % PAGE_SIZE;// 如果字符串和字符串數(shù)組在內(nèi)核空間,則恢復fs 段寄存器原值。 if (from_kmem == 2) set_fs (old_fs);// 如果當前偏移值p 所在的串空間頁面指針數(shù)組項page[p/PAGE_SIZE]==0,表示相應頁面還不存在,// 則需申請新的內(nèi)存空閑頁面,將該頁面指針填入指針數(shù)組,并且也使pag 指向該新頁面,若申請不// 到空閑頁面則返回0。 if (!(pag = (char *) page[p / PAGE_SIZE]) && !(pag = (char *) page[p / PAGE_SIZE] = (unsigned long *) get_free_page ())) return 0;// 如果字符串和字符串數(shù)組來自內(nèi)核空間,則設置fs 段寄存器指向內(nèi)核數(shù)據(jù)段(ds)。 if (from_kmem == 2) set_fs (new_fs); }// 從fs 段中復制參數(shù)字符串中一字節(jié)到pag+offset 處。 *(pag + offset) = get_fs_byte (tmp); } }// 如果字符串和字符串數(shù)組在內(nèi)核空間,則恢復fs 段寄存器原值。 if (from_kmem == 2) set_fs (old_fs);// 最后,返回參數(shù)和環(huán)境空間中已復制參數(shù)信息的頭部偏移值。 return p;}//// 修改局部描述符表中的描述符基址和段限長,并將參數(shù)和環(huán)境空間頁面放置在數(shù)據(jù)段末端。// 參數(shù):text_size - 執(zhí)行文件頭部中a_text 字段給出的代碼段長度值;// page - 參數(shù)和環(huán)境空間頁面指針數(shù)組。// 返回:數(shù)據(jù)段限長值(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 值,計算以頁面長度為邊界的代碼段限長。并設置數(shù)據(jù)段長度為64MB。 code_limit = text_size + PAGE_SIZE - 1; code_limit &= 0xFFFFF000; data_limit = 0x4000000;// 取當前進程中局部描述符表代碼段描述符中代碼段基址,代碼段基址與數(shù)據(jù)段基址相同。 code_base = get_base (current->ldt[1]); data_base = code_base;// 重新設置局部表中代碼段和數(shù)據(jù)段描述符的基址和段限長。 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ù)的頁面(共可有MAX_ARG_PAGES 頁,128kB)放到數(shù)據(jù)段線性地址的// 末端。是調用函數(shù)put_page()進行操作的(mm/memory.c, 197)。 data_base += data_limit; for (i = MAX_ARG_PAGES - 1; i >= 0; i--) {
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -