(萊昂氏unix原始碼分析導讀-47) exec
by cszhao1980
現在,我們已經儲備了足夠的知識,該吹響向EXEC sys call衝鋒的號角了。
exec是系統中最重要也是最複雜的系統呼叫之一,它的作用是執行指定的“可執行檔案”。一般說來,
exec與fork配合使用,fork生成一個新程序,而exec是新程序執行其應該執行的程式碼。
萊昂對exec有著比較詳細的介紹,但很不幸,這些程式碼理解起來仍然困難重重。所以,我要在這裡多囉嗦幾句。
1. exec的引數
exec擁有可變個數的引數,其中:
(1) 第一個引數指向一個字串,而該字串記憶體放的為要執行檔案的路徑名;
如#1程序的初始exec呼叫中,路徑名為:“/etc/init”
(2) 其他引數也都為指向字串的指標;
(3) 引數及其指向的內容,均在user態地址空間中。
而程式碼3050~3070行的作用,就是把user態地址空間的引數內容,拷貝到kernel態的緩衝區內。
3049: cp = bp->b_addr;
3050: na = 0;
3051: nc = 0;
3052: while(ap = fuword(u.u_arg[1])) {
3053: na++;
3054: if(ap == -1)
3055: goto bad;
3056: u.u_arg[1] =+ 2;
3057: for(;;) {
3058: c = fubyte(ap++);
3059: if(c == -1)
3060: goto bad;
3061: *cp++ = c;
3062: nc++;
3063: if(nc > 510) {
3064: u.u_error = E2BIG;
3065: goto bad;
3066: }
3067: if(c == 0)
3068: break;
3069: }
3070: }
2. 可執行檔案的“執行”資訊
可執行檔案的前8個byte記錄了該檔案的“執行”資訊,如text、data、bss段的size等。
3085~3108行的作用是讀取這8個byte,並根據其資訊進行設定。
1. estabur()的多次呼叫。
exec中多次呼叫了estabur()——這是非常奇怪的事情,我們知道該函式的作用是設定user態
的8個地址暫存器。因此,一般說來,這個函式呼叫一次即可。為理解這個問題,我們首先
複習一下estabur()的實現。extabur()的前三個引數,分別為text、data、stack segment的size,
而函式也會分別為三個segment分配user態地址暫存器:
(1) Text、Data Segment順序佔據邏輯地址的低地址部分;
(2) Stack Segment佔據邏輯地址的高地址部分,且從最高地址向地址延伸。
舉例說明,當三個segment都佔據2個page時,其user態地址暫存器的設定如下圖所示:
下面,讓我們看看exec中的幾次呼叫:
(1)第一次呼叫
3116: ts = ((u.u_arg[1]+63)>>6) & 01777;
3117: ds = ((u.u_arg[2]+u.u_arg[3]+63)>>6) & 01777;
3118: if(estabur(ts, ds, SSIZE, sep))
3119: goto bad;
這次呼叫比較好理解,當時剛剛得到了“執行資訊”(即text、dataSegment的Size),
其目的是為了檢查該“執行資訊”是否合法。
(2)第二次呼叫
3138: estabur(0, ds, 0, 0);
3139: u.u_base = 0;
3140: u.u_offset[1] = 020+u.u_arg[1];
3141: u.u_count = u.u_arg[2];
3142: readi(ip);
此次呼叫的目的是為了方便的讀取檔案中的Data Segment。呼叫中只是指定了data segment的
size(Text Segment的size為0),按照前面的描述,user[0]將指向data Segment的首地址,即程序
的data Segment的邏輯起始地址為0。而其後的程式碼將檔案中的Data Segment讀入以0開始的地址,
即程序所分配的Data Segment當中。
(3)第三次呼叫
3148: u.u_tsize = ts;
3149: u.u_dsize = ds;
3150: u.u_ssize = SSIZE;
3151: u.u_sep = sep;
3152: estabur(u.u_tsize, u.u_dsize, u.u_ssize, u.u_sep);
此次呼叫為最終呼叫,正確設定了程序需要的user態地址暫存器。
相信有些讀者會有這樣的疑惑:本次呼叫過後,其user態地址暫存器得到了重置,其設定顯然
與第二次呼叫的不同。那末,在第二次呼叫後讀入的Data Segment是否還有效?
好問題!如果您仔細研究estabur()就會發現,在這兩次呼叫下,Data Segment的邏輯地址有變化,
但其實體地址是重合的。因此,剛剛讀入的Data Segment仍然有效。
1. 奇怪的負數地址
太奇怪了,下列程式碼竟然指定了負數地址,3156行將na(引數個數)“set”到user空間的負數地址:
3154: ap = -nc - na*2 - 4;
3155: u.u_ar0[R6] = ap;
3156: suword(ap, na);
3157: c = -nc;
3158: while(na--) {
3159: suword(ap=+2, c);
3160: do
3161: subyte(c++, *cp);
3162: while(*cp++);
3163: }
3164: suword(ap+2, -1);
呵呵。ap並非負數地址,而是一個很大的正數,事實上它距最高地址也就“nc + na*2 +4”個word。
前面說過,最高地址為stack所處的位置。而這幾行程式碼將引數個數和引數本身壓入stack Segment的
最高地址部分,並將user sp設定好。簡單的說,即將使用者引數壓入使用者棧而已。
由於user sp已經設定好,因此,在下面的程式碼clear user暫存器,唯獨放過了user sp:
2677: char regloc[9]
2678: {
2679: R0, R1, R2, R3, R4, R5, R6, R7, RPS
2680: };
3186: for(cp = ®loc[0]; cp < ®loc[6];)
3187: u.u_ar0[*cp++] = 0;
3188: u.u_ar0[R7] = 0;
2. 返回user態
exec系統呼叫最後會通過call彙編例程(0805: rtt)返回user態。而exec已經將user pc清0,這樣,
系統呼叫返回時,將直接跳到user態地址0開始執行,而這正是text segment的起始地址。
相關推薦
(萊昂氏unix原始碼分析導讀-47) exec
by cszhao1980 現在,我們已經儲備了足夠的知識,該吹響向EXEC sys call衝鋒的號角了。 exec是系統中最重要也是最複雜的系統呼叫之一,它的作用是執
(萊昂氏unix原始碼分析導讀-33) swap函式
By cszhao1980 是瞭解swap函式的時候了,它有四個引數: (1)blkno:磁碟塊號; (2)coreaddr:實體記憶體block號; (3)count:讀寫位元組數; (4)rdflg:讀
(萊昂氏unix原始碼分析導讀-18) 再談中斷與陷入
從產生原因看,中斷和陷入也有巨大的差別。 硬體中斷由外部事件造成,屬於非同步事件,往往與當前程序毫無關係; 陷入則不同,它常常都是同步的(如除0錯),並與當前程序上下文相關。 除此之外,陷入還用來實現系統呼叫——核心為user程序提供了大量的服務,這些服務就是通過系統
(萊昂氏unix原始碼分析導讀-36) 快取管理(下)
by cszhao1980 理解了上述內容,下面的這些程式就不難理解了。 首先是函式brelse(buf bp),該函式將傳入的快取歸還到AV佇列中,函式採用尾插法, 即快取會插到AV佇列的隊尾——
(萊昂氏unix原始碼分析導讀-35)快取管理(上)
by cszhao1980 系統定義了NBUF個快取區域,每個514個位元組: 4720: char buffers
(萊昂氏unix原始碼分析導讀-21)時鐘中斷處理
時鐘中斷是系統中最重要的中斷,每個時鐘滴答都會產生時鐘中斷,它的中斷向量為(0100)或(0103)。 0533: . = 100^. 0534: kwlp; br6 0535: kwlp; br6 0569: .globl _clock 05
【 專欄 】- 萊昂氏unix原始碼分析導讀
萊昂氏unix原始碼分析導讀 即使到今天,《萊昂氏unix原始碼分析》仍是最好的核心原始碼分析書籍之一,它以短短9000多行程式碼實現了一個複雜的作業系統,其程式碼質量之高令人歎為觀止。希望本專欄能為讀者的讀碼過程提供一點幫助。
(萊昂氏unix原始碼分析導讀-11)系統初啟(4)
本章討論程序複製,繼續Main函式。 1625 */ 1626 1627 if(newproc()) { …… 現在看newproc的程式碼。 1826 newproc() 1827 { ......
(萊昂氏unix原始碼分析導讀-15) 系統初啟(8)
程序user態下的分段 User態中將程序空間分為text、data、stack segment三部分。 estabur(nt, nd, ns,sep)根據各個segment的大小,為各段分配page,引數如下: (1) nt—— text segment的長度(blo
(萊昂氏unix原始碼分析導讀-20)中斷、陷入的入口和出口
陷入處理程式的入口都為“trap”(這裡是指一個彙編程式,而非PDP11指令); 中斷則不同。他們有不同的入口,如: 525 .=60^. 當前地址設定為60 octal 526 klin; br4
(萊昂氏unix原始碼分析導讀-44) 檔案系統資源
by cszhao1980 一個裝置被mount進系統後,就被稱為一個檔案系統。它有兩類資源: (1) 磁碟inode資源; (2) 普通盤塊資
(萊昂氏unix原始碼分析導讀-43) 檔案系統的mount
By cszhao1980 當一個裝置被mount進系統,就會在“mount表”中佔據一個表項,mount表的定義如下: 0272: struct mount 0273: { 0274: int m_dev
(萊昂氏unix原始碼分析導讀-39)inode“資源”的獲取和釋放
by cszhao1980 iget()函式用於獲取inode資源,它有2個引數,裝置號和inode id。前面說過,通過這兩個引數 會唯一確定一個inode。簡單的說,該函式的作
(萊昂氏unix原始碼分析導讀-19)再談程序swtch
我們已經涉及到了部分程序切換的概念,在本章中,我們會從更一般的意義上考察程序切換的行為。 首先,程序切換(也稱作context switch)一定是在核心中完成的。 比如,以下為發生程序切換的最常見的情況: (1) active程序因等待某資源阻塞,自動讓出cpu;
(萊昂氏unix原始碼分析導讀-46)許可權、管道
by cszhao1980 1. 檔案與許可權控制 程序u結構中,身份相關的資訊有: 0420: char u_uid; /* effective user id */ 0421: char u_gid; /* effective group id */ 0
(萊昂氏unix原始碼分析導讀-27) Swap in/out (上)
By cszhao1980 Swap in/out指的是程序在實體記憶體(core空間)和磁碟交換檔案間的雙向移動過程,程序在active狀態時, 其segment
47.Fabric 1.0原始碼分析(47)Fabric 1.0.4 go程式碼量統計
Fabric 1.0原始碼筆記 之Fabric 1.0.4 go程式碼量統計 1、概述 除test、vendor、mocks、example、protos外,go核心程式碼檔案341個,核心程式碼行63433行。 find ./ |grep -vE 'test|
Android 7 原始碼分析系列導讀
關於作者 郭孝星,程式設計師,吉他手,主要從事Android平臺基礎架構方面的工作,歡迎交流技術方面的問題,可以去我的Github提issue或者發郵件至[email protected]與我交流。 文章目錄 一 基礎篇 二 工具篇 三
UNIX v6原始碼分析除錯之二:單步除錯系統程式碼 main函式之 kinit1
環境搭建完成,該學習Main.c的main函數了。先貼上main函式的實現。int main(void) { kinit1(end, P2V(4*1024*1024)); // phys page allocator kvmalloc(); // k
UNIX v6原始碼分析除錯之一:環境搭建 -- Ubuntu單步除錯系統程式碼
UNIX v6(x86)版本的程式碼短小精悍,非常值得學習除錯。而且程式碼量比較少,容易找到方向。我使用的Ubuntu 12.04 32位的系統。首先下載程式碼,下載地址 https://github.com/guilleiguaran/xv6。該版本的程式碼能執行在x86平