UNIX 系統概述
1. UNIX體系結構(UNIX Architecture)
調用內核的接口叫做系統調用(system call,圖1.1中的陰影部分),普通函數庫是建立在系統調用接口的基礎之上。應用(application)可以同時使用函數庫或者系統調用。Shell是一種特殊的應用,它為運行其他應用提供接口。總的來說,一個操作系統由內核和所有其他的軟件組成,這些軟件包括系統實用程序,應用,shell,普通的函數庫等等。
2. 登錄(Logging In)
2.1 登錄名
系統在password文件中尋找登錄名,通常為文件/etc/passwd。每個用戶由7部分組成:登錄名:加密密碼:用戶ID:用戶組ID:註釋字段:home文件夾:shell程序
所有現代的系統都將加密密碼移到了另外一個文件中。
2.2 Shell
一個shell是一個命令行解釋器,它讀取用戶輸入並執行命令。用戶輸入通常從終端中讀入,有時候也從文件讀入(叫做shell腳本)。我們在圖1.2中對使用的shell進行總結:
具體執行哪個shell由passwd文件中最後一個字段指定。
3. 文件和文件夾
3.1 文件系統
UNIX文件系統是文件和文件夾的一個分層布置。文件夾也是一個文件,它包含了文件夾入口。我們可以將每個文件夾入口想象成一個文件名和描述文件屬性的結構體的組合。文件的屬性包括文件類型(文件,文件夾),文件大小,文件擁有者,文件的訪問級別(其他用戶是不是可以訪問這個文件),還有文件的最後修改時間
3.2 文件名
只有兩個字符不能出現在文件名中,反斜杠“/”和null字符。因為反斜杠用於分隔路徑中的文件名,null字符用於終止路徑名。
4. 輸入和輸出
4.1 文件描述符
文件描述符是一個非負整數,內核用它來標識進程訪問的文件。
4.2 標準輸入,標準輸出,標準錯誤
按照慣例,當一個新的程序運行時,所有的shell都會打開三個描述符:標準輸入,標準輸出和標準錯誤。如果沒有任何特殊操作,這三個描述符都會被連接到終端。大多數shell都提供了將三個描述符重定向到文件的功能。
4.3 無緩沖I/O
無緩沖I/O由函數open,read,write,lseek和close提供。這些函數同文件描述符一塊工作。
1 #include "apue.h" 2 #define BUFFSIZE 4096 3 int 4 main(void) 5 { 6 int n; 7 char buf[BUFFSIZE]; 8 while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) 9 if (write(STDOUT_FILENO, buf, n) != n) 10 err_sys("write error"); 11 if (n < 0) 12 err_sys("read error"); 13 exit(0); 14 }
4.4 標準I/O
標準I/O函數為不帶緩沖的I/O函數提供了一個帶緩沖的接口。使用標準I/O函數無需擔心如何選取最佳的緩沖區大小。
我們最熟悉的標準I/O函數是printf。
用標準I/O將標準輸入復制到標準輸出:
1 #include "apue.h" 2 int 3 main(void) 4 { 5 int c; 6 while ((c = getc(stdin)) != EOF) 7 if (putc(c, stdout) == EOF) 8 err_sys("output error"); 9 if (ferror(stdin)) 10 err_sys("input error"); 11 exit(0); 12 }
5. 程序和進程
5.1 程序
程序是一個存儲在磁盤上某個目錄中的可執行文件。內核使用exec函數,將程序讀入內存,並執行程序。
5.2 進程和進程ID
程序的執行實例被成為進程(process)。UNIX系統確保每個進程都有一個唯一的數字標識符,稱為進程ID。進程ID為非負整數。
5.3 進程控制
有三個用於進程控制的主要函數:fork,exec和waitpid(exec有七種變體,但經常把它們統稱為exec函數)
5.4 線程和線程ID
多個控制線程可以使得某些問題解決起來更加容易,並能充分利用多處理器系統的並行行為。
一個進程內的所有線程共享同一地址空間、文件描述符、棧以及進程相關的屬性。因為他們要訪問同一存儲區,因此各線程在訪問共享數據時需要采取同步錯誤以避免不一致性。
線程也用ID標識,一個進程中的線程ID在另外一個進程中沒有意義。
6. 出錯處理
當UNIX系統函數出錯時,通常會返回一個負值,而且整型變量errno通常被設置為具有特定信息的值。有些函數對於出錯使用另外一種約定而不是返回負值。例如大多數返回對象指針的函數在出錯時會返回一個null指針。
Linux支持多線程存取errno:
1 extern int *_ _errno_location(void); 2 #define errno (*_ _errno_location())
對於errno應當註意兩條規則。第一條是:如果沒有出錯,其值不會被例程清除。第二條是:任何函數都不會將errno值設置為0.
6.1 出錯恢復
可將在<errno.h>中定義的各種出錯分成兩類:致命性的和非致命性的。致命性的錯誤無法恢復。只能打印日誌。對於非致命性的錯誤,有時可以較為妥善的進行處理。
7. 用戶標識
7.1 用戶ID
口令文件登錄項中的用戶ID是一個數值,它向系統標識各個不同的用戶。
用戶ID為0的用戶為根用戶(root)或超級用戶(superuser)。在口令文件中,通常有一個登錄項,其登錄名為root,我們稱這種用戶的特權為超級用戶特權。超級用戶對系統具有自由的支配權。
7.2 組ID
口令文件登錄項也包括用戶的組ID,它是一個數值。在口令文件中有多個登錄項具有相同的組ID。組被用於將若幹用戶集合到項目或部門中去。這種機制允許同組的各個成員之間共享資源(如文件)。
組文件通常是/etc/group。
8. 信號
信號用於通知進程發生了某種情況。進程有三種處理信號的方式。
- 忽略信號。
- 按系統默認方式處理。對於除數為0,系統默認方式是終止該進程。
- 提供一個函數,信號發生時調用該函數,這被稱為捕捉該信號。
9. 時間值
歷史上UNIX系統使用過兩種不同的時間值。
- 日歷時間。這個值是UTC時間(1970年1月1日 00:00:00)這個特定時間以來所經過的描述累計值。系統基本數據類型time_t用來保存這種時間值。
- 進程時間。也被稱為CPU時間,用以度量進程使用的中央處理器資源。進程時間以始終滴答計算。系統基本數據類型clock_t保存這種時間值。
當度量一個進程的執行時間時,UNIX系統為一個進程維護了三個進程時間值:
- 時鐘時間,它是進程運行的時間總量,其值與系統中同時運行的進程數有關。
- 用戶CPU時間;它是執行用戶指令所用的時間量。
- 系統CPU時間。它是該進程執行內核程序所經歷的時間。
可以用time命令來獲取上述三個時間值。
10. 系統調用和庫函數
所有操作系統都提供多種服務的入口點,由此程序向內核請求服務。各版本的UNIX實現都提供良好定義、數量有限、直接進入內核的入口點,這些入口點被稱作系統調用。
庫函數可能會調用一個或者多個內核的系統調用,但是它們並不是內核的入口點。
應用程序既可以調用庫函數也可以調用內核函數。
UNIX 系統概述