天天看點

《APUE》讀書筆記—第八章程序控制

本章介紹unix的程序控制,包括程序建立,執行程式和程序終止,程序的屬性,exec函數系列,system函數,程序會計機制。

1、程序辨別符

  每一個程序都有一個非負整數辨別的唯一程序id。id為0表示排程程序,即交換程序,是核心的一部分,也稱為系統程序,不執行任何磁盤操作。id為1的程序為init程序,init程序不會終止,他是一個普通的使用者程序,需要超級使用者特權運作。擷取辨別符函數如下:

#include <sys/types.h>

#include <unistd.h> 

pid_t getpid(void);    //調用程序的程序id

pid_t getppid(void);     //調用程序的父程序id

gid_t getgid(void);       //調用程序的實際組id

gid_t getegid(void);    //調用程序的有效組id

uid_t getuid(void);     //調用程序的實際使用者id

uid_t geteuid(void);   //調用程序的有效使用者id

2、fork函數

  一個現有的程序可以調用fork函數建立一個新程序。函數原型為:pid_t fork(void)。有fork建立的新程序稱為子程序,fork函數調用一次傳回兩次。子程序的傳回值為0,父程序的傳回值為新子程序的id。子程序和父程序舉行執行fork調用後的指令,子程序是父程序的副本。子程序獲得父程序的資料空間、棧和隊的副本,父子程序不共享這些存儲空間部分,共享正文段。子程序相當于父程序克隆了一份自己。 建立新程序成功後,系統中出現兩個基本完全相同的程序,這兩個程序執行沒有固定的先後順序,哪個程序先執行要看系統的程序排程政策。

  fork出錯可能有兩種原因:

    1)目前的程序數已經達到了系統規定的上限,這時errno的值被設定為eagain。

    2)系統記憶體不足,這時errno的值被設定為enomem。

注意的是:父程序設定的檔案鎖不會被子程序繼承。

寫個程式,建立一個子程序,在子程序中改變變量的值,然後父子程序同時輸出變量的值,看看有什麼變化,同時輸出子程序的辨別符資訊。程式如下:

《APUE》讀書筆記—第八章程式控制
《APUE》讀書筆記—第八章程式控制
《APUE》讀書筆記—第八章程式控制

 程式執行結果如下:

《APUE》讀書筆記—第八章程式控制

從結果可以看出子程序擁有自己的資料空間,不與父程序共享資料空間。

  vfork函數的調用序列和傳回值與fork相同,但是vfork并不将父程序的位址空間完全複制到子程序中,在調用exec和exit之前在父程序的空間中運作。vfork保證子程序先運作,在它調用exec或exit之後父程序才可能被排程運作。

  程序終止最後都會執行核心中的同一段代碼,為相應的程序關閉所有打開描述符,釋放它所使用的存儲器等。子程序可以通過exit函數通知父程序是如何終止的,父程序調用wait或waitpid函數可以擷取終止狀态。子程序是在父程序調用fork後産生的,如果父程序在子程序之前終止,則将子程序的父程序改變為init程序,保證每一個程序都有一個父程序。一個已經終止,但其父程序尚沒有對其進行善後處理的程序稱為僵死程序(zombie)。由init程序領養的子程序不會變成僵死程序,因為init程序在子程序終止的時候會調用一個wait函數取得子程序的終止狀态。

  當一個程序正常或者異常終止的時,核心就像其父程序發送sigchld信号。調用wait和waitpid函數可能發生的情況:(1)如果所有子程序都還在運作,則阻塞;(2)如果一個子程序已經終止,正等待父程序擷取程序終止狀态,則取得孩子的終止狀态立刻傳回;(3)若果沒有任何子程序,則立即出錯傳回。如果在任意時刻調用wait,則程序可能會阻塞。

pid_t wait(int *status);   //在一個子程序終止前,wait使調用者阻塞

pid_t waitpid(pid_t pid, int *status, int options); //可以使調用者不阻塞

pid_t wait3(int *status, int options,struct rusage *rusage);

pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);

寫個程式,子程序給出退出狀态,父程序通過wait和waitpid擷取退出狀态。程式如下:

《APUE》讀書筆記—第八章程式控制
《APUE》讀書筆記—第八章程式控制
《APUE》讀書筆記—第八章程式控制

程式執行結果如下:

《APUE》讀書筆記—第八章程式控制

