UNIX环境高级编程学习总结
生活随笔
收集整理的這篇文章主要介紹了
UNIX环境高级编程学习总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這些日子學習了一下APUE(Advanced Programming in the UNIX,UNIX環境高級編程)。這是一本公認的好書,它詳細的講解200多個函數、提出并解決各種可能存在的問題,這絕對是UNIX程序員居家旅行必備寶典。對于初學者來說它講得太細太繁雜了。學習完本書后,總體的對它有了點認識,并懂得了使用里面常用的函數。下面是簡單的對本書重要的章節給個大體的介紹。
本書印象
本書覆蓋了UNIX上數百個系統調用及函數,但是實際上大多數的內容集中在I/O和進程兩大方面上。這兩方面的內容大概也是用得最廣的吧。I/O方面的內容包括了不帶緩存的I/O、標準I/O庫、終端I/O、高級I/O。進程方面的內容包括UNIX進程的環境、進程控制、信號、進程間通信、守護進程。
終端I/O、數據庫函數庫、打印機通信、調制解調器、偽終端這些章節感覺不重要,所以沒看。進程關系這一章好像也不重要,只有編寫精靈進程會涉及到該章的部分知識。其他章節應該都是很重要。
原子操作
只要涉及到多個進程共享資源,原子操作的概念就變成非常重要。
原子操作(atomic operation)指的是由多步組成的操作。如果該操作原子地執行,則或者執行完所有步,或者一步也不執行,不可能只執行所有步的一個子集。
競態條件(race condition):當多個進程都企圖對共享數據進行某種處理,而最后結果又取決于進程運行的順序時,則我們認為發生了競態條件。
與之相關的有一個概念,叫時間窗口,它就是競態條件下發生的。時間窗口大概就是說進程某一個操作與其下一個操作本來應該是連續執行,但是因為進程的切換,導致操作的不連續,導致共享資源被修改,導致程序運行偏離預期。書中10.4節介紹了早期的信號及一些經典的處理案例,這些案例都是由于時間窗口的存在而暗藏隱患。
出錯處理
UNIX函數出錯,通常返回一負值,并把全局變量errno設置為具有特定信息的值。
<errno.h>定義了errno以及可以賦予它的各種常數。對于errno應當知道兩條規則:
1. 如果沒有出錯,其值不會被一個例程清除。因此,僅當函數的返回值指明出錯時,才檢驗其值。
2. 任一函數都不會將errno值設為0,<errno.h>中定義的所有常數都不為0。
由于每個進程只有一個errno變量,一個通用的規則是,當在信號處理程序中調用庫函數或系統調用時,應當在其前保存errno,然后在其后恢復。
I/O效率
1. 對于不帶緩存的I/O,當緩存長度等于文件系統的塊長時,讀操作的時間是最小;緩存長度小于塊長,讀操作時間增多;大于塊長,也不會提高讀操作的效率。
2. 標準I/O可以使用戶不必像文件I/O那樣擔心如何選擇正確的緩存長度。標準I/O并不比文件I/O慢很多。5.8講標準I/O的效率。
3. 比起多次read和write,readv和writev更好。12.7講readv和writev。
4. mmap/memcpy方式比read/write方式效率高,見12.9。
標準I/O
標準I/O可以使用戶不必像文件I/O那樣考慮緩存及最佳I/O長度的選擇。它提供緩存,目的是盡可能少的減少使用read和write的數量。它有三種類型的緩存:全緩存、行緩存和不帶緩存。但是標準I/O緩存也是產生很多問題,引起很多混淆的一個領域。
中文版的P142講了一個很有意思的例子,由于fork的實現方式和IO緩存導致在終端和文件中輸出結果的不同。
高級I/O
非阻塞I/O是調用不會永遠阻塞的I/O操作,如果操作不能完成,則立即出錯返回。
記錄鎖(record locking)的功能:一個進程正在讀或修改文件的某個部分時,可以阻塞其他進程修改同一文件區。
程序12-5講了一個有趣的例子,讓精靈進程阻止其多份副本同時運行,原理是這樣的:精靈進程把它們的進程ID寫道一個各自專有的PID文件上,通過對這個文件加寫鎖來保證只有一個副本在運行。
I/O多路轉接: select 和 poll,網絡編程用得多。
readv和writev函數用于在一個函數調用中讀、寫多個非連續緩存。
readn和writen讀、寫指定的N字節數據,并處理返回值小于要求值得情況。
存儲映射I/O mmap使一個磁盤文件與存儲空間中的一個緩存相映射。當從緩存中取數據,就相當于讀文件中的相應字節,類似的,將數據存入緩存,則相應字節就自動地寫入文件。
stat函數
第4章詳細介紹了stat結構中的每一個成員。包括3個函數,stat、fstat、lstat函數。這使得我們對UNIX文件的各個屬性都有所了解。對文件的所有屬性以及對文件進行操作的所有函數都有完整的了解對各種UNIX程序設計都非常重要。
非局部轉移
setjmp和longjmp函數這兩個函數用于執行跨越函數的跳轉功能。
用于信號處理程序中做非局部轉移時應用sigsetjmp和siglongjmp函數。
進程
特殊進程:進程ID為0的是調度進程,為1的是init進程。
僵死進程:一個已經終止、但是其父進程尚未對其進行善后處理(獲取終止子進程的有關信息、釋放它仍占用的資源)的進程稱為僵死進程。
孤兒進程:父進程已經終止的進程。init進程會領養這些進程。
精靈進程:長生存期,系統引導裝入時啟動,關閉時終止,后臺運行。
fork函數創建新進程。子進程是父進程的復制品(如果正文段是只讀的,則父、子進程共享正文段)。現在很多實現并不做一個父進程數據段和堆的完全拷貝,而是使用了在寫時復制(Copy-On-Write,COW)技術。
vfork用于創建一個用來exec一個新程序的新進程,所以它不將父進程的地址空間完全復制到子進程。vfork保證子進程會先運行。
exec函數只是用另一個新程序替換了當前進程的正文、數據、堆和棧段。
進程有三種正常終止法及兩種異常終止法。不管哪種情況,最后都會執行內核中的同一段代碼。該段代碼為相應進程關閉所有打開描述符,釋放它所使用的存儲器等等。終止進程會通知其父進程它是如何終止的。
信號
信號是通知進程已發生某種條件的一種技術。
信號是軟件中斷。
信號提供了一種處理異步事件的方法。
進程處理信號有三種選擇:
1. 忽略該信號。
2. 按系統默認方式處理。
3. 提供一個函數,信號發生時則調用該函數。
早期UNIX系統的一個特性是:如果在進程執行一個低速系統調用而阻塞期間捕捉到一個信號,則該系統調用被中斷不再繼續執行。該系統調用返回出錯,其errno設置為EINTR。
好處:意味著已經發生某種事情,所以是個好機會應當喚醒阻塞的系統調用。
產生信號后,內核通常在進程表中設置某種形式的一個標志。當做了這個動作,我們就說向一個進程遞送了一個信號。在信號產生到遞送之間的時間間隔,稱為信號未決(pending)。
進程可以阻塞某個信號。除非對被阻塞的信號的動作是忽略,否則該進程將此信號保持在未決狀態,直到阻塞解除。進程調用sigpending函數將指定的信號設置為阻塞和未決。
每個進程都有一個信號屏蔽字,它規定了當前要阻塞遞送的信號集。進程可以調用sigprocmask來檢測和更改信號屏蔽字。
kill函數將信號發送給進程(組)。raise函數則允許進程向自身發送信號。
alarm函數設置鬧鐘時間,時間超過設置值時默認行為是終止進程。
pause函數使進程掛起直到捕捉到一個信號。
sigaction函數的功能是檢查或修改(或兩者)與指定信號相關聯的處理動作。此函數取代UNIX早期版本中使用的signal函數。
用于信號處理程序中作非局部轉移時應用sigsetjmp和siglongjmp函數。
sigsupsend函數恢復信號屏蔽字,然后使進程睡眠。
可再入函數
可再入函數是信號處理程序可以放心調用的函數。像malloc,如果malloc過程捕捉到信號,信號處理程序又malloc,這就會帶來問題,所以它不是可再入函數。10.6表10-3列出了所有的可再入函數。若在信號處理程序中調用一個不可再入函數,則其結果是不可預見的。
進程間通信
IPC (InterProcess Communication)。作者建議:學會使用管道和FIFO,盡可能避免使用消息隊列以及信號量,而應當考慮流管道和記錄鎖。
管道、FIFO、流管道、命名流管道、消息隊列、信號量、共享存儲這7種IPC通常限于同一臺主機的各個進程間的IPC。套接字和流則支持不同主機上各個進程間IPC。
管道是UNIX IPC最老的形式,有兩種限制:
1)它們是半雙工的。
2)它們只能在具有共同祖先的進程之間使用。
流管道沒有管道的第一種限制,FIFO和命名流管道沒有第二種限制。
main函數是如何調用的?
內核用exec函數啟動C程序。在調用main前先調用一個特殊的啟動例程(可執行程序文件將此啟動例程指定為程序的起始地址——這是編譯、鏈接的時候設置的)。啟動例程從內核取得命名行參數和環境變量值,然后為調用main函數做好準備。從main返回后,啟動例程會立即調用exit函數。
進程終止
_exit函數立即進入內核,而exit函數則先執行一些清除處理,再進入內核。
atexit函數登記至多32個函數,這些函數將由exit自動調用,它們被稱為exit handler。
設置-用戶-ID和設置-組-ID
對各種不同的用戶ID和組ID(實際、有效和保存的)的理解和編寫安全的設置-用戶-ID程序是至關重要的。運行設置-用戶-ID程序的進程通常得到額外的權限,所以編寫這種程序要特別謹慎。
以上內容轉自互聯網,現總結一下本書需要掌握的函數:
文件I/O
open()、close()、lseek()、read()、write()、dup()和dup()2、fcntl()。
文件和目錄
stat()、fstat()、lstat()、access()。
標準I/O庫
fopen()、fclose()、fread()、fwrite()、fget()、fseek()、ftell()、fcntl()。
進程控制
fork()、vfork()、exit()、wait()、waitpid()、system()。
信號
signal()、kill()、raise()、alarm()、pause()、sigprocmask()、sigpending()、sigsuspend()、sigaction()、abort()、sleep()。
線程
pthread_self()、pthread_create()、pthread_exit()、pthread_join()、pthread_detach()、互斥量、條件變量。
高級I/O
I/O多路轉接:select()、poll()、epoll()。
進程間通信
消息隊列、信號量semget()、sembuf()、共享內存shmget()、shmctl()、shmat()、shmdt()。
網絡IPC:套接字
TCP/UDP:socket()、htons()、inet_pton()、bind()、connect()、listen()、accept()、send()、sendto()、recv()、recvfrom()。
本書印象
本書覆蓋了UNIX上數百個系統調用及函數,但是實際上大多數的內容集中在I/O和進程兩大方面上。這兩方面的內容大概也是用得最廣的吧。I/O方面的內容包括了不帶緩存的I/O、標準I/O庫、終端I/O、高級I/O。進程方面的內容包括UNIX進程的環境、進程控制、信號、進程間通信、守護進程。
終端I/O、數據庫函數庫、打印機通信、調制解調器、偽終端這些章節感覺不重要,所以沒看。進程關系這一章好像也不重要,只有編寫精靈進程會涉及到該章的部分知識。其他章節應該都是很重要。
原子操作
只要涉及到多個進程共享資源,原子操作的概念就變成非常重要。
原子操作(atomic operation)指的是由多步組成的操作。如果該操作原子地執行,則或者執行完所有步,或者一步也不執行,不可能只執行所有步的一個子集。
競態條件(race condition):當多個進程都企圖對共享數據進行某種處理,而最后結果又取決于進程運行的順序時,則我們認為發生了競態條件。
與之相關的有一個概念,叫時間窗口,它就是競態條件下發生的。時間窗口大概就是說進程某一個操作與其下一個操作本來應該是連續執行,但是因為進程的切換,導致操作的不連續,導致共享資源被修改,導致程序運行偏離預期。書中10.4節介紹了早期的信號及一些經典的處理案例,這些案例都是由于時間窗口的存在而暗藏隱患。
出錯處理
UNIX函數出錯,通常返回一負值,并把全局變量errno設置為具有特定信息的值。
<errno.h>定義了errno以及可以賦予它的各種常數。對于errno應當知道兩條規則:
1. 如果沒有出錯,其值不會被一個例程清除。因此,僅當函數的返回值指明出錯時,才檢驗其值。
2. 任一函數都不會將errno值設為0,<errno.h>中定義的所有常數都不為0。
由于每個進程只有一個errno變量,一個通用的規則是,當在信號處理程序中調用庫函數或系統調用時,應當在其前保存errno,然后在其后恢復。
I/O效率
1. 對于不帶緩存的I/O,當緩存長度等于文件系統的塊長時,讀操作的時間是最小;緩存長度小于塊長,讀操作時間增多;大于塊長,也不會提高讀操作的效率。
2. 標準I/O可以使用戶不必像文件I/O那樣擔心如何選擇正確的緩存長度。標準I/O并不比文件I/O慢很多。5.8講標準I/O的效率。
3. 比起多次read和write,readv和writev更好。12.7講readv和writev。
4. mmap/memcpy方式比read/write方式效率高,見12.9。
標準I/O
標準I/O可以使用戶不必像文件I/O那樣考慮緩存及最佳I/O長度的選擇。它提供緩存,目的是盡可能少的減少使用read和write的數量。它有三種類型的緩存:全緩存、行緩存和不帶緩存。但是標準I/O緩存也是產生很多問題,引起很多混淆的一個領域。
中文版的P142講了一個很有意思的例子,由于fork的實現方式和IO緩存導致在終端和文件中輸出結果的不同。
高級I/O
非阻塞I/O是調用不會永遠阻塞的I/O操作,如果操作不能完成,則立即出錯返回。
記錄鎖(record locking)的功能:一個進程正在讀或修改文件的某個部分時,可以阻塞其他進程修改同一文件區。
程序12-5講了一個有趣的例子,讓精靈進程阻止其多份副本同時運行,原理是這樣的:精靈進程把它們的進程ID寫道一個各自專有的PID文件上,通過對這個文件加寫鎖來保證只有一個副本在運行。
I/O多路轉接: select 和 poll,網絡編程用得多。
readv和writev函數用于在一個函數調用中讀、寫多個非連續緩存。
readn和writen讀、寫指定的N字節數據,并處理返回值小于要求值得情況。
存儲映射I/O mmap使一個磁盤文件與存儲空間中的一個緩存相映射。當從緩存中取數據,就相當于讀文件中的相應字節,類似的,將數據存入緩存,則相應字節就自動地寫入文件。
stat函數
第4章詳細介紹了stat結構中的每一個成員。包括3個函數,stat、fstat、lstat函數。這使得我們對UNIX文件的各個屬性都有所了解。對文件的所有屬性以及對文件進行操作的所有函數都有完整的了解對各種UNIX程序設計都非常重要。
非局部轉移
setjmp和longjmp函數這兩個函數用于執行跨越函數的跳轉功能。
用于信號處理程序中做非局部轉移時應用sigsetjmp和siglongjmp函數。
進程
特殊進程:進程ID為0的是調度進程,為1的是init進程。
僵死進程:一個已經終止、但是其父進程尚未對其進行善后處理(獲取終止子進程的有關信息、釋放它仍占用的資源)的進程稱為僵死進程。
孤兒進程:父進程已經終止的進程。init進程會領養這些進程。
精靈進程:長生存期,系統引導裝入時啟動,關閉時終止,后臺運行。
fork函數創建新進程。子進程是父進程的復制品(如果正文段是只讀的,則父、子進程共享正文段)。現在很多實現并不做一個父進程數據段和堆的完全拷貝,而是使用了在寫時復制(Copy-On-Write,COW)技術。
vfork用于創建一個用來exec一個新程序的新進程,所以它不將父進程的地址空間完全復制到子進程。vfork保證子進程會先運行。
exec函數只是用另一個新程序替換了當前進程的正文、數據、堆和棧段。
進程有三種正常終止法及兩種異常終止法。不管哪種情況,最后都會執行內核中的同一段代碼。該段代碼為相應進程關閉所有打開描述符,釋放它所使用的存儲器等等。終止進程會通知其父進程它是如何終止的。
信號
信號是通知進程已發生某種條件的一種技術。
信號是軟件中斷。
信號提供了一種處理異步事件的方法。
進程處理信號有三種選擇:
1. 忽略該信號。
2. 按系統默認方式處理。
3. 提供一個函數,信號發生時則調用該函數。
早期UNIX系統的一個特性是:如果在進程執行一個低速系統調用而阻塞期間捕捉到一個信號,則該系統調用被中斷不再繼續執行。該系統調用返回出錯,其errno設置為EINTR。
好處:意味著已經發生某種事情,所以是個好機會應當喚醒阻塞的系統調用。
產生信號后,內核通常在進程表中設置某種形式的一個標志。當做了這個動作,我們就說向一個進程遞送了一個信號。在信號產生到遞送之間的時間間隔,稱為信號未決(pending)。
進程可以阻塞某個信號。除非對被阻塞的信號的動作是忽略,否則該進程將此信號保持在未決狀態,直到阻塞解除。進程調用sigpending函數將指定的信號設置為阻塞和未決。
每個進程都有一個信號屏蔽字,它規定了當前要阻塞遞送的信號集。進程可以調用sigprocmask來檢測和更改信號屏蔽字。
kill函數將信號發送給進程(組)。raise函數則允許進程向自身發送信號。
alarm函數設置鬧鐘時間,時間超過設置值時默認行為是終止進程。
pause函數使進程掛起直到捕捉到一個信號。
sigaction函數的功能是檢查或修改(或兩者)與指定信號相關聯的處理動作。此函數取代UNIX早期版本中使用的signal函數。
用于信號處理程序中作非局部轉移時應用sigsetjmp和siglongjmp函數。
sigsupsend函數恢復信號屏蔽字,然后使進程睡眠。
可再入函數
可再入函數是信號處理程序可以放心調用的函數。像malloc,如果malloc過程捕捉到信號,信號處理程序又malloc,這就會帶來問題,所以它不是可再入函數。10.6表10-3列出了所有的可再入函數。若在信號處理程序中調用一個不可再入函數,則其結果是不可預見的。
進程間通信
IPC (InterProcess Communication)。作者建議:學會使用管道和FIFO,盡可能避免使用消息隊列以及信號量,而應當考慮流管道和記錄鎖。
管道、FIFO、流管道、命名流管道、消息隊列、信號量、共享存儲這7種IPC通常限于同一臺主機的各個進程間的IPC。套接字和流則支持不同主機上各個進程間IPC。
管道是UNIX IPC最老的形式,有兩種限制:
1)它們是半雙工的。
2)它們只能在具有共同祖先的進程之間使用。
流管道沒有管道的第一種限制,FIFO和命名流管道沒有第二種限制。
main函數是如何調用的?
內核用exec函數啟動C程序。在調用main前先調用一個特殊的啟動例程(可執行程序文件將此啟動例程指定為程序的起始地址——這是編譯、鏈接的時候設置的)。啟動例程從內核取得命名行參數和環境變量值,然后為調用main函數做好準備。從main返回后,啟動例程會立即調用exit函數。
進程終止
_exit函數立即進入內核,而exit函數則先執行一些清除處理,再進入內核。
atexit函數登記至多32個函數,這些函數將由exit自動調用,它們被稱為exit handler。
設置-用戶-ID和設置-組-ID
對各種不同的用戶ID和組ID(實際、有效和保存的)的理解和編寫安全的設置-用戶-ID程序是至關重要的。運行設置-用戶-ID程序的進程通常得到額外的權限,所以編寫這種程序要特別謹慎。
以上內容轉自互聯網,現總結一下本書需要掌握的函數:
文件I/O
open()、close()、lseek()、read()、write()、dup()和dup()2、fcntl()。
文件和目錄
stat()、fstat()、lstat()、access()。
標準I/O庫
fopen()、fclose()、fread()、fwrite()、fget()、fseek()、ftell()、fcntl()。
進程控制
fork()、vfork()、exit()、wait()、waitpid()、system()。
信號
signal()、kill()、raise()、alarm()、pause()、sigprocmask()、sigpending()、sigsuspend()、sigaction()、abort()、sleep()。
線程
pthread_self()、pthread_create()、pthread_exit()、pthread_join()、pthread_detach()、互斥量、條件變量。
高級I/O
I/O多路轉接:select()、poll()、epoll()。
進程間通信
消息隊列、信號量semget()、sembuf()、共享內存shmget()、shmctl()、shmat()、shmdt()。
網絡IPC:套接字
TCP/UDP:socket()、htons()、inet_pton()、bind()、connect()、listen()、accept()、send()、sendto()、recv()、recvfrom()。
總結
以上是生活随笔為你收集整理的UNIX环境高级编程学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android中多点触控以及手势的基础知
- 下一篇: GAN的后门攻击:The Devil i