• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • UNIX 編程資料(1)

    發表于:2007-05-26來源:作者:點擊數: 標簽:
    整理了一篇UNIX編程的文章,拿出來和大家共享 UNIX 編程資料 第一章 概述 1.1UNIX的版本 UNIX操作系統是貝爾實驗室于六十年代末用C語言研制 開發 的。經過幾十年的發展,已經成為流行于從大型機、小型機到工作站甚至微機等多種平臺的操作系統。UNIX的成功同

    整理了一篇UNIX編程的文章,拿出來和大家共享

     UNIX 編程資料

    第一章 概述
    1.1UNIX的版本
    UNIX操作系統是貝爾實驗室于六十年代末用C語言研制開發的。經過幾十年的發展,已經成為流行于從大型機、小型機到工作站甚至微機等多種平臺的操作系統。UNIX的成功同時也推動了C語言的普及。本教材的目的是講解UNIX系統下的C程序設計,使C程序員快速掌握UNIX系統下的編程開發。作者在進行UNIX編程開發的實踐過程中,深感實例的重要性-一個簡短的C語言實例往往勝過長篇累牘的文字說明,當然了,文字說明也是必不可少的。本教材將本著實例優先的原則,使您能夠對UNIX編程開發快速入門。

    UNIX的版本不統一是出了名的,從UNIX的發展歷史來看,主要有兩大流派:AT&T的UNIX系統V版本和加州大學伯克利分校的BSD版本,在此基礎上,各家UNIX廠商均開發了各自的UNIX操作系統。如:工作站廠商中有HP的hpux、SUN的solaris、SGI的irix、IBM的AIX等,小型機有VAX上的Ultrix,微機上有SCO
    UNIX、微軟的Xenix以及隨著Inte.net而風靡全球的Linux等。由于Windows
    NT的異軍突起,對UNIX的市場形成巨大的威脅,各大UNIX廠商不得不聯合起來,在工作站市場上,統一以系統V版作為標準,加入BSD版本中的一些優點,支持統一的CDE(Common
    Desktop Environment)窗口環境,以與Windows NT進行對抗。

    1.2 UNIX編程環境

    UNIX操作系統通過Shell程序實現系統與用戶的交互,在Shell提示符下,用戶鍵入UNIX命令,即可得到操作系統的輸出結果。BSD系統的常用Shell是C  Shell,缺省提示符是"%",系統V的常用Shell是Bourne Shell(現在多為Korn
    Shell),缺省提示符是"$",有關Shell的編程,我們在后面的章節中進行介紹。

    UNIX上的標準編譯器是cc。在Shell提示符下(以C Shell為例)鍵入下列命令:
    %cc -o hello hello.c

    即將C文件hello.c編譯為可執行文件hello。在編譯多個文件生成一個可執行文件時,UNIX提供命令make。用戶需要針對多個C文件,按照一定的格式編寫一個叫做Makefile的文本文件。下面是SGI上的一個Makefile的例子:
    CC = cc
    CFLAGS = $(DEBUG) -cckr -I$(INC)/X11 -DSYSV
    DEBUG = -g
    INC = /usr/include
    LDFLAGS = -lXext -lXm -lXt -lX11 -lPW -lc
    OBJS = initx.o windowx.o
    TGTS = showxwin

    all:: $(TGTS)
    showxwin: $(OBJS)
    $(CC) -o $@ $(OBJS) $(CFLAGS) $(LDFLAGS)

    大寫字母的字串是一些宏,CC是編譯器的名字、CFLAGS定義cc的編譯開關、DEBUG是調試宏、INC是頭文件所在目錄、LDFLAGS定義了編譯連接庫、OBJ定義了目標文件名、TGTS定義了可執行文件名。在Shell提示符下直接鍵入:
    %make

    即可將Makefile中指定的所有C文件進行編譯并生成可執行文件。

    1.3 UNIX編程中的基本概念
    在討論UNIX編程開發前,首先需要闡明系統調用和庫函數這兩個概念。
    一個系統調用指一個需要操作系統代表用戶程序來執行某些任務的請求。例如:read是一個系統調用,它請求操作系統存儲在一個磁盤設備(或其他設備)上的數據去填充一個緩沖區。如果任何人在他們想執行任務的時候都能隨便訪問設備,那么后果將是不可預測的。所以,這種服務必須請求操作系統來做,它(經常是透明地)記錄所有處理每個設備的請求。
    而一個庫函數,并不經常需要操作系統來執行其任務。例如數學庫函數中的sin(),cos()等,這些計算只需要簡單地對一個有限序列求和,所以并不需要操作系統干預。
    在UNIX操作系統中,有一個常用的命令man,可用來查閱命令、庫函數和系統調用等的具體使用方法。傳統 Unix 聯機幫助手冊的分節法為:
    1 用戶級命令(User-level commands)
    2 系統調用(System calls)
    3 庫函數(Library functions)
    4 設備及驅動程序(Devices and device drivers)
    5 文件格式(File formats)
    6 游戲(Games)
    7 雜項(Various miscellaneous stuff - macro packages etc.)
    8 系統維護及操作命令(System maintenance and operation commands)

    第二章 標準輸入/輸出庫

    2.1 概述

    本章介紹UNIX的標準輸入/輸出庫,UNIX提供一些庫函數完成高級輸入/輸出,為程序員提供了三方面的主要功能:
    ·自動開辟緩沖區。即使一次讀或寫的數據只有幾個字節,庫函數仍然在大到由數千個字節組成的"塊"中執行實際輸入或輸出(緩沖區大小通常由頭文件stdio.h中的常量BUFSIZ定義)。這個緩沖區在內部開辟給庫函數使用,對于程序員來說是透明的;

    ·自動執行輸入和輸出轉換。

    ·輸入輸出被自動格式化。以上兩點在C語言的教程中一般均以講到。

    在標準輸入/輸出庫中,一個文件被稱為一串字符流,并且被一個指向類型為FILE的目標指針所描述,該指針被稱為文件指針。在UNIX中文件指針stdin、stdout、stderr是預先定義好的,分別對應標準輸入(鍵盤)、標準輸出(終端屏幕)和標準錯誤輸出。

    2.2 庫函數介紹

    ·文件創建和關閉
    fopen()用于打開已存在的文件或創建新文件

    ·文件讀寫
    1、 一次處理一個字符 getc(), putc()
    2、 一次處理多個字符 fgets(), fputs()
    3、 文件的二進制讀寫 fread(), fwrite()
    4、 文件的格式化輸入/輸出 fscanf(), fprintf()
    5、 字符串的格式化輸入/輸出 sscanf(), sprintf()

    ·文件移動定位
    用于在文件中移動的標準輸入/輸出庫函數是fseek(),它接收三個參數:一個文件指針指向一個打開的字符流;一個整數指明要移動的字節數,稱為offset;一個整數指明從文件中什么位置移動。

    第三章 低級輸入/輸出

    3.1 概述

    與第二章內容相對應,本章介紹UNIX系統中通過系統調用來實現的輸入/輸出,通常稱之為低級輸入/輸出。這些系統調用能夠直接實現對設備(如磁帶驅動器等)的輸入和輸出,程序員能夠決定要使用的緩沖區的大小,而不象標準輸入/輸出庫函數那樣透明設定緩沖區大小。

    在標準輸入/輸出庫中,一個文件是由一個文件指針來對應的。當使用低級界面時,則用一個文件描述字對應一個文件。文件描述字是一個小的整數。有3個事先定義的文件描述字0、1和2,分別對應標準輸入、標準輸出和標準錯誤輸出。一般說來,文件描述字都是作為系統調用的第一個參數給出的。

    3.2 相關系統調用介紹

    ·文件創建和關閉
    open()用于為讀寫而打開一個文件,或用它來創建新文件。
    int open (const char *path, int oflag, ... /* mode_t mode */);
    open使用三個參數:一個字符串path包含要打開的文件名;一個整數oflag指明文件將被如何打開;整數mode在創建文件時使用。常用的oflag包括:
    O_RDONLY 打開文件僅用于讀。
    O_WRONLY 打開文件僅用于寫。
    O_RDWR 打開文件用于讀寫。
    O_CREAT 如果文件不存在,則創建,此時mode作為第三個參數給出。
    close()用于關閉一個已經打開的文件。

    ·文件讀寫
    read()用于讀文件,格式為:
    read(int fildes, void *buf, size_t nbyte);
    三個參數說明如下:filedes是文件描述字;指針buf指向一個數據將被讀入的緩沖區;整數nbytes指明要讀的字節個數。成功時返回實際讀入的字節數,出錯則返回-1。
    write()用于寫文件,與read類似,格式為:
    write(int fildes, void *buf, size_t nbyte);
    三個參數說明如下:filedes是文件描述字;指針buf指向一個數據將被寫入的緩沖區;整數nbytes指明要寫的字節個數。成功時返回實際寫入的字節數,出錯則返回-1。

    ·文件移動定位
    用于在文件中移動的低級輸入/輸出系統調用是lseek(),與fseek()類似,它也接收三個參數:一個文件描述字對應一個打開的文件;一個整數指明要移動的字節數,稱為offset;一個整數指明從文件中什么位置移動。

    ·復制文件描述字
    有時候有不只一個文件描述字對應一個文件。當創建子進程時(參加后面關于進程開發的章節),這一點很常用。為了獲得一個新的文件描述字,并保證其與fd對應同一個文件,應調用
    fd2 = dup(fd)
    fd2現在和fd對應同一個文件,并且和fd一樣在文件中有相同的位置。

    第四章 文件與目錄編程
    4.1 基本概念
    ·文件目錄概述
    文件系統是UNIX對計算機技術的一大貢獻!UNIX系統的文件管理十分靈活、功能強大,許多首次在UNIX系統中出現的概念被其他操作系統所采用,如MS-DOS等。

    UNIX系統提供了一種層次目錄方案。目錄就象存放一組文件的柜子一樣,目錄也可以包括在其他目錄中,這樣就形成了一種龐大的、具有分支的組織方式,這種結構通常被稱為樹狀結構。目錄實際上也是一種特殊的文件。命令、數據文件、其他命令甚至設備(特別文件)都可以作為目錄中的項(文件)。

    ·I標識號、I列表和I節點
    一個目錄是由一系列結構組成的;每個結構包含一個文件名和一個指向文件自身的指針,該指針是一個整數,稱為文件的I標識號。當文件被訪問時,它的I標識號用來作為索引打開一個系統表(I列表),系統中存放著文件(I節點)的實體。I節點包含了對文件的描述:
    ·文件自身的用戶和用戶組ID
    ·文件的保護碼
    ·文件內容所在的物理磁盤地址
    ·文件的大小
    ·最后一次I節點改變的時間,最后一次使用和修改的時間
    ·連接該文件的次數,即它出現在其他目錄中的次數
    ·一個指明文件類型的標記(目錄、普通文件、特別文件)
    ·文件的三級保護

    UNIX把使用文件的用戶分成三個等級:文件所有者、同組用戶和其他用戶。文件所有者也稱文件主,是文件的創建者,對該文件擁有所有權限;同組用戶是具有相同組標識號的所有用戶,文件主可以決定一個文件屬于哪個組以及該組用戶對文件的存取權;其他用戶是指與文件主無關的用戶,他們與文件主不屬于同一個用戶組,其他用戶對一個文件的訪問權限也由該文件主決定的。

    一個文件的訪問權限存放在該文件I節點的di_mode域中,di_mode的0-8位表示文件主、文件組用戶和其他用戶對該文件的存取權限。舉個例子,用"ls
    -l"命令可列出文件hello.c的模式和屬性:

    -rwxr-xr-x 1 yds user 58 9月 25日 10時54分 hello.c

    最左面一欄顯示了該文件的模式:文件主對該文件可讀(r)、可寫(w)、可執行(x),同組用戶對該文件可讀、可執行,其他用戶對該文件可讀、可執行。相應的,di_mode的0-8位為111101101(0755)。

    4.2 文件編程介紹

    ·檢查訪問權限-access系統調用

    access系統調用的格式為:

    #include <unistd.h>
    int access(const char *path, int amode);

    其中:參數path指出被檢查文件的路徑,參數amode指出訪問權限。Access判斷調用進程的實際用戶對文件path是否具有amode所指定的訪問權限,若有相應權限,access返回0,否則返回-1。

    參數amode可取以下值或它們的邏輯"或":
    R_OK 檢查讀權限
    W_OK 檢查寫權限
    X_OK 檢查執行(搜索)權限
    F_OK 檢查文件是否存在

    例如:access("hello.c", R_OK|W_OK),
    用來檢查實際用戶對文件hello.c是否具有讀/寫權;access("hello.c",F_OK) 判斷文件hello.c是否存在。

    ·鏈接與刪除文件-link和unlink系統調用

    link和unlink系統調用的格式為:

    #include <unistd.h>
    int link (const char *path1, const char *path2);
    int unlink(const char *path);

    其中:參數path1指出已經存在的要被鏈接的文件路徑名,path2指出要建立的鏈接文件。link實現path2到path1的鏈接,相當于給path1起了一個別名,同時文件path1的鏈接計數加1。若成功則返回0,否則返回-1。

    參數path指出要被刪除的文件路徑名。Unlink刪除由path指出的文件,若成功則返回0,否則返回-1。

    ·從I節點上獲取信息-stat與fstat系統調用

    stat與fstat的調用格式為:

    #include <sys/types.h>
    #include <sys/stat.h>
    int stat(const char *path, struct stat *buf);
    int fstat(int fildes, struct stat *buf);

    說明:stat和fstat都用于獲取文件I節點中有關狀態信息。stat根據參數path給出的文件路徑名,搜索它對應的盤I節點,而fstat則根據參數fildes給出的文件描述字去查找對應的I節點。這兩個調用都把從I節點中獲取到的信息重組后放入參數buf指向的stat結構中(stat結構的說明在文件/usr/include/sys/stat.h中)。這兩個調用成功時均返回0,否則返回-1。

    stat與fstat調用無論在使用上還是在功能上都是非常類似的,在參數上有一點區別。下面我們來看一個例子。

    /* statfile.c */
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>

    main(argc,argv)
    int argc;
    char *argv[];
    {
    int fd;
    struct stat statbuf;
    if (argc!=2){
    printf("usage: statfile filename!\n");
    exit(1);
    }
    if ((fd = fopen(argv[1], O_RDONLY)) == -1)
    fprintf(stderr, "Cannot open %s!\n", argv[1]);
    if (unlink(argv[1]) == -1)
    fprintf(stderr,"Cannot unlink %s!\n", argv[1]);
    if (stat(argv[1], &statbuf) == -1) /* by file name */
    fprintf(stderr, "stat %s fails as it should !\n");
    else
    printf("stat %s succeed!\n", argv[1]);
    if (fstat(fd, &statbuf) == -1) /* by file descriptor */
    fprintf(stderr, "fstat %s fails!\n", argv[1]);
    else
    printf("fstat %s succeeds as it should!\n", argv[1]);
    }

    程序首先打開命令行中指定的文件,然后用unlink將該文件刪除,接著分別用stat與fstat系統調用獲取該文件的信息。假設當前命令下有一個名為xxx.c的文件,運行

    %statfile xxx.c

    后,將會輸出如下結果:

    stat xxx.c fails as it should!
    fstat xxx.c succeeds as it should!

    從中可知,當一個打開文件被刪除后,用stat無法獲取該文件的信息。而fstat就可獲取該文件的信息。這是由于文件名在unlink之后已從目錄中除去,無法找到該文件名,而文件描述字則因文件仍打開而保存下來。因此stat不能成功返回,但fstat仍可成功返回。

    使用stat調用來判定一個文件為何種文件類型時相當有用。例如下列代碼:

    stat("hello", &statbuf);
    if (statbuf.st_mod & S_IFMT) == S_IFDIR)
    printf("This is a directory file!\n");
    else if (statbuf.st_mod & S_IFMT) == S_IFREG)
    printf("This is a regular file !\n");
    else …

    以上代碼可判定當前目錄下的hello文件是否為目錄文件或其他類型的文件。

    4.3 目錄編程介紹

    UNIX把目錄也視為一種文件,稱為目錄文件,并同普通文件一樣進行管理和保護。如open、close、read、lseek等文件操作對目錄文件都是有效的。前面講到的文件編程中的各個系統調用對于目錄來說也同樣有效。但是與普通文件相比目錄文件又具有自身的一些特點:

    目錄文件的讀/寫/執行訪問權限有特殊的含義:讀權限允許用戶讀取目錄項的內容;寫權限允許用戶創建或刪除一個文件;執行權限則允許用戶檢索目錄(此時通常稱為目錄搜索權限)。

    目錄的創建、刪除與普通文件也不同,另外,任何用戶都不能對目錄文件以寫方式打開進行文件寫操作。

    ·目錄的創建和刪除-mkdir和rmdir系統調用

    mkdir 和rmdir系統調用的格式為:

    #include <sys/types.h>
    #include <sys/stat.h>
    int mkdir (const char *path, mode_t mode);

    #include <unistd.h>
    int rmdir(const char *path);

    其中:參數path分別指出要創建和刪除的目錄文件的文件名。mkdir調用中的參數mode指出新創建目錄文件的文件模式。新創建目錄后,除"."和".."兩項外,無別的目錄項;刪除目錄時,要求目錄中除"."和".."兩項外,也無別的目錄項。這兩個系統調用Access成功時都返回0,否則返回-1。

    ·目錄的讀?。璷pendir/readdir/closedir庫函數

    目錄文件可以像普通文件一樣,先用系統調用open以讀方式打開,再用read調用讀取其中的內容。同時,由于目錄文件是由具有目錄結構的目錄項組成的,用read讀取其內容有些不方便。UNIX提供的庫函數opendir/readdir/closedir等可以方便地實現目錄讀取。函數說明如下:

    #include <sys/types.h>
    #include <sys/dir.h>
    DIR *opendir(char *filename);
    struct direct *readdir(DIR *dirp);
    void closedir(DIR *dirp);

    說明:參數filename指出要打開的目錄路徑名,庫函數opendir返回一個指向結構DIR(在文件/usr/include/sys/dir.h中定義)的指針。庫函數readdir和closedir均以這個指針作為參數,其中readdir返回一個指向結構direct的指針。有關目錄的操作均可基于這個指針。下面是一個例子,查找當前目錄下文件名為"name"的文件。

    len = strlen(name);
    dirp = opendir(".");
    if (dirp == NULL) {
    return NOT_FOUND;
    }
    while ((dp = readdir(dirp)) != NULL) {
    if (dp->d_namlen == len && !strcmp(dp->d_name, name)) {
    closedir(dirp);
    return FOUND;
    }
    }
    closedir(dirp);
    return NOT_FOUND;

    庫函數closedir關閉打開的目錄。值得注意的是,上面這一小段代碼在編程中很實用,稍加修改即可實現UNIX下類似"ls"的簡單命令。
    第五章 基本進程編程

    5.1 概述

    UNIX系統為程序員提供了一個強有力的工具:在一個程序中執行另一個程序。執行一個程序最簡單的途徑就是使用庫函數system。該函數使用一個參數:一個包含要被執行的命令的字符串。這一庫函數的特點是用法簡單,在程序中調用簡單的UNIX命令時很有用。但是由于它的調用要由SHELL進程來實現,故效率并不高,在實際的編程中應用并不廣泛。本章主要介紹在實際編程中經常使用的有關進程控制和管理方面的系統調用,它們包括:

    fork - 創建一子進程
    exec - 執行子進程
    exit - 終止進程執行
    wait - 等待子進程暫?;蚪K止
    setpgrp - 設置進程標識符
    getpid、getppid - 獲取進程標識符
    setuid、setgid - 設置進程的用戶標識符
    getuid、geteuid、getgid、getegid - 獲取進程的用戶標識符

    5.2 進程控制

    1. fork系統調用

    系統調用fork是UNIX操作系統創建新進程的唯一手段,習慣上將新創建的進程稱為子進程,調用fork的進程稱為父進程。
    fork系統調用的格式為:

    int fork()

    fork系統調用沒有參數,如執行成功,則創建一子進程,子進程繼承了父進程的某些屬性。當從該系統調用返回時,系統中已有兩個用戶級環境完全相同的進程存在。這兩個進程從fork調用中得到的返回值不同,其中子進程得到的返回值為零,父進程得到的返回值是最新創建的子進程的進程標識符。

    2. exec系統調用

    fork系統調用只是將父進程的環境拷貝到新進程中,而沒有啟動執行一個新的目標程序。UNIX系統提供了exec系統調用,用它更換進程的執行映象,啟動新的目標程序。例如:UNIX系統中的所有命令都是通過exec來執行的。

    exec系統調用有六種不同的使用格式,但在核心中只對應一個調用入口。它們有不同的調用格式和調用參數。這六種調用格式分別為:

    #include <unistd.h>
    int execl (const char *path, const char *arg0, ..., const char
    *argn, (char *)0);
    int execv (const char *path, char *const *argv);
    int execle (const char *path, const char *arg0, ..., const char
    *argn,
    (char *0), const char *envp[]);
    int execve (const char *path, char *const *argv, char *const *envp);
    int execlp (const char *file, const char *arg0, ..., const char
    *argn, (char *)0);
    int execvp (const char *file, char *const *argv);

    說明:參數path指出一個可執行目標文件的路徑名;參數file指出可執行目標文件的文件名。arg0作為約定同path一樣指出目標文件的路徑名;參數arg1到argn分別是該目標文件執行時所帶的命令行參數;參數argv是一個字符串指針數組,由它指出該目標程序使用的命令行參數表,按約定第一個字符指針指向與path
    或file相同的字符串;最后一個指針指向一個空字符串,其余的指向該程序執行時所帶的命令行參數;參數envp同argv一樣也是一個字符指針數組,由它指出該目標程序執行時的進程環境,它也以一個空指針結束。

    exec的六種格式在以下三點上有所不同:
    (1) path是一個目標文件的完整路徑名,而file是目標文件名,它是可以通過環境變量PATH來搜索的;
    (2) 由path或file指定的目標文件的命令行參數是完整的參數列表或是通過一指針數組argv來給出的;
    (3) 環境變量是系統自動傳遞或者通過envp來給出的。

    下圖說明了exec系統調用的六種不同格式對以上三點的支持。

    系統調用 參數形式 環境傳送 路徑搜索
    Execl 全部列表 自動 否
    Execv 指針數組 自動 否
    Execle 全部列表 不自動 否
    Execve 指針數組 不自動 否
    Execlp 全部列表 自動 是
    Execvp 指針數組 自動 是

    3. exit、wait系統調用
    (1) exit系統調用格式如下:
    #include <stdlib.h>
    void exit(int status);
    #include <unistd.h>
    void _exit(int status);
    說明:exit的功能是終止進程的執行,并釋放該進程所占用的某些系統資源。參數status是調用進程終止時傳遞給其父進程的值。如果調用進程執行exit系統調用時,其父進程正在等待子進程暫?;蚪K止(使用wait系統調用),則父進程可立刻得到該值;如果此時父進程并不處在等待狀態,那么一旦父進程使用wait調用,便可立刻得到子進程傳過來的status值,注意:只有status的低八位才傳遞給其父進程。

    系統調用_exit與exit之間的差異是_exit只做部分的清除,因此建議不要輕易地使用這種調用形式。
    每個進程在消亡前都要調用該系統調用,沒有顯示地使用該系統調用,則生成目標文件的裝載程序為該進程隱含地做這一工作。

    (2) wait系統調用格式如下:
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait (int *statptr);
    說明:wait系統調用將調用進程掛起,直到該進程收到一個被其捕獲的信號或者它的任何一個子進程暫?;蚪K止為止。如果在wait調用之前已有子進程暫?;蚪K止,則該調用立刻返回。格式wait((int
    *)0) 的功能是等待所有子進程終止。Wait返回時,其返回值為該子進程的進程號。參數statptr的值為該子進程的終止原因:

    1、 如果子進程暫停,statptr目的高八位存放使該子進程暫停的信號值(在第七章中介紹信號),低八位為0177
    2、 如果子進程由于調用exit終止,則該值的低八位為0,高八位為子進程終止時,exit系統調用中參數status值的低八位;
    3、 如果子進程因信號終止,該值的高八位為0,低八位為引起終止的信號值。此外如低七位為1,則表示產生了一個core文件。
    下面我們來看一個例子,該例是一個fork、exec、exit和wait聯合使用的一個實例程序,我們稱之為feew.c:

    /* feew.c */
    main(argc, argv)
    int argc;
    char *argv[];
    {
    int pid, stat;
    if (argc != 1){
    if ((pid = fork()) == 0){
    printf("Child pid = %d\n", getpid());
    execl(argv[1], argv[1], 0);
    exit(5);
    }
    }
    pid = wait(&stat);
    printf("pid=%d, H_stat=%d, L_stat=%d\n", pid, stat>>8, stat&0xff);
    }

    當命令行參數的個數不為1時,程序使用fork系統調用產生一個子進程。子進程通過系統調用getpid獲得自己的進程標識符,然后調用exec執行命令行中用戶提交的命令,如果exec執行失敗,則子進程調用exit(5)終止。父進程使用wait系統調用等待子進程暫?;蚪K止,然后輸出從wait中返回的信息。下面以三種方式執行該程序:
    1〕 不帶命令行參數
    % ./feew
    pid=-1, H_stat=0, L_stat=0
    %
    不產生子進程,從運行結果來看,當無子進程時,wait的返回值為-1。

    2〕 帶命令行參數,參數為合法的可執行命令
    % ./feew /bin/date
    Child pid = 1725
    1998年 2月16日(星期一) 15時59分14秒 CST
    pid=1725, H_stat=0, L_stat=0

    產生子進程。子進程輸出其進程標識符后,再調用exec執行從命令行中提交的命令(/bin/date),同時父進程等待子進程暫?;蚪K止,然后輸出從wait中得到的信息:子進程標識符或狀態參數stat的高八位、低八位的內容。從中可以看到:子進程因調用一個隱含的exit(0)而終止,終止時傳給父進程的值為0。

    3〕 帶命令行參數,但參數不合法
    %./feew /etc/shudown
    Child pid = 1760
    /etc/shutdown: 只有超級用戶(root)能運行 /etc/shutdown。
    pid=1760, H_stat=2, L_stat=0
    %
    子進程創建成功。但由于以普通用戶的身份執行/etc/shutdown,因此exec失敗,爾后調用exit(5)而終止;父進程調用wait得到返回值:子進程號和狀態參數stat的高八位、低八位的內容。從執行結果可以看出:子進程因調用exit(5)而終止,終止時傳給父進程的值為5。

    5.3 進程管理

    進程管理包括的面很廣,諸如進程的用戶標識符管理、進程標識符管理等。進程的用戶標識符有兩個:實際用戶標識符(real user
    id)和有效用戶標識符(effective user id),其對應的組標識符分別稱為實際組標識符(real group
    id)和有效組標識符(effective groud
    id)。一般而言,進程的實際用戶標識符為運行該進程的用戶標識符,通常只用于系統記帳,其他功能由有效用戶標識符來完成,如用有效用戶標識符來完成對新創建文件賦予屬性關系、檢查文件的存取權限和利用kill系統調用向進程發送信號的權限。一般情況下,一進程的有效用戶標識符和實際用戶標識符是相等的,但系統允許改變進程的有效用戶標識符。

    1. 進程的用戶標識符管理
    UNIX系統提供了一組系統調用來管理進程的用戶標識符,它們的使用形式是:
    #include <sys/types.h>
    #include <unistd.h>
    uid_t getuid (void);
    uid_t geteuid (void);
    gid_t getgid (void);
    gid_t getegid (void);
    int setuid(uid_t uid);
    int setgid(gid_t gid);
    說明:前四個系統調用沒有參數,分別返回調用進程的實際用戶標識符、有效用戶標識符、實際用戶組標識符和有效組標識符。這些系統調用的執行總能獲得成功,不會發生任何錯誤。系統調用setuid和setgid用于設置進程的實際用戶(組)標識符和有效用戶(組)標識符,如調用進程的有效用戶標識符是超級用戶標識符,則將調用的進程實際用戶(組)標識符和有效用戶(組)標識符設置為uid或gid;如調用進程的有效用戶標識符不是超級用戶標識符,但其實際用戶(組)標識符等于uid或gid時,則其有效用戶(組)標識符被設置為uid或gid;否則setuid或setgid調用失敗。系統調用setuid或setgid調用成功時返回0,失敗時返回-1。

    2. 進程標識符管理
    UNIX系統使用進程標識符來管理當前系統中的進程。為對具有某類似特性的進程統一管理,系統又引入了進程組的概念,以組標識符來區別進程是否同組。進程的組標識符是從父進程繼承下來的,所以,通常進程的組標識符就是和它相關聯的注冊進程的標識符。進程的標識符是由系統為之分配的,不能被修改;組標識符可通過setpgrp系統調用修改。

    相關系統調用的格式如下:
    #include <sys/types.h>
    #include <unistd.h>
    pid_t getpid(void);
    pid_t getpgrp(void);
    pid_t getppid(void);
    pid_t getpgid(pid_t pid);
    說明:前三個系統調用分別返回調用進程的進程標識符、進程組標識符和其父進程標識符。它們總能成功地返回。第四個調用置進程組標識符,它將調用進程的進程組標識符改為調用進程的進程標識符,使其成為進程組首進程,并返回這一新的進程組標識符。

    下面我們來看一個實例:
    /* setuid.c */
    main(argc, argv)
    int argc;
    char *argv[];
    {
    int ret, uid;
    uid = atoi(argv[1]);
    printf("Before uid=%d, euid=%d\n", getuid(), geteuid());
    ret = setuid(uid);
    printf("After uid=%d, euid=%d\n", getuid(), geteuid());
    printf("ret = %d\n", ret);
    }

    下面分三種情況討論該程序的執行:
    1、 如果執行該程序的用戶為超級用戶,則只要命令行所給的用戶標識符大于0,無論所給的用戶標識符是否存在,執行總能成功。
    #./setuid 3434
    Before uid=0, euid=0
    After uid=3434, euid=3434
    ret = 0

    結果分析:將進程的實際和有效用戶標識符均改為3434。

    2、 如果執行該程序的用戶為一般用戶,用id命令得到用戶uid和gid后,再調用該程序,過程如下:
    %id
    uid=1111(yds) gid=20(user)
    %./setuid 3434
    Before uid=1111, euid=1111
    After uid=1111, euid=1111
    ret = -1
    %./setuid 1111
    Before uid=1111, euid=1111
    After uid=1111, euid=1111
    ret = 0
    %

    結果分析:當命令行參數為1111時,setuid執行成功,因為用戶的uid就是1111。

    值得注意的是:注冊程序login
    是個典型的setuid系統調用程序,login進程的有效用戶是超級用戶,該進程在建立用戶的Shell進程前,調用setuid將實際和有效用戶標識符調整為注冊用戶的用戶實際和有效標識符。

    原文轉自:http://www.kjueaiud.com

    評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>