天天看點

Unix進階環境程式設計

[07] Unix程序環境

==================================

1、 程序終止

    atexit()函數注冊終止處理程式。

    exit()或return語句:

        終止處理程式->終止處理程式->标準I/O清除->_exit()->進入核心。

    _exit()直接進入核心。

2、 環境表

    extern char **environ;

    例:

    for( i=0; environ[i] != NULL; i++)

    {

        printf( "env[%d]: %s\n", i, environ[i] );

    }

3、 C程式存儲空間布局

    * 正文                    即代碼段

    * 初始化資料段            如:int maxcount = 99;

    * bss(非初始化資料)        如:long sun[1000];

    * 棧                    自動變量及函數調用所需的資訊

    * 堆                    動态存取配置設定,在bss頂端,棧的底端。

    -----------------------------------------------------------

    高位址     棧 -> .... <- 堆  bss  初始資料  正文    低位址

    注: 可用size指令檢視text data bss資訊。

4、 存儲器配置設定

    alloca()是在棧中配置設定空間,函數調用結束後空間自動釋放,但有些系統不支援。

5、 環境變量

    char * getenv(char * name);

    int putenv( const char * str );

    int setenv(const char * name, const char * value, int rewrite );

    void unsetenv(const char *name );

    注:environ表及字串存放在棧的頂部,當調用上述函數時,可能需将其移到至堆中。

6、 setjmp和longjmp

    #include <setjmp.h>

        jmp_buf jmpbuffer;

        ......

    _@:    setjmp( jmpbuffer);

    call    my_function();

        my_function(){

            longjmp(jmpbuffer, 1 );

        }

    注:

    *    代碼中,在_@處調用setjmp函數,然後經過多層調用至my_function函數,

    在該函數中調用longjmp(,1)函數,責程式調整至_@處。

    即:反繞過上層的所有棧幀,跳至_@所在處的棧狀态。

    *    setjmp傳回值: 如果直接調用,則傳回0, 否則傳回longjmp的第二個參數。

7、 getrlimit和setrlimit

    #include <sys/time.h>

    #include <sys/resource.h>

    int getrlimit( int resource, struct rlimit * rlptr );

    int setrlimit( int resource, const struct rlimit * rlptr );

[08] 程序控制

1、    程序辨別

    程序ID:

        0    交換程序swapper

        1    init,在自舉結束後由核心調用。它不會終止,但是它是普通使用者程序,以超級使用者權限運作。

        2    頁精靈程序,pagedaemon

    函數:

    #include <sys/types.h>

    #include <unistd.h>

    pid_t getpid(void);

    pid_t getppid(void);

    uid_t getuid(void);

    uid_t geteuid(void);

    gid_t getgid(void);

    git_t getegid(void);

