1. 程式人生 > >(萊昂氏unix原始碼分析導讀-47) exec

(萊昂氏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()的前三個引數,分別為textdatastack segmentsize

而函式也會分別為三個segment分配user態地址暫存器:

(1)         TextData 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;

這次呼叫比較好理解,當時剛剛得到了“執行資訊”(即textdataSegmentSize),

其目的是為了檢查該“執行資訊”是否合法。

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

sizeText Segmentsize0),按照前面的描述,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 = &regloc[0]; cp < &regloc[6];)

3187:       u.u_ar0[*cp++] = 0;

3188:    u.u_ar0[R7] = 0;

2.        返回user

exec系統呼叫最後會通過call彙編例程(0805: rtt)返回user態。而exec已經將user pc0,這樣,

系統呼叫返回時,將直接跳到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平