waitpid函數中的pid參數取值情況:

pid=-1   等待任一子程序,此時相當于wait

pid>0     等待期程序id與pid相等的子程序

pid==0    等待期組id等于調用程序組id的任一個子程序

pid<-1    等待其組id等于pid絕對值的任一子程序

另外提供了一種避免僵死程序的方法:調用fork兩次。

  exec函數,用fork函數建立子程序後,子程序往往要調用一種exec函數以執行另外一個程式。當調用exec函數時,該程序執行的程式完全替換為新程序,exec函數不建立程序,隻是用一個全新的程式替換了目前的正文、資料、堆和棧段。執行完之後,程序id不會改變。在程序間通信的時候,經常需要調用exec函數啟動另外一個例程。函數原型如下:

extern char **environ; 

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg,..., char * const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

int execve(const char *file, char *const argv[],char *const envp[]);

其中l表示清單(list),v辨別矢量(vector)。execl、execlp、execle中每個指令行參數都是一個單獨參數,這種參數以空指針結尾。execv、execvp、execve指令行參數是一個指針數組。e辨別環境變量,傳遞參數。寫個程式進行測試,程式分為兩部分,exec調用的程式,exec執行程式。程式如下:

exec調用程式如下,可執行檔案名稱為exectest,存放在/home/anker/programs目錄下。

《APUE》讀書筆記—第八章程式控制
《APUE》讀書筆記—第八章程式控制

 exec執行程式如下:存放在/home/anker/programs目錄下。

《APUE》讀書筆記—第八章程式控制
《APUE》讀書筆記—第八章程式控制

執行結果如下:

《APUE》讀書筆記—第八章程式控制

system函數,在程式中執行一個指令字元串。例如system("date > file")。函數原型如下:

int system(const char *command);、

system函數實作中調用了fork、exec和waitpid函數,是以有三種傳回值。system函數實作一下,沒有信号處理。程式如下:

《APUE》讀書筆記—第八章程式控制
《APUE》讀書筆記—第八章程式控制
《APUE》讀書筆記—第八章程式控制

system函數可以設定使用者的id,這是一個安全漏洞。

程序會計:啟用後,每當程序結束時候核心就寫一個會計記錄,包括指令名、所使用的cpu時間總量、使用者id群組id、啟動時間等。accton指令啟動會計處理,會計記錄寫到指定的檔案中,linux中位于/var/account/ pacct。會計記錄結構定義在<sys/acct.h>頭檔案中。

#define acct_comm 16

typedef u_int16_t comp_t;

struct acct {

    char ac_flag;           /* accounting flags */

    u_int16_t ac_uid;       /* accounting user id */

    u_int16_t ac_gid;       /* accounting group id */

    u_int16_t ac_tty;       /* controlling terminal */

    u_int32_t ac_btime;     /* process creation time(seconds since the epoch) */

    comp_t    ac_utime;     /* user cpu time */

    comp_t    ac_stime;     /* system cpu time */

    comp_t    ac_etime;     /* elapsed time */

    comp_t    ac_mem;       /* average memory usage (kb) */

    comp_t    ac_io;        /* characters transferred (unused) */

    comp_t    ac_rw;        /* blocks read or written (unused) */

    comp_t    ac_minflt;    /* minor page faults */

    comp_t    ac_majflt;    /* major page faults */

    comp_t    ac_swaps;     /* number of swaps (unused) */

    char      ac_comm[acct_comm+1];/* command name (basename of lastexecuted command; null-terminated) */

    char      ac_pad[x];    /* padding bytes */

};

使用者辨別,用getlogin函數擷取使用者的登入名。函數原型如下:char *getlogin(void)。

程序時間:牆上時鐘時間、使用者cpu時間和系統cpu時間。任一個程序都可以調用times函數擷取它自己及已終止子程序時間。程序時間操作函數及結構如下:

clock_t times(struct tms *buf);

struct tms {

    clock_t tms_utime;  /* user time */

    clock_t tms_stime;  /* system time */

    clock_t tms_cutime; /* user time of children */

    clock_t tms_cstime; /* system time of children */

總結:通過本章的學習。完全的了解unix的程序控制,掌握了fork、exec簇、wait和waitpid程序控制函數。另外學習了system函數和程序會計。了解了解釋器檔案及其工作方式,使用者辨別和程序時間。

繼續閱讀