2、    fork函數

    fork() : 建立子程序。

    pid_t fork(void);

    * 調用fork後,子程序和父程序繼續執行fork之後的代碼。

    * fork傳回兩次,子程序傳回0,父程序傳回子程序ID。

    * fork之後,無法知道父程序和子程序哪個先執行。

    int main()

        if( (pdi = fork()) < 0 )

            error();

        else if( pid == 0 ){//子程序

        else{                //父程序

        ……                //父子程序繼續執行fork之後的代碼

    兩種fork用法

    * fork之後,父子程序各自執行自己的代碼。在網絡伺服器中常見。

    * 程序執行不同的程式。fork後,子程序調用exec函數。

3、    vfork

    vfork()建立一個新程序。

    與fork差別:

    * vfork保證子程序先于父程序執行,

    * vfork子程序在父程序的位址空間内執行。

4、 wait和waitpid函數

5、 競态條件

    多個程序企圖對共享資料進行處理,但結果取決于程序的運作順序。

6、 6個exec函數對比

    --------------------------------------------------------------

    函數    path    name        參數表    argv[]        environ    envp[]

    execl    *                    *                    *

    execlp            *            *                    *

    execle    *                    *                            *

    execv    *                            *            *

    execvp            *                    *            *

    execve    *                            *                    *

    p    取filename做參數

    l    取參數表

    v    表示去argv[]數組

    e    表示取envp[]數組    

[10] 信号

=================================

1、 概念

    信号:軟體中斷,以SIG開頭,在<signal.h>中定義。

    三種方式處理信号:

    * 忽略此信号,但SIGKILL和SIGSTOP不能被忽略,因為他向超級使用者提供了一種使程序終止或停止的可靠方法。如果忽略某些硬體異常産生的信号,則程序的行為未知。

    * 捕捉信号,要通知核心在某信号發生時,調用一個使用者函數。

    * 執行系統預設動作。

2、 signal函數

    #include <signal.h>

    void (*signal (int signo, void (*func)(int))) (int );

        * 傳回值 void (*signal)(int);

          傳回老的信号處理程式指針。

        * 參數

          signo: 信号表示

          void (*func)(int); 新的信号處理程式,

           若為SIG_IGN - 忽略此信号

           若為SIG_DFL - 系統預設操作

    用typedef方法定義signal函數:

        typedef void Sigfunc(int);

        Sigfunc *signal( int, Sigfunc *);

    當程序調用fork時,子程序繼承了父程序的信号處理方式。因為子程序開始時複制了父程序的存儲映像,所有信号捕捉函數的位址在子程序中式有意義的。

3、kill和raise函數

    int kill( pid_t pid, int signo );

    int raise(int signo );

    * kill 将信号發送給程序或程序組

      pid>0 将信号發送給該程序

      pid==0 将信号發送給程序組ID等于發送程序的程序組ID,而且發生程序有許可權向其發送信号的所有程序。

      pid<0 将信号發送給程序組ID等于pid絕對值,與pid==0類似。

    * raise 程序向自身發送信号

4、alarm和pause函數

    alarm: 設定一時間段,當超過該時間時,産生SIGALRM信号,預設動作時終止該程序。

    unsigned int alarm( unsigned int seconds );

    * seconds 是秒數。

    * 每個程序隻能有一個鬧鐘時間,如果調用alarm時,以前以為該程序設定過鬧鐘時間,而且還沒有逾時,則該鬧鐘時間的餘留值作為本次alarm函數調用的傳回值。以前登記的鬧鐘時間被新值替換。

    int pause(void)

    注:

    隻有執行了一個信号處理程式并從其傳回,pause才傳回-1, errno設為EINTR。

    (書中有很多alarm例子,說明使用信号所要注意的地方)

5、 信号集

    用sigset_t類型表示一信号集。

    int sigemptyset(sigset_t * set );

    int sigfillset(sigset_t *set );

    int sigaddset(sigset_t *set, int signo );

    int sigdelset(sigset_t *set, int signo );

    ----傳回值: 成功 0, 錯誤 -1

    int sigismember( conset sigset_t *set, int signo );

    ----傳回值: 真 1, 假 0

6、 sigprocmask函數

    功能:檢測或更改程序的信号屏蔽字。

    int sigprocmask(int how, const sigset_t *set, sigset_t *oset );

    * 若oset非空, oset為目前信号屏蔽字

    * 若set非空, how訓示如何修改目前信号屏蔽字。

    * how:

    *     SIG_BLOCK    或操作

    *     SIG_UNBLOCK 與

    *     SIG_SETMASK 指派操作

7、 sigpending函數

    功能:傳回調用程序的被阻塞不能遞送和目前未決的信号集。

    int sigpending(sigset_t *set );

8、 sigaction函數

    功能,取代了signal函數。

    int sigaction(int signo, const struct sigaction *act, \

            struct sigaction * oact );

    struct sigaction{

        void        (*sa_handler)();

        sigset_t    sa_mask;

        int            sa_flags;

    };

    sa_handler    信号捕捉函數

    sa_mask        調用sa_handler之前需要添加的信号屏蔽字,調用結束後,程序的信号屏蔽字再恢複為原先值。

    sa_flags    信号處理選項。

9、 sigsetjmp和siglongjmp

    int sigsetjmp( sigjmp_buf env, int savemask );

    void siglongjmp( sigjmp_buf env, int val );

    差別:

    當savemask 為非0時,sigsetjmp在env中儲存程序的目前屏蔽字,調用siglongjmp時,siglongjmp從中恢複儲存的信号屏蔽字。

    而,setjmp和longjmp并不保證該操作。

10、sigsuspend函數

    #include<signal.h>

    int sigsuspend(const sigset_t sigmask );

    程序的信号屏蔽字設定為sigmask,在捕捉到一個信号或發生了一個會終止該程序的信号之前,該程序也被挂起。如果捕捉到一個信号而且從該信号處理程式傳回,則sigsuspend傳回,并且該程序的信号屏蔽字設定為調用sigsuspend之前的值。

[11] 終端

[12] 進階I/O    

====================================

1、 非阻塞I/O

    對一個給定的描述符有兩種方法對其指定非阻塞I/O

    * 如果是調用open以擷取描述符,則可以指定O_NONBLOCK标志

    * 對于已經打開的描述符,調用fcntl打開O_NONBLOCK檔案狀态标志。

2、 記錄鎖

    功能: 一個程序正在讀或者修改檔案的某個部分時,可以阻止其他程序修改同一檔案區。它鎖定的隻是檔案的一個區域,也可是整個檔案。

    fcntl記錄鎖

    #include <fcntl.h>

    int fcntl ( int filedes, int cmd, struct flock * flockptr );

    參數:

    * cmd        /* F_GETLK, F_SETLK, F_SETLKW */

        F_GETLK    如果存在一把鎖,則把現存的鎖的資訊寫到flockptr指向的結構中。若不存在,則将l_type設定為F_UNLCK,其他域儲存不變。

        F_SETLK 設定由flockptr所描述的鎖。或者用于清除所描述的記錄鎖(設定l_type為F_UNLCK)。

        F_SETLKW 這是F_SETLK得阻塞版本。

    * flockptr    以下結構指針

    struct flock {

        short l_type;    /* F_RDLCK, F_WRLCK, F_UNLCK */

        off_t l_start;    /* offset in bytes, ralative to l_whence */

        short l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */

        off_t l_len;    /* length, in bytes; 0 means lock to EOF */

        pid_t l_pid;    /* returned with F_GETLK */

    [注]

    1、當程序終止,鎖全部釋放,當描述符被關閉時,該描述符的鎖也被釋放。

    2、fork後,子程序不繼承父程序的記錄鎖

    3、exec後,新程式可以繼續原執行程式的鎖

[13] 精靈程序

================================

1. 程式設計規則

    - 調用fork,然後父程序調用exit。

      用處: 1.如果該精靈程序有Shell啟動,那麼父程序終止,是的Shell認為該條指令已經執行完成。

          2.保證了子程序不是一個程序組的首程序,它程序了父程序的程序組ID。

    - 調用setsid建立一個新的會話期。

          1.是程序成為對話期首程序。 2.成為一個新程序組的首程序。 3.沒有控制終端。

    - 将工作目錄更改為跟目錄。

    - 将檔案方式建立屏蔽字設定為0。

          1.去除由父程序繼承得來的屏蔽字。

    - 關閉不需要的檔案描述符。這與具體的精靈程序有關。

#include <sys/type.h>

#include <sys/stat.h>

#include <fcntl.h>

int daemon_init(void)

{

    pid_t    pid;

    if( (pid = fork() )<0 )

        return -1;

    else if( pid!=0 )

        exit(0);        //parent goes bye-bye

    setsid();

    chdir("/");

    umask(0);        //clear file mode creation mask

    return 0;

}

[14] 程序間通信

1、 管道

    int pipe( int filedes[2]);

    filedes[0]為讀而打開,filedes[1]為寫而打開。filedes[1]的輸出是filedes[0]的輸入。

14.6----