1. 如前文(http://www.linuxidc.com/Linux/2011-08/41869.htm)所述,為了執行一個程序,首先do_execve建立數據結構,並將一些數據從用戶空間拷貝到內核空間,然後調用search_binary_handler加載可執行文件映像。
- int do_execve(char * filename,
- char __user *__user *argv,
- char __user *__user *envp,
- struct pt_regs * regs)
2. search_binary_handler()尋找對應的handler。對於elf 文件,即是load_elf_binary。
- int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
3. load_elf_binary()讀取可執行文件頭文件信息,進行簡單的一致性檢測,分配用戶模式的頁表,設置棧的起始地址,加載可執行文件映像到內存;然後調用create_elf_tables(); 最後調用start_thread(),執行_start函數開始的代碼。
- static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
4. create_elf_tables()將參數指針,環境變量數組指針壓入用戶模式的棧。
- static int
- create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
- unsigned long load_addr, unsigned long interp_load_addr)
值得注意的是,create_elf_tables()很可能會在壓棧前調整棧指針。比如,在支持超線程的體系結構裡面,通過隨機化初始棧指針,可以減少進程間在L1上的競爭。如下所示,隨機化初始棧指針的頁內偏移量,並使得棧指針保持16字節對齊。
- unsigned long arch_align_stack(unsigned long sp)
- {
- if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
- sp -= get_random_int() % 8192;
- return sp & ~0xf;
- }
為什麼針對頁內偏移呢?
在某些體系結構中,首先要完成從邏輯地址到物理地址的轉換,然後才能去cache中查找該物理地址是否已經在cache當中。這樣,cache命中的代價較高。一種常用的技巧是,在L1中,邏輯地址索引-物理地址比較(virtually indexed, physically tagged)[1]。思路是,利用邏輯地址與物理地址的頁內偏移一樣的特點,用頁內偏移進行索引,頁號通過TLB轉換成物理頁號進行tag比較。這樣,可以不經轉換,就先索引,從而加快速度。這樣,如果兩個邏輯地址的塊內偏移一樣,它們索引的cache行也就一樣,所以需要隨機化頁內偏移來減少L1的競爭。其缺點是,L1的set大小,不能超過頁的大小。換言之:
L1的大小 <= 相聯度 * 塊的大小 * 頁的大小
5. start_thread(),執行_start函數開始的代碼
- void
- start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
- {
- set_user_gs(regs, 0);
- regs->fs = 0;
- set_fs(USER_DS);
- regs->ds = __USER_DS;
- regs->es = __USER_DS;
- regs->ss = __USER_DS;
- regs->cs = __USER_CS;
- regs->ip = new_ip;
- regs->sp = new_sp;
- /*
- * Free the old FP and other extended state
- */
- free_thread_xstate(current);
- }
參考:[1] Computer Architecture: A Quantitative Approach, Fourth Edition. Page 291-292.