Chapter 1. UNIX System
Overview UNIX
結構: 操作系統(tǒng)是控制硬件資源和提供程序運行環(huán)境的一種軟件。 系統(tǒng)調(diào)用是訪問內(nèi)核的接口。 Shell是特殊的應用程序,提供運行其他程序的接口。 登入: 用戶名,/etc/passwd, shell 文件和目錄: 文件系統(tǒng),文件名,路徑名,工作目錄,Home目錄 輸入和輸出: 文件描述符,標準輸入、輸出、錯誤,unbuffered I/O,標準I/O 程序和進程: 程序是磁盤上的可執(zhí)行文件,讀入到內(nèi)存中并由內(nèi)核執(zhí)行。 進程,進程ID,進程控制(fork, exec, waitpid),線程和線程id(id只在本進程內(nèi)有效) 錯誤處理: 當函數(shù)執(zhí)行錯誤發(fā)生時,將返回一個負值,并設置errno為一個整數(shù)以提供額外的信息。strerror和perror打印錯誤信息。 錯誤定義可以分為兩類:致命和非致命的錯誤。非致命錯誤一般是暫時的,可以恢復,如缺少資源,就可以延遲一段時間再重試。 用戶身份: 用戶id,組id,補充組id(一個用戶可以屬于多個組中) 信號: 用來通知進程有情況發(fā)生的一種技術。進程收到信號后有三種選擇1,忽略
2,默認處理方法 3,自己提供一個函數(shù)當信號發(fā)生時調(diào)用。 時間值: 有兩種:1,日歷時間time_t 2,進程時間clock_t( clock time, user
cpu time, system cpu time) 日歷時間可用來記錄文件最后修改的時間等。 Clock
time是此進程開始執(zhí)行到終止的時間,包括了進程的切換,以及其他進程運行的時間片。 系統(tǒng)調(diào)用和庫: 系統(tǒng)調(diào)用是在內(nèi)核空間中運行。庫封裝了系統(tǒng)調(diào)用,運行在用戶空間。 Chapter 2. UNIX
Standardization and Implementation UNIX標準化: ISO
C, IEEE POSIX,Single UNIX
Specification(POSIX的超集) UNIX系統(tǒng)的實現(xiàn): System
V (AT&T) BSD (Berkeley) Linux Mac
OS(core is Darwin,
combination of Mach kernel and FreeBSD) Solaris
(Sun, based on System V) 限制: 由于不同系統(tǒng)的實現(xiàn)定義了很多magic numbers和常量。為了增強可移植性,需要兩類的限制1,編譯時的限制2,運行時的限制。處理方法分為三種:1,編譯時的限制(headers) 2,運行時和文件、目錄無關的限制(sysconf函數(shù))3,運行時和文件、目錄相關的限制(pathconf和fpathconf函數(shù)) 選項: 同樣為了不同系統(tǒng)的代碼可移植性. 1. Compile-time options are
defined in <unistd.h>. 2. Runtime options that are
not associated with a file or a directory are identified with the sysconf function. 3. Runtime options that are
associated with a file or a directory are discovered by calling either the pathconf or the fpathconf function. 特性測試宏: 測試系統(tǒng)是否支持某種特性的宏定義,如__STDC__ 原語系統(tǒng)數(shù)據(jù)類型: <sys/types.h>中定義的數(shù)據(jù)類型。如time_t, off_t, pid_t等。 標準間的沖突: 為了兼容性,很多POSIX的系統(tǒng)實現(xiàn)了ISO C的函數(shù)。 Chapter 3. File I/O 文件描述符: 打開文件、創(chuàng)建文件時返回的值,用來讀、寫、定位文件的標識。 open,creat,close函數(shù): 好像沒什么好說的。要注意open時一些特殊的文件狀態(tài)標記,如O_SYNC,O_NOCTTY等。 lseek函數(shù): 定位文件,可以產(chǎn)生空洞文件。定位可以從頭,從尾,從當前位置開始。 Read,write函數(shù): 沒啥好說的。I/O效率根據(jù)自己設定的buffer大小而不同。因此,調(diào)用read,write的次數(shù)不同,耗去的系統(tǒng)cpu時間不同。測試時,第一次和之后的測試效率是不同的,因為cache的存在。 文件共享: 文件共享是指不同進程間打開文件的共享。內(nèi)核通過三種數(shù)據(jù)結構來表示打開的文件,1,每個進程有個進程表項,表中是打開文件的描述符向量。每個向量包含了文件描述符標記和一個指向文件表項的指針;2,內(nèi)核為所有打開的文件維護一個文件表,每個文件表項包括了a,文件狀態(tài)標記,如讀、寫、添加、非阻塞等b,當前的文件偏移量c,指向文件v-node表項的指針;3,每個打開文件或設備都有一個v-node結構 ,包含文件類型和指向操作文件的函數(shù)的指針。 當兩個以上獨立進程打開同一個文件實現(xiàn)文件共享時,內(nèi)核維護不同的文件表項,就是兩個以上進程表項中的文件表項指針指向不同的文件表項,而不同的文件表項中的v-node指針指向同一個v-node而實現(xiàn)文件共享。由于每個進程有自己的打開文件表項,所以有自己的文件打開狀態(tài)以及文件偏移量。 原子操作: 為了解決多個進程間共享文件時讀寫不一致的問題。如多個進程對同一個文件添加內(nèi)容,如多個進程創(chuàng)建同一個文件,如lseek后的write,當多個進程同時執(zhí)行時就會使文件寫的內(nèi)容不是預期的。 函數(shù)pread,pwrite是把lseek和read/write作為原子操作來實現(xiàn),避免上述問題。 Dup和dup2函數(shù): 復制描述符,dup是將文件描述符復制到最小可用的描述符,讓最小可用的描述符指向原描述符所指向的文件表項。 dup2是將指定的文件描述符指向原描述符所指向的文件表項。如果指定的描述符已打開則先關閉。 Sync,fsync,fdatasync函數(shù): 延遲寫是將排隊在buffer或page
cache中的數(shù)據(jù)過一些時間再寫到磁盤中。當再次用到這些cache時再寫到磁盤中。 提供這些函數(shù)是將數(shù)據(jù)立即寫到磁盤中,保持文件系統(tǒng)的一致性。Sync和fsync的區(qū)別是,前者不等待數(shù)據(jù)寫到磁盤完成即返回,而后者等待完成后才返回。前者是寫入cache中的所有數(shù)據(jù),系統(tǒng)中的update是daemon進程,它調(diào)用sync,并30s中刷新一次。而后者是指定單個文件刷新數(shù)據(jù),多用于數(shù)據(jù)庫的更新。Fdatasync和fsync類似,但是它只刷新數(shù)據(jù)部分,而不更新文件的屬性。 fcntl函數(shù): 作用是能夠改變打開文件的屬性。根據(jù)參數(shù)不同主要有五種用途: 1, 復制已存在的描述符 2, 獲得和設置文件描述符標記 3, 獲得和設置文件狀態(tài)標記 4, 獲得和設置異步I/O的所屬者 5, 獲得和設置記錄鎖 注意的是:我們在設置文件描述標記或文件狀態(tài)標記時,不能只是F_SETFD 或 F_SETFL的fcntl的調(diào)用,而是要先獲得值再設置值。 文件狀態(tài)標記O_SYNC,表示同步寫,每個write要等待數(shù)據(jù)寫到磁盤上才返回。而在通常情況下write只是將要寫的數(shù)據(jù)排隊,某個時刻后才寫到磁盤上。 還提到fcntl這樣的用處,fcntl可以設置shell打開的標準輸出輸入的屬性,因為它只要知道文件描述符就能做到文件屬性的改變。 Iocntl函數(shù): 主要用于終端的I/O和提供本章中提到的函數(shù)無法實現(xiàn)的功能。如對磁帶的讀寫和定位的操作。 /dev/fd: 打開/dev/fd/n文件就等于是對文件描述符dup(n),假定n是已打開的描述符。 即open(”/dev/fd/n”,
mode) ==
dup(n) Chapter 4. Files and
Directories stat,
fstat, lstat函數(shù): 獲得磁盤上文件的結構信息,由結構stat描述。這些信息應該是從文件系統(tǒng)中的inode結構讀出的。包括:文件類型、訪問權限、大小、所屬用戶id等等信息。 文件類型: 普通文件,目錄文件,符號鏈接,字符設備、塊設備、socket、FIFO。 Set-User-ID和Set-Group-ID: 每個進程有六個和他相關聯(lián)的id,真實用戶id、有效用戶id、保存設置用戶id以及三個對應的組id。注意的是這是與進程相關的,以前總是弄混。 真是用戶id是用戶登錄時使用的id。 一般,有效用戶id=真是用戶id。例外是在文件模式字st_mode中置位一個特殊標記set-user-id,那么有效用戶id就是此進程所執(zhí)行的文件所屬者的用戶id。有用的是,判斷一個文件是否可以被進程訪問,是根據(jù)進程的有效用戶id是否等于文件所屬的用戶id來的。舉例:普通用戶用程序passwd來修改密碼時,會訪問到root用戶的文件/etc/passwd和/etc/shadow。為什么能成功,是因為passwd是set-user-id程序并且所屬是root,當普通用戶執(zhí)行時,它的有效用戶id就變成root,就可以訪問上面兩個文件。 新建文件和目錄的所屬者: 新文件的用戶id是進程的有效用戶id。 而新文件的組id有兩種說法 1.進程的有效組id;2.
文件所在的目錄的組id。具體實現(xiàn),每個系統(tǒng)有所不同。 access函數(shù): 它的用處是檢測真實用戶id的訪問文件的權限,而不管它是否是set-user-id程序。 umask函數(shù): 創(chuàng)建文件時的屏蔽位,設置創(chuàng)建的文件的訪問權限。shell中也有umask命令。 chmod和fchmod函數(shù): 改變已有文件的訪問權限。 Sticky
bit: 作用是在可執(zhí)行程序在第一次執(zhí)行時,程序的代碼段text將復制到swap空間中。在下次執(zhí)行時,此程序load到內(nèi)存中就很快。如今的unix系統(tǒng),虛擬內(nèi)存以及快速文件系統(tǒng)的出現(xiàn),這項技術并不需要了。 chown,
fchown, and lchown
Functions: 改變文件的所屬者。大多數(shù)系統(tǒng)中,只有root能改變文件的所屬者。Whether the owner can be changed depends
on the different systems. 文件大?。?/span> stat結構中的st_size包含文件的字節(jié)大小。但是只對普通、目錄、符號鏈接文件有意義。 對于普通文件,大小為0是允許的。第一次讀就得到end-of-file。 對于目錄文件,大小通常是16或512的倍數(shù)。 對于符號鏈接文件,大小是被鏈接的文件路徑名長度。此外,它不包含C語言中字符串的終止空字符。 stat結構中的st_blksize為文件I/O時所使用的塊大小。 stat結構中的st_blocks為文件所占的實際磁盤塊數(shù)。 文件中的洞,當文件中有洞時,通過ls –l core顯示時,會包含洞所占的字節(jié)數(shù)。而用du
–s core則顯示文件實際所占的磁盤塊數(shù)。當用read讀文件中洞的內(nèi)容時,得到的是0(不是‘ 文件截斷: truncation,ftruncation函數(shù)。例外:當截斷的長度大于原有文件的長度時,效果依賴于具體的系統(tǒng)。符和XSI的系統(tǒng)文件變長,而變長的部分read出內(nèi)容是0. 文件系統(tǒng): 磁盤可以有多個分區(qū),每個分區(qū)可以是不同的文件系統(tǒng)。文件系統(tǒng)主要包含部分:boot block,super
block,cylinder
group0~n。 每個cylinder group中包含:super block copy,i-node
map,block
bitmap,
i-nodes(一個文件/目錄一個), data/directory blocks(存放文件/目錄的數(shù)據(jù))。 對于普通文件,i-node指向了所屬文件的數(shù)據(jù)塊。而目錄文件,i-node則指向了所屬目錄的directory
blocks。 每個directory block中的主要內(nèi)容是目錄項所包含的文件的i-node號以及文件名。當目錄項包含這樣的信息時,被包含的文件的i-node中stat的成員st_nlink即鏈接數(shù)就增1。這種鏈接就是硬鏈接,通過ln命令實現(xiàn),即創(chuàng)建一個新目錄項指向已有的文件。而只有當鏈接數(shù)為0的時候,文件才被刪除。 修改文件名如mv命令,是添加一個新目錄項指向已有文件,再unlink掉原目錄項,而不需要移動文件的實際內(nèi)容。 葉目錄(目錄中不包含任何其他目錄)的鏈接數(shù)是2,分別是目錄中dot,以及父目錄對它的指向。在葉目錄增加一個子目錄,則葉目錄的鏈接數(shù)就增1,因為新增子目錄的dotdot指向它。 link,unlink,remove,rename函數(shù): 通過link創(chuàng)建一個新的目錄項指向一個文件,并使鏈接數(shù)增1。這樣多個目錄項就可以指向同一個文件。大多數(shù)的系統(tǒng)不允許對目錄硬鏈接,因為會產(chǎn)生文件系統(tǒng)中的loop。 unlink刪除目錄項,鏈接數(shù)減1.參數(shù)是符號鏈接的話,將移除本身而不是所鏈接的文件。超級用戶能用unlink去刪除目錄,同rmdir。 remove對文件來說相當于unlink,對目錄來說是rmdir。 rename的處理情況較多復雜些。暫時不去關注,用到再說。 符號鏈接: 硬鏈接是直接指向文件的i-node,通過對i-node的鏈接增加計數(shù)來實現(xiàn)的。使用限制有兩點1,鏈接和文件必須在同一個文件系統(tǒng)中,因為硬鏈接是與i-node號相關的,不同的文件系統(tǒng)中不同文件可能有相同的i-node號。2,只有超級用戶才能創(chuàng)建目錄的硬鏈接。 符號鏈接則是間接指向一個文件,通過創(chuàng)建新的i-node,i-node指向data block,data
block內(nèi)容是文件的路徑名。符號鏈接通常是用來將一個文件或整個目錄層次移動到系統(tǒng)的另一個位置。 注意的是,當用到文件名作參數(shù)的函數(shù)時,函數(shù)是否follow符號鏈接。 syslink和readlink函數(shù): 創(chuàng)建符號鏈接syslink。 open函數(shù)會follow符號鏈接,如果要打開符號鏈接并讀其中的內(nèi)容,則要用readlink。 文件的時間: 有三種,1,最后訪問時間 2,最后修改時間 3,最后i-node狀態(tài)改變時間。 utime函數(shù)可以修改1,2的時間,而3的時間調(diào)用utime時自動更新。 mkdir和rmdir函數(shù) mkdir創(chuàng)建空的目錄,并自動創(chuàng)建dot和dotdot目錄項。注意創(chuàng)建時mode的權限除了讀寫外,還要有執(zhí)行,這不同于文件。新目錄的用戶id和組id需要討論。 rmdir則是刪除空目錄。 讀目錄: 有訪問權限就可讀目錄,而只有內(nèi)核才可以寫目錄。 Chdir,
fchdir,getcwd函數(shù) 改變當前的工作目錄chdir,fchdir。當前工作目錄是進程的一個屬性。它并不影響調(diào)用進程的當前工作目錄。 Getcwd得到當前工作目錄。 Chapter 5. Standard I/O
Library Streams
and FILE Objects: 就是通過標準庫如fopen打開的返回值指針FILE,使Stream關聯(lián)到打開的文件。 fwide函數(shù)的作用是改變Stream的模式,是wide-oriented還是byte-oriented。Wide是為了支持國際化字符集,寬字符。 標準輸出、輸入、錯誤: 對應文件描述符的有相應的流, stdin, stdout, stderr。在stdio.h中有定義。 Buffering: 目的是為了最小化系統(tǒng)調(diào)用read、write的次數(shù)。 分為完全緩沖、行緩沖、無緩沖三種。 完全緩沖:行緩沖的流如果沒有連接到終端設備上,則是完全緩沖。Stdin和stdout如果被重定位到文件上,則是完全緩沖。 行緩沖:stdin,stdout 無緩沖:stderr 打開流: Fopen,freopen,fdopen。 讀寫流: 一字節(jié)一次I/O: getc, fgetc, getchar.
Putc,fputc, putchar.其中,getc,putc一般由宏來實現(xiàn)。 一行一次I/O:gets,fgets,puts,fputs,最好不要使用gets,puts,不安全。造成緩沖區(qū)溢出。 直接I/O(二進制I/O):fread,fwrite,結構體的讀寫。以上的幾個讀寫函數(shù)均不適用,如遇到空字節(jié)在結構體中時,上面函數(shù)的讀寫就無法完成。存在問題,當編譯器或系統(tǒng)的不同,結構體字節(jié)對齊會造成讀寫不正確;機器體系結構的不同,二進制格式對多字節(jié)的整數(shù)或浮點數(shù)的存儲也會不同。 標準I/O的效率: 驗證的結果是,用戶不用去設定在系統(tǒng)調(diào)用read、write時所用的緩沖區(qū)大小,在標準I/O中內(nèi)部已經(jīng)自動設定了這樣最佳的buffer,使系統(tǒng)CPU的時間使用最少。而fgets和fputs中用戶設定的line buffer的大小只會影響用戶CPU的時間。因此,數(shù)據(jù)的copy有兩次:一次在內(nèi)核和標準I/O buffer之間(系統(tǒng)調(diào)用read,write);另一次在標準I/O buffer和我們的行buffer之間。
流的定位: ftell,fseek,rewind fgetpos,fsetpos(可移植的) 格式化I/O: 用得最多的是printf,scanf, sprintf,
snprintf, 具體的參數(shù)標志細節(jié),用到時再查。 臨時文件: 由兩個函數(shù)生成,tmpnam, tmpfile. 目前作用貌似不大。 Chapter 6. System Data
Files and Information Password
File: /etc/passwd文件中包含了各個用戶的如用戶名、密碼、用戶id、組id等信息。并通過passwd結構來描述。 通過如getpwuid、getpwnam、getpwent函數(shù)來獲得passwd結構。 Shadow
Passwords: 出于安全考慮,用戶登錄密碼在/etc/shadow文件中加密。由spwd結構描述。 加密是one-way加密算法,意思是你不能通過加密后的密碼得出原密碼,而只能通過原密碼去驗證是否正確。 提供了相似的訪問函數(shù)。但是shadow中的用戶加密密碼是不可讀出的。 Group
File,Supplementary Group ID: /etc/group文件描述組的信息,并由group結構描述,包括組名、加密密碼、組id、所屬組 的用戶名數(shù)組。 Other
Data Files: /etc/services; /etc/protoclos; /etc/networks; Login
Accounting: 有兩個相關的文件:utmp,記錄當前所有登錄的用戶。wtmp,記錄所有的登錄和退出。 System
Identification: 主機號、操作系統(tǒng)名、版本號等信息。uname,gethostname來獲取。 Time
and Date Routines: 提供一系列的時間的相關函數(shù),具有不同的表示方式。有個不同時間函數(shù)之間的關系圖可作 參考。 Chapter 7. Process
Environment main函數(shù): 在執(zhí)行之前,內(nèi)核通過start-up routine(通常是由匯編編寫的)來執(zhí)行main; 進程終止: 多種exit的終止:exit(會關閉打開的流), _exit, Exit,還有線程的pthread_exit。 atexit注冊終止時調(diào)用的函數(shù),按照注冊的順序,反序依次調(diào)用。 shell命令:echo
$? 查看程序的返回狀態(tài)。 命令行參數(shù): 沒什么好說的 環(huán)境列表: 很少用到,主要是程序運行時的環(huán)境變量,如:HOME,SHELL,PATH,USER,LOGNAME等。 C程序的內(nèi)存布局: Text,
Data, BSS, Stack, Heap. Text是程序的代碼段。 Data是程序中初始化了的全局、靜態(tài)數(shù)據(jù)變量。 BSS是程序中未初始化的全局、靜態(tài)數(shù)據(jù)變量。即使全局、靜態(tài)數(shù)據(jù)變量初始化為0仍然是屬于BSS段。 Stack是程序中的局部變量,由高地址到低地址向下增長。 Heap是malloc調(diào)用動態(tài)分配的內(nèi)存,由低地址到高地址向上增長。 注意:定義的字符串常量,其中變量算Data,而字符串的大小是屬于Text的。 共享庫: 好處是1.共享,減少可執(zhí)行文件的大小 2.庫更新時,使用庫的每個程序都不用重新鏈接。 編譯時用$ cc -static hello1.c 就取消了使用共享庫,編譯出的程序很大。 內(nèi)存分配: malloc,
calloc, realloc 環(huán)境變量: 提供讀取和設置環(huán)境變量的接口,getenv,setenv。。用處大概就是通過程序來設置和修改環(huán)境變量。 setjmp和longjmp: 用處是處理有深度嵌套調(diào)用的不同函數(shù)間的錯誤情況。用goto只能是本函數(shù)內(nèi)的局部跳轉,這兩個函數(shù)就可以在函數(shù)間進行跳轉。 有問題是跳轉會影響不同類型變量的狀態(tài),對于沒有優(yōu)化編譯的程序,即使是register變量也是放在內(nèi)存中,因此所有類型的變量仍然得到內(nèi)存中的值。但是優(yōu)化-O編譯的程序,由于自動變量、register變量是在寄存器中,當執(zhí)行setjmp時,會從寄存器中讀取數(shù)值,setjmp之后的重新賦值將不起作用。而全局、靜態(tài)、以及volatile變量仍在內(nèi)存中,值不會受到影響。因此,當寫可移植性非局部跳轉代碼時,應使用volatile屬性。 getrlimit和setrlimit函數(shù): 每個進程都有資源限制的一個集合。如進程可用的總內(nèi)存的大小,core文件的大小,創(chuàng)建文件的最大字節(jié)數(shù)等等。 包括軟限制和硬限制??梢栽O置軟限制小于等于硬限制。可以降低硬限制大于等于軟限制但不可逆。只有超級用戶進程才能提升硬限制。 資源限制影響調(diào)用的進程,并由子進程繼承。一般內(nèi)建到shell中設置資源限制。 Chapter 8. Process Control 進程標識符: 進程id是唯一的但是可以被重用。 進程id=0是調(diào)度進程,即swapper。此進程在磁盤上沒有對應的程序,是內(nèi)核的一部分,系統(tǒng)進程。 進程id=1是init進程,內(nèi)核在啟動過程結束后調(diào)用它,對應磁盤程序/etc/init,或/sbin/init.它是普通的用戶進程,雖然它以超級用戶權限運行。 Fork函數(shù): 子進程和父進程共享代碼段,但子進程有父進程的數(shù)據(jù)段、堆和棧的副本,但具體實現(xiàn)并不是完全的副本。 (sizeof 和
strlen,前者是在編譯期間就計算了常量字符串的長度,它包括了字符串終止符,而strlen是在運行時計算字符串長度,并且不包括字符串終止符) Fork函數(shù)生成的子進程改變變量時并不影響父進程中的變量值。 對于I/O來說,如write輸出字符串到標準輸出上,由于是無緩沖的I/O,直接將字符串輸出到標準輸出上。 對于有緩沖的I/O,如printf的標準輸出是連接到終端的,即此時是行緩沖,當輸出的字符串沒有換行符時,由于行緩沖沒有被fflush掉,此時會從父進程中copy一份行緩沖到子進程中,輸出的時候就會有兩份。當字符串中有換行符,換行符會fflush掉緩沖,同無緩沖的I/O,只有父進程中的一份,直接將字符串輸出到標準輸出上。 當標準輸出被重定向到文件上時,緩沖變?yōu)槿彌_,由于緩沖一直存在,子進程就copy了父進程的緩沖,因此也輸出兩份。 Fork函數(shù)使父進程中所有打開的文件描述符在子進程中都有一個副本。他們共享父進程打開的所有文件的文件表項(File Table Entry) Vfork函數(shù): 與fork相似,但是語義不同,主要是用來exec一個新程序。它保證子進程先運行,直到調(diào)用exec或exit時,父進程才開始執(zhí)行。 此外,不同于fork,vfork使父子進程共享虛擬內(nèi)存空間,子進程中修改的變量會影響父進程中的變量。在子進程中調(diào)用exec或exit之前,他在父進程的空間中運行。 Exit函數(shù): 正常退出和非正常退出. 正常退出包括,exit,_exit,_Exit, return,.pthread_exit;一般會通知父進程它是怎么退出的以及退出狀態(tài)。 非正常退出包括, abort, 接受到某種信號,cancellation request(不懂);內(nèi)核會產(chǎn)生非正常退出的原因。 僵尸進程是一個進程終止了而它的父進程沒有等到它并且沒有獲得它的退出狀態(tài)。當一個進程的父進程終止了,它的父進程將由init進程來繼承,其pid是1. |
|