當命令行參數的個數不為1時,程序使用fork系統調用產生一個子進程。子進程通過系統調用getpid獲得自己的進程標識符,然後調用exec執行命令行中用戶提交的命令,如果exec執行失敗,則子進程調用exit(5)終止。父進程使用wait系統調用等待子進程暫停或終止,然後輸出從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),同時父進程等待子進程暫停或終止,然後輸出從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將實際和有效用戶標識符調整為注冊用戶的用戶實際和有效標識符。
第六章 設備輸入/輸出控制 6.1 概述 UNIX將設備看成文件,這是UNIX的一大特色。這裡需要介紹一個設備號的概念。設備特別文件與兩個設備號有關-主設備號和次設備號。主設備號告訴操作系統,當涉及文件名時,將使用哪種設備類型。對於每一種類型的設備都有一段駐留在操作系統中的程序代碼,以控制相應類型的設備,這段代碼被稱為"設備驅動程序"。次設備號被傳遞給設備驅動程序,這個號碼用來決定使用哪種物理設備。例如,決定在一塊多重驅動控制卡上,哪個磁盤驅動器將被訪問,以及該磁盤驅動器中哪一部分將被使用;或者,當一個磁盤驅動器所請求的操作已經完成後,應該被恢復原狀。幾個設備(如同類型的磁盤驅動器)可以用同一個主設備號,但它們將有不同的次設備號。看下面的例子: %ls -l /dev/ttyq* crw--w---- 2 yds user 15, 1 2月 17日 09時03分 ttyq1 crw--w---- 2 yds user 15, 14 2月 16日 17時00分 ttyq14 % 上例中15是主設備號,1和14是次設備號。 用戶可以使用系統提供的統一而且獨立於設備的界面-對文件進行操作的系統調用來操作設備,而沒有必要涉及設備的具體細節。大部分對文件進行操作的系統調用對它們仍起作用,例如,用open打開設備,用read/write對設備進行讀/寫,設備操作完成後,用close關閉設備。但有的系統調用在對設備文件進行操作時,其功效有所不同。如create及open的創建方式都不能創建設備文件。 6.2 設備輸入/輸出控制-ioctl系統調用 ioctl是UNIX系統專門提供的用於設備控制的系統調用。該系統調用與設備類型(即主設備號)相關。不同的設備,系統提供了不同的控制命令。 ioctl的調用格式是: ioctl(int fd, int cmd,arg…) 說明:參數fd是一設備文件的文件描述字,cmd是控制命令,它與設備相關,不同類型的設備有不同的控制命令。參數arg沒有固定的數據結構,它隨cmd的不同而不同。
第七章 高級編程 7.1 處理信號 信號是UNIX進程間最基本的通訊手段,主要作用是實現進程間異步事件的通訊。信號是傳送到進程的"軟中斷",它通知進程在它們的環境中出現了非正常事件。進程接收到信號後要進行處理,處理方式為以下四種之一: (1)缺省方式(SIG_DFL):這是進程對信號的一般處理方式,在無特殊情況下,進程在接收到信號後將終止執行。有一些信號,在終止進程運行前需將終止進程的正文段、數據段、user結構和棧段內容寫到當前目錄的core文件中,以備調試工具分析與使用。 (2)忽略方式(SIG_IGN):進程接收到一個已指明忽略的信號,則將該信號清除後,立即返回,不在任何工作。信號SIGKILL不能被忽略。 (3)保持方式(SIG_HOLD):當進程處於該方式時,將接收的信號保存起來,等該進程的保持方式解除後,再進行處理。 (4)捕獲方式(設置信號處理函數):這是用戶設置的信號處理方式,當進程接收到這種信號時