《UNIX环境高级编程——APUE》
《UNIX環(huán)境高級(jí)編程——APUE》
【附】小知識(shí)
1、同步、異步
概念:消息的通知機(jī)制
解釋:涉及到IO通知機(jī)制;
同步,就是發(fā)起調(diào)用后,被調(diào)用者處理消息,必須等處理完才直接返回結(jié)果,沒處理完之前是不返回的,調(diào)用者主動(dòng)等待結(jié)果;
eg. 我去銀行辦理業(yè)務(wù), 選擇排隊(duì)等,排到頭了就辦理。
eg. 我去銀行辦理業(yè)務(wù)*,* 取一個(gè)小紙條上面有我的號(hào)碼*,* 等到排到我這一號(hào)時(shí)由柜臺(tái)的人通知我輪到我去辦理業(yè)務(wù)。
2、阻塞與非阻塞
概念:程序等待調(diào)用結(jié)果時(shí)的狀態(tài)
解釋:涉及到CPU線程調(diào)度;
阻塞:就是調(diào)用結(jié)果返回之前,該執(zhí)行線程會(huì)被掛起,不釋放 CPU 執(zhí)行權(quán),線程不能做其它事情,只能等待,只有等到調(diào)用結(jié)果返回了,才能接著往下執(zhí)行;
eg. 上面的那個(gè)例子, 不論是排隊(duì)還是使用號(hào)碼等待通知,如果在這個(gè)等待的過程中,等待者除了等待消息之外不能做其它的事情, 那么該機(jī)制就是阻塞的。
非阻塞:就是在沒有獲取調(diào)用結(jié)果時(shí),不是一直等待,線程可以往下執(zhí)行,如果是同步的,通過輪詢的方式檢查有沒有調(diào)用結(jié)果返回,如果是異步的,會(huì)通知回調(diào)。
eg. 在銀行辦理這些業(yè)務(wù)的時(shí)候一邊打打電話發(fā)發(fā)短信一邊等待,這樣的狀態(tài)就是非阻塞的。
3、基本的 進(jìn)程控制語(yǔ)言
? 用 fork 創(chuàng)建新進(jìn)程;
? 用 exec 可以初始執(zhí)行新的程序;
? exit 函數(shù) 和 wait 函數(shù)處理終止和等待終止。
exit(0) 正常退出
exit(1) 非正常退出
4、進(jìn)程組、會(huì)話、前臺(tái)進(jìn)程組、后臺(tái)進(jìn)程組、終端控制
進(jìn)程組與會(huì)話:
進(jìn)程組是一組相關(guān)進(jìn)程的集合,會(huì)話是一組相關(guān)進(jìn)程組的集合。進(jìn)程都有父進(jìn)程, 父進(jìn)程也有父進(jìn)程, 這就形成了一個(gè)以init進(jìn)程為根的家族樹。除此以外,進(jìn)程還有其他層次關(guān)系:進(jìn)程、進(jìn)程組、會(huì)話。進(jìn)程組和會(huì)話在進(jìn)程之前形成了兩級(jí)的層次:進(jìn)程組是一組相關(guān)進(jìn)程的集合,會(huì)話是一組相關(guān)進(jìn)程組的集合。這樣說來,一個(gè)進(jìn)程會(huì)有如下ID:
? PID:進(jìn)程的唯一標(biāo)識(shí)。對(duì)于多線程的進(jìn)程而言所有線程調(diào)用getpid()函數(shù)會(huì)返回相同值。
? PGID:進(jìn)程組ID。每個(gè)進(jìn)程都會(huì)有進(jìn)程組ID,表示該進(jìn)程所屬的進(jìn)程組。默認(rèn)情況下新創(chuàng)建的進(jìn)程會(huì)繼承父進(jìn)程的進(jìn)程組ID。
? SID:會(huì)話ID。每個(gè)進(jìn)程也都有會(huì)話ID。默認(rèn)情況下,新創(chuàng)建的進(jìn)程會(huì)繼承父進(jìn)程的會(huì)話ID
會(huì)話:
由于Linux是多用戶多任務(wù)的分時(shí)系統(tǒng),所以必須要支持多個(gè)用戶同時(shí)使用一個(gè)操作系統(tǒng)。當(dāng)一個(gè)用戶登錄一次系統(tǒng)就形成一次會(huì)話。
一個(gè)會(huì)話包含多個(gè)進(jìn)程組,但是只能有一個(gè)前臺(tái)進(jìn)程組。每個(gè)會(huì)話都有一個(gè)會(huì)話首領(lǐng) (leader),即創(chuàng)建會(huì)話的進(jìn)程 sys_setsid() / setsid() 調(diào)用能創(chuàng)建一個(gè)會(huì)話。但必須注意的是,只有當(dāng)前進(jìn)程不是進(jìn)程組組長(zhǎng)時(shí),才能創(chuàng)建一個(gè)新的會(huì)話。調(diào)用 setsid 之后,該進(jìn)程成為會(huì)話的 leader 。
一個(gè)會(huì)話只能有一個(gè)控制終端。這通常是登錄到其上的終端設(shè)備(在終端登錄情況下)或者偽終端設(shè)備在網(wǎng)絡(luò)登錄情況下。建立與控制終端連接的會(huì)話被稱為控制進(jìn)程一個(gè)會(huì)話中的幾個(gè)進(jìn)程組可以分為前臺(tái)進(jìn)程組與后臺(tái)進(jìn)程組。所以一個(gè)會(huì)話中,應(yīng)該包括控制進(jìn)程(會(huì)話首進(jìn)程),一個(gè)前臺(tái)進(jìn)程組與任意多個(gè)后臺(tái)進(jìn)程組。
前臺(tái)進(jìn)程 與 后臺(tái)進(jìn)程:
用戶在 shell 中可以同時(shí)執(zhí)行多個(gè)命令。對(duì)于耗時(shí)很久的命令(如編譯大型工程),用戶不必傻傻等待命令運(yùn)行完畢才執(zhí)行下一個(gè)命令。用戶在執(zhí)行命令時(shí),可以在命令的結(jié)尾添加 “&” 符號(hào),表示將命令放入后臺(tái)執(zhí)行。這樣該命令對(duì)應(yīng)的進(jìn)程組即為后臺(tái)進(jìn)程組。
在任意時(shí)刻,可能同時(shí)存在多個(gè)后臺(tái)進(jìn)程組,但是不管什么時(shí)候都只能有一個(gè)前臺(tái)進(jìn)程組。只有在前臺(tái)進(jìn)程組中進(jìn)程才能在控制終端讀取輸入。當(dāng)用戶在終端輸入信號(hào)生成終端字符(如ctrl+c、ctrl+z、ctr+\等)時(shí),對(duì)應(yīng)的信號(hào)只會(huì)發(fā)送給前臺(tái)進(jìn)程組。
shell 中可以存在多個(gè)進(jìn)程組,無論是前臺(tái)進(jìn)程組還是后臺(tái)進(jìn)程組,它們或多或少存在一定的聯(lián)系,為了更好地控制這些進(jìn)程組(或者稱為作業(yè)),系統(tǒng)引入了會(huì)話的概念。會(huì)話的意義在于將很多的工作囊括在一個(gè)終端,選取其中一個(gè)作為前臺(tái)來直接接收終端的輸入及信號(hào),其他的工作則放在后臺(tái)執(zhí)行。
終端控制:
會(huì)話的領(lǐng)頭進(jìn)程打開一個(gè)終端后,該終端就會(huì)成為該會(huì)話的控制終端(SVR4/linux),與控制終端建立連接的會(huì)話領(lǐng)頭進(jìn)程成為控制進(jìn)程(session leader)。一個(gè)會(huì)話只能有一個(gè)控制終端,產(chǎn)生在控制終端上的輸入和信號(hào)將發(fā)送給會(huì)話的前臺(tái)進(jìn)程組中的所有進(jìn)程,終端上的連接斷開時(shí)(比如網(wǎng)絡(luò)斷開或Modem斷開),掛起信號(hào)將發(fā)送到控制進(jìn)程(session leader)。
綜上:
進(jìn)程屬于一個(gè)進(jìn)程組,進(jìn)程組屬于一個(gè)會(huì)話,會(huì)話可能有也可能沒有控制終端。一般而言,當(dāng)用戶在某個(gè)終端上登錄時(shí),一個(gè)新的會(huì)話就開始了。
進(jìn)程組由組中的領(lǐng)頭進(jìn)程標(biāo)識(shí),領(lǐng)頭進(jìn)程的進(jìn)程標(biāo)識(shí)符就是進(jìn)程組的組 標(biāo)識(shí)符。類似的,每個(gè)會(huì)話也有對(duì)應(yīng)一個(gè)領(lǐng)頭進(jìn)程。同一會(huì)話中的進(jìn)程通過該會(huì)話的領(lǐng)頭進(jìn)程和一個(gè)終端相連,該終端作為這個(gè)會(huì)話的控制終端。
一個(gè)會(huì)話只有一個(gè)控制終端,而一個(gè)控制終端只能控制一個(gè)會(huì)話。用戶通過控制終端,可以向控制終端所控制的會(huì)話中的進(jìn)程發(fā)送鍵盤信號(hào)。
同一個(gè)會(huì)話中只能有一個(gè)前臺(tái)進(jìn)程組,屬于前臺(tái)進(jìn)程組的進(jìn)程可以從控制終端獲得輸入,而其他進(jìn)程均是后臺(tái)進(jìn)程,可能分屬于不同的后臺(tái)進(jìn)程組。
當(dāng)我們打開多個(gè)終端窗口時(shí),實(shí)際上就創(chuàng)建了多個(gè)終端會(huì)話。每個(gè)會(huì)話都有自己的前臺(tái)工作和后臺(tái)工作
5、init
在計(jì)算機(jī)上啟動(dòng)Linux時(shí),內(nèi)核裝入并啟動(dòng)init程序。然后init程序裝載硬盤和啟動(dòng)終端程序。登錄終端程序時(shí),它啟動(dòng)命令行界面Shell。在計(jì)算機(jī)上啟動(dòng)Linux之后,init程序監(jiān)視任何關(guān)閉計(jì)算機(jī)的信號(hào),如不間斷電源(UPS)發(fā)生的電源故障信號(hào)和重新啟動(dòng)命令。init是Linux系統(tǒng)操作中不可缺少的程序之一。所謂的init進(jìn)程,它是一個(gè)由內(nèi)核啟動(dòng)的用戶級(jí)進(jìn)程。內(nèi)核自行啟動(dòng)(已經(jīng)被載入內(nèi)存,開始運(yùn)行,并已初始化所有的設(shè)備驅(qū)動(dòng)程序和數(shù)據(jù)結(jié)構(gòu)等)之后,就通過啟動(dòng)一個(gè)用戶級(jí)程序init的方式,完成引導(dǎo)進(jìn)程。所以,init始終是第一個(gè)進(jìn)程(其進(jìn)程編號(hào)始終為1)。 內(nèi)核會(huì)在過去曾使用過init的幾個(gè)地方查找它,它的正確位置(對(duì)Linux系統(tǒng)來說)是/sbin/init。如果內(nèi)核找不到init,它就會(huì)試著運(yùn)行/bin/sh,如果運(yùn)行失敗,系統(tǒng)的啟動(dòng)也會(huì)失敗。6、Unix/Linux 的 System V、BSD、Posix概念
System V和BSD
??Unix操作系統(tǒng)在操作風(fēng)格上主要分為System V和BSD(目前一般采用BSD的第4個(gè)版本SVR4),前者的代表的操作系統(tǒng)有Solaris操作系統(tǒng),在Solaris1.X之前,Solaris采用的是BSD風(fēng)格,2.x之后才投奔System V陣營(yíng)。后者的代表的操作系統(tǒng)有FreeBSD。
??System V它最初由AT&T開發(fā),曾經(jīng)也被稱為AT&T System V,是Unix操作系統(tǒng)眾多版本中的一支。在1983年第一次發(fā)布,一共發(fā)行了4個(gè)System V的主要版本,System V Release4,或者稱為SVR4,是最成功的版本,該版本有些風(fēng)格成為一些UNIX共同特性的源頭,如下表格的初始化腳本/etc/init.d。用來控制系統(tǒng)的啟動(dòng)和關(guān)閉。
??BSD(Berkeley Software Distribution,伯克利軟件套件)是Unix的衍生系統(tǒng),1970年代由伯克利加州大學(xué)(Uni Versity of California, Berkeley)開創(chuàng)。BSD用來代表由此派生出的各種套件集合。
Poxis和System V
??System V的概念如上所述。Posix是Portable Operating System Interface(可移植性操作系統(tǒng)接口)的簡(jiǎn)稱,是一個(gè)電氣與電子工程學(xué)會(huì)即IEEE開發(fā)的一系列標(biāo)準(zhǔn),目的是為運(yùn)行在不同操作系統(tǒng)的應(yīng)用程序提供統(tǒng)一的接口,實(shí)現(xiàn)者是不同的操作系統(tǒng)內(nèi)核。
??將這兩個(gè)名詞放在一起討論的一般是在Linux的進(jìn)程間通信中,如在信號(hào)量編程中,有Posix信號(hào)量和System V信號(hào)量。它們都可以用于進(jìn)程或者線程間的同步。然而, Posix信號(hào)量是基于內(nèi)存的,即信號(hào)量值是放在共享內(nèi)存中的,它使與文件系統(tǒng)中的路徑名對(duì)應(yīng)的名字來標(biāo)識(shí)。當(dāng)我們談?wù)摗癙osix 信號(hào)量”時(shí),所指的是單個(gè)計(jì)數(shù)信號(hào)量。在Linux操作系統(tǒng)中,Posix信號(hào)量(共享內(nèi)存、消息隊(duì)列)可以通過ipcs命令查看。Posix信號(hào)量多用于進(jìn)程間通信。
??System v信號(hào)量測(cè)試基于內(nèi)核的,它放在內(nèi)核里面,要使用System V信號(hào)量需要進(jìn)入內(nèi)核態(tài),所以在多線程編程中一般不建議使用System V信號(hào)量,因?yàn)榫€程相對(duì)于進(jìn)程是輕量級(jí)的,從操作系統(tǒng)的調(diào)度開銷角度看,如果使用System V信號(hào)量會(huì)使得每次調(diào)用都要進(jìn)入內(nèi)核態(tài),喪失了線程的輕量?jī)?yōu)勢(shì)。當(dāng)我們討論“System v信號(hào)量”時(shí),所指的是計(jì)數(shù)信號(hào)量集。
第1章-UNIX基礎(chǔ)知識(shí)
1、UNIX 和Linux的區(qū)別:
? UNIX 誕生于 20 世紀(jì) 60 年代末,
Windows 誕生于 20 世紀(jì) 80 年代中期,
Linux 誕生于 20 世紀(jì) 90 年代初
(后來的 Windows 和 Linux 都參考了 UNIX)
? UNIX大多與硬件配套(UNIX操作系統(tǒng)),Linux 可運(yùn)行在多種硬件平臺(tái)上。
? UNIX收費(fèi)(貝爾實(shí)驗(yàn)室),Linux 免費(fèi)開源
2、什么是Linux 系統(tǒng)
Linux系統(tǒng)主要由以下4部分構(gòu)成:
Linux內(nèi)核
內(nèi)核主要負(fù)責(zé)以下四種功能:
系統(tǒng)內(nèi)存管理
內(nèi)核不斷地在交換空間(swap space)和實(shí)際物理內(nèi)存之間交換虛擬內(nèi)存中的內(nèi)容。
2.軟件程序管理(進(jìn)程管理)
3.硬件設(shè)備管理
通過驅(qū)動(dòng)程序?qū)崿F(xiàn)硬件設(shè)備與應(yīng)用程序之間的通信。在Linux系統(tǒng)中加入驅(qū)動(dòng)程序代碼的方式有以下兩種: 1.編譯進(jìn)內(nèi)核的設(shè)備驅(qū)動(dòng)代碼
2.可插入內(nèi)核的設(shè)備驅(qū)動(dòng)代碼
4.文件系統(tǒng)管理
ext,ext2,ext3,ext4,minix,nfs,ntfs,XFS等。
GNU工具
GNU(GNU is not Unix的縮寫),是一套為Unix系統(tǒng)管理員設(shè)計(jì)的一套類似于Unix的環(huán)境。
Linux 系統(tǒng)和 GNU 工具的結(jié)合體稱為 Linux系統(tǒng),也叫做 GNU/Linux系統(tǒng)。 核心 GNU 工具(coreutils)包括以下三部分:
1.用以處理文件的工具
2.用于處理文本的工具
3.用于管理進(jìn)程的工具
還包括 shell,例如 bash shell。
圖形化桌面環(huán)境
X Window軟件包:直接和 PC 上的顯卡和顯示器打交道的底層程序,可以產(chǎn)生圖形化顯示環(huán)境。 其中最流行的軟件包時(shí) x.org。 桌面環(huán)境:KDE、GNOME、Unity(Ubuntu特有)等
應(yīng)用程序
3、進(jìn)程控制
進(jìn)程
進(jìn)程:程序的執(zhí)行實(shí)例被稱為進(jìn)程(process)。
進(jìn)程ID: UNIX系統(tǒng)確保每個(gè)進(jìn)程都有一個(gè)唯一的數(shù)字標(biāo)識(shí)符(是一個(gè)非負(fù)整數(shù))。
進(jìn)程控制:有 3 個(gè)用于進(jìn)程控制的主要函數(shù): folk 、exec 和 waitpid。
線程
線程:某一時(shí)刻執(zhí)行的一組機(jī)器指令。
線程ID:線程也有自己的ID標(biāo)識(shí)符,只在它所屬的進(jìn)程內(nèi)起作用。一個(gè)線程中的線程ID在另一個(gè)進(jìn)程中沒有意義。
信號(hào)
信號(hào)(signal): 用于通知進(jìn)程發(fā)生了某種情況。
第 2 章- UNIX 標(biāo)準(zhǔn)及實(shí)現(xiàn)
沒講啥
第 3 章-文件 I/O
1、引言
文件 I/O函數(shù)主要包括:打開文件、讀文件、寫文件 操作。用到的函數(shù)有:open、 read、 write、 lseed、 close。2、文件描述符
1) UNIX系統(tǒng)shell把
文件描述符 0 與進(jìn)程的標(biāo)準(zhǔn)輸入關(guān)聯(lián);
文件描述符 1 與標(biāo)準(zhǔn)輸出關(guān)聯(lián);
文件描述符 2 與標(biāo)準(zhǔn)錯(cuò)誤關(guān)聯(lián);
一個(gè)套接字端點(diǎn)表示為一個(gè)文件描述符(16.5)。
3、函數(shù) open 和 openat
在 <fcnt1.h>中定義了幾個(gè)常用的常量,表示對(duì)文件的操作:
O_RDONLY 只讀打開, 一般定義為 0
O_WRONLY 只寫打開, 一般定義為 1
O_RDWR 讀、寫打開, 一般定義為2
4、函數(shù) creat
#include <fcnt1.h>
int creat(const char *path, mode_t mode);
//返回值:成功,返回為只寫打開的文件描述符。出錯(cuò),返回-1
//path 創(chuàng)建的文件名, mode 權(quán)限位
//eg.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PERM 0755
int main(void)
{
static char filename[] = “file.txt”; //會(huì)創(chuàng)建一個(gè)名為 file的txt文件
int fd;
}
4.1 Linux 系統(tǒng)中采用三位十進(jìn)制數(shù)表示權(quán)限,如0755, 0644.
ABCD A- 0, 表示十進(jìn)制 B-用戶 C-組用戶 D-其他用戶
— -> 0 (no excute , no write ,no read) --x -> 1 excute, (no write, no read) -w- -> 2 write -wx -> 3 write, excute r-- -> 4 read r-x -> 5 read, excute rw- -> 6 read, write , rwx -> 7 read, write , excute
0755-> 即用戶具有讀/寫/執(zhí)行權(quán)限,組用戶和 其它用戶具有讀寫權(quán)限; 0644->即用戶具有讀寫權(quán)限, 組用戶和 其它用戶具有只讀權(quán)限;
一般賦予目錄0755權(quán)限,文件0644權(quán)限。
5、函數(shù) close
頭文件:
#include<unistd.h>
//功能:關(guān)閉一個(gè)已經(jīng)打開的文件
原型
int close(int fd)
//參數(shù)說明: fd:是需要關(guān)閉的文件描述符
返回值
//成功:返回0;
//失敗:返回-1,并設(shè)置errno
6、函數(shù) lseek
可以調(diào)用 lseek 顯示地為一個(gè)打開文件設(shè)置偏移量。7、函數(shù) read
從打開文件中讀數(shù)據(jù)。8、函數(shù)write
像打開文件寫數(shù)據(jù)11、原子操作
1.函數(shù) pread 和 pwrite
這兩種擴(kuò)展允許原子性地定位并執(zhí)行 I/O
12、函數(shù) dup 和 dup2
這兩個(gè)函數(shù)可用來復(fù)制一個(gè)現(xiàn)有的文件描述符。文件描述符:
Linux 中一切皆文件。Linux 會(huì)給每一個(gè)文件分配一個(gè)編號(hào)(ID),這個(gè)編號(hào)就是一個(gè)整數(shù),也就是文件描述符。
13、函數(shù) sync、fsync 和fdatasync
延遲寫(delay write):我們向文件寫入數(shù)據(jù)時(shí),內(nèi)核通常先將數(shù)據(jù)復(fù)制到緩沖區(qū)中,然后排入隊(duì)列,晚些時(shí)候再寫入磁盤。
這三個(gè)函數(shù)是為了保證磁盤上實(shí)際文件系統(tǒng)與緩沖區(qū)中內(nèi)容的一致性。
14、函數(shù) fcnt1
它可以改變已經(jīng)打開文件的屬性
15、函數(shù) ioct1
ioct1 函數(shù)一直是 I/O 操作的雜物箱。終端I/O是使用 ioct1最多的地方。16、 /dev.fd
其目錄項(xiàng)是名為0、1、2等的文件。 打開文件 /dev/fd/n 等效于復(fù)制描述符 n(假定描述符 n 是打開的)。第 4 章- 文件和目錄
1、
2、函數(shù) stat、 fstat、fstatat 和 lstat
功能:返回與此命名文件有關(guān)的信息結(jié)構(gòu)。3、文件類型
? 普通文件(regular file)
? 目錄文件(directory file)
? 塊特殊文件(block special file)
? 字符特殊文件(character special file)
? FIFO
? 套接字(socket)
? 符號(hào)鏈接(symbolic link)
4、設(shè)置用戶 ID 和設(shè)置組 ID
5、文件訪問權(quán)限
每個(gè)文件有 9 個(gè)訪問權(quán)限位S_IRUSR 用戶讀
S_IWUSR 用戶寫
S_IXUSR 用戶執(zhí)行
S_IRGRP 組讀
S_IWGRP 組寫
S_IXGRP 組執(zhí)行
S_IROTH 其他讀
S_IWOTH 其他寫
S_IXOTH 其他執(zhí)行
6、新文件和目錄的所有權(quán)
7、函數(shù) access 和 faccessat
這兩個(gè)函數(shù)是按實(shí)際用戶 ID 和實(shí)際組 ID 進(jìn)行訪問權(quán)限測(cè)試的。
8、函數(shù) umask
9、函數(shù) chmod 、fchmod 、和 fchmodat
10、黏著位
11、函數(shù) chown、fchown、fchownat 和 lchowm
13、文件截?cái)?/h2>
#include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
函數(shù) truncate 和ftruncate 可以將一個(gè)現(xiàn)有文件長(zhǎng)度截?cái)酁?length 長(zhǎng)
14、文件系統(tǒng)
任何一個(gè)文件可以有多個(gè)目錄指向其 i 節(jié)點(diǎn)。15、函數(shù) link、linkat、unlink、unlinkat 和 remove
函數(shù)link、linkat 可以創(chuàng)建一個(gè)指向現(xiàn)有文件的鏈接。#include <unistd.h>
int link(const char *existingpath, const char *newpath);
int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
函數(shù) unlink 可以刪除一個(gè)現(xiàn)有的目錄項(xiàng)。
#include <unistd.h>
int unlink(const char *pathname);
int unlink(int fd, const char *pathname, int flag);
函數(shù) unlinkat 可以類似于 rmdir 一樣刪除目錄
函數(shù) remove 可以解除對(duì)一個(gè)文件或目錄的鏈接。
16、函數(shù) rename 和 renameat
文件或目錄可以用 rename 函數(shù) 或者renameat函數(shù)進(jìn)行重命名。
17、符號(hào)鏈接
18、創(chuàng)建和讀取符號(hào)鏈接
可以用 symlink 或 symlinkat函數(shù)創(chuàng)建一個(gè)符號(hào)鏈接
19、文件的時(shí)間
20、函數(shù) futimens、utimensat 和 utimes
futimens 和 utimensat函數(shù)可以指定納秒級(jí)精度的時(shí)間戳。21、函數(shù) mkdir、mkdirat 和 rmdir
用 mkdir 和 mkdirat 函數(shù)創(chuàng)建目錄
用 rmdir 函數(shù)刪除目錄。
22、讀目錄
對(duì)某個(gè)目錄具有訪問的任一用戶都可以讀該目錄,但是為了防止文件系統(tǒng)產(chǎn)生混亂,只有內(nèi)核才能寫目錄。23、函數(shù) chdir、 fchdir 、 和 getcwd
進(jìn)程調(diào)用 chdir 和 fchdir 函數(shù)可以更改當(dāng)前工作目錄。24、設(shè)備特殊文件
25、文件訪問小結(jié)
第 5 章- 標(biāo)準(zhǔn)I/O庫(kù)
1、
2、流和 FILE 對(duì)象
? 對(duì)于所有的 I/O函數(shù),都是圍繞文件描述符的;
對(duì)于標(biāo)準(zhǔn)的 I/O庫(kù),它們的操作是圍繞 流(stream)進(jìn)行的。
? 流的定向(stream’s orientation)決定了所讀、寫的字符是單字節(jié)還是多字節(jié)。
有兩個(gè)函數(shù)可以改變流的定向: freopen 、 fwide;
=> freopen函數(shù)可以清除一個(gè)流的定向;
fwide 函數(shù)可用于設(shè)置流的定向。
3、標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出 和標(biāo)準(zhǔn)錯(cuò)誤
分別是: STDIN_FILENO 、STDOUT_FILENO、STDERR_FILENO4、緩沖
標(biāo)準(zhǔn) I/O庫(kù)提供緩沖的目的是盡可能減少使用 read 和write調(diào)用的次數(shù)。標(biāo)準(zhǔn)I/O庫(kù)提供了三種類型的緩沖:? 全緩沖:在填滿標(biāo)準(zhǔn)I/O緩沖區(qū)后才進(jìn)行實(shí)際I/O操作。
? 行緩沖:當(dāng)再輸入和輸出中遇到換行符時(shí),標(biāo)準(zhǔn)I/O庫(kù)執(zhí)行I/O操作。
? 不帶緩沖:標(biāo)準(zhǔn)I/O庫(kù)不對(duì)字符進(jìn)行緩沖存儲(chǔ)。
5、打開流
與以下三個(gè)函數(shù)有關(guān): fopen、 freopen、 fdopen。? fopen 函數(shù)打開路徑名為 pathname 的一個(gè)指定的文件;
? fropen 函數(shù)在一個(gè)指定的流上打開一個(gè)指定的文件,若該流已經(jīng)打開,則先關(guān)閉該流;
? fdopen 函數(shù)取一個(gè)已有的文件描述符,并使一個(gè)標(biāo)準(zhǔn)的I/O流與該描述符相結(jié)合。
6、讀和寫流
一旦打開了流,則可在3種不同類型的非格式化I/O中進(jìn)行選擇,對(duì)其進(jìn)行讀、寫操作。
? 每次一個(gè)字符的I/O;
? 每次一行的I/O;
? 直接I/O。
7、每行一次 I/O
有兩個(gè)函數(shù)指定了緩沖區(qū)的地址,讀入的行將送入其中:fgets、gets函數(shù)。
? fgets 必須指定緩沖的長(zhǎng)度 n
? gets 函數(shù)是一個(gè)不推薦的函數(shù)。
問題:調(diào)用子在使用gets時(shí)不能指定緩沖區(qū)的長(zhǎng)度。
8、標(biāo)準(zhǔn) I/O 的效率
9、二進(jìn)制 I/O
Linux 提供了兩個(gè)函數(shù)以執(zhí)行二進(jìn)制I/O操作: fread 函數(shù) 和 fwrite函數(shù)。
函數(shù)功能是 返回讀或?qū)懙膶?duì)象數(shù)。
問題:它只能用于讀在同一系統(tǒng)上已寫的數(shù)據(jù)。
10、定位流
有三種方法定位 I/O流:
? ftell 和 fseek 函數(shù)。
? ftello 和 fseeko 函數(shù)
? fgetpos 和 fsetpos 函數(shù)。
11、格式化 I/O
? 格式化輸出:
由5個(gè)printf函數(shù)來處理:
o printf:將格式化數(shù)據(jù)寫到標(biāo)準(zhǔn)輸出
o fprintf:寫至指定的流;
o dprintf:寫至指定的文件描述符;
o sprintf:將格式化的字符送入數(shù)組 buf 中。
o snprintf:能夠解決緩沖區(qū)溢出問題。
? 格式化輸入:
由3個(gè) scanf函數(shù)來處理:
o scanf 族用于分析輸入字符串,并將字符序列轉(zhuǎn)換成指定類型的變量。
12、實(shí)現(xiàn)細(xì)節(jié)
13、臨時(shí)文件
ISO C標(biāo)準(zhǔn)I/O庫(kù)提供了兩個(gè)函數(shù)以幫助創(chuàng)建臨時(shí)文件。
? tmpnam 函數(shù)產(chǎn)生一個(gè)與現(xiàn)有文件名不同的一個(gè)有效路徑名字字符串。
? tmpfile 函數(shù)創(chuàng)建一個(gè)臨時(shí)二進(jìn)制文件(類型 wb+),在關(guān)閉該文件或程序結(jié)束時(shí)將自動(dòng)刪除這種文件。
14、內(nèi)存流
我們已經(jīng)看到,標(biāo)準(zhǔn) I/O庫(kù)把數(shù)據(jù)緩存在內(nèi)存中,因此每次一字符和每次一行的 I/O更有效。有的I/O都是通過在緩沖區(qū)主存之間來回傳送字節(jié)來完成的。
15、標(biāo)準(zhǔn) I/O的替代軟件
第 6 章- 系統(tǒng)數(shù)據(jù)文件和信息
2、口令文件
某些系統(tǒng)提供了 vipw 命令,允許管理員使用該命令編輯口令文件。vipw命令串行化地更改口令文件。
POSIX.1定義了兩個(gè)獲取口令文件項(xiàng)的函數(shù)。在給出用戶登錄名或數(shù)值用戶ID后,這兩個(gè)函數(shù)就能查看相關(guān)項(xiàng)目:getpwuid函數(shù)、getpwnam函數(shù)。
getpwuid: 由 ls(1)程序使用,它將i節(jié)點(diǎn)中的數(shù)字用戶ID映射為用戶登錄名。在鍵入登錄名后,getpwnam函數(shù)由login(1)程序使用。
3、陰影口令
為使別人難以獲得原始資料(加密口令),現(xiàn)在,某些系統(tǒng)將加密口令存放在另一個(gè)通常稱為陰影口令(shadow password)的文件中。該文件至少要包含用戶名和加密口令。
4、組文件
5、附屬組 ID
1983年左右,4.2 BSD( 伯克利軟件套件 )引入了附屬組ID的概念。這樣不僅可以屬于口令文件記錄項(xiàng)中組ID所對(duì)應(yīng)的組,也可以屬于多至15個(gè)另外的組。
6、實(shí)現(xiàn)區(qū)別
7、其它數(shù)據(jù)文件
8、登陸賬戶記錄
大多數(shù)時(shí)候UNIX系統(tǒng)都提供下列兩個(gè)數(shù)據(jù)文件:
utmp文件記錄當(dāng)前登陸到系統(tǒng)的各個(gè)用戶;
wtmp文件跟蹤各個(gè)登陸和注銷事件。
9、系統(tǒng)標(biāo)識(shí)
POSIX.1(可移植操作系統(tǒng)接口)定義了uname 函數(shù),它返回與主機(jī)和操作系統(tǒng)有關(guān)的信息。
10、時(shí)間和日期歷程
第7章-進(jìn)程環(huán)境
1、引言
本章將學(xué)習(xí):
當(dāng)程序執(zhí)行時(shí),其main函數(shù)是如何被調(diào)用的; 02
命令行參數(shù)是如何傳遞給新程序的; 04
典型的存儲(chǔ)空間布局是什么樣式; 06
如何分配另外的存儲(chǔ)空間; 08
進(jìn)程如何使用環(huán)境變量 09
進(jìn)程的各種不同終止方式; 03
longjmp 和 setjmp 函數(shù)以及它們與棧的交互作用。 10
2、 main函數(shù)
main 函數(shù)的原型是:
int main( int argc, char *argv[] )
其中 argc 是命令行參數(shù)的數(shù)目; argv是指向參數(shù)的各個(gè)指針?biāo)鶚?gòu)成的數(shù)組( 存放命令行每個(gè)字符串的首地址)。
在調(diào)用 mian 函數(shù)前先調(diào)用一個(gè)特殊的 啟動(dòng)例程??蓤?zhí)行程序文件將此啟動(dòng)例程指定為程序的起始地址——這是由 連接編輯器設(shè)置的,而連接編輯器則是由 C 編譯器調(diào)用。
3、進(jìn)程終止
有8種方式使進(jìn)程終止(termination):
5種正常終止:
? 從 main 返回;
? 調(diào)用 exit;
? 調(diào)用 _exit 或 _Exit;
? 最后一個(gè)線程從其啟動(dòng)歷程返回 (11.5節(jié))
? 從最后一個(gè)線程調(diào)用 pthread_exit; (11.5節(jié))
3種異常終止:
? 調(diào)用 abort (10.17節(jié))
? 接到一個(gè)信號(hào) (10.2節(jié))
? 最后一個(gè)線程對(duì)取消請(qǐng)求作出響應(yīng)(11.5節(jié)、12.7節(jié))
3.1- 退出函數(shù)
有 3 個(gè)函數(shù)可用于正常終止一個(gè)程序:
? _exit 和 _Exie 會(huì)立即進(jìn)入內(nèi)核;
? exit 則先執(zhí)行一些清理處理,然后返回內(nèi)核。
這3個(gè)退出函數(shù)都帶一個(gè)整形參數(shù),稱為 終止?fàn)顟B(tài)。
若 main 的返回類型是整形, 并且 main 執(zhí)行到最后一條語(yǔ)句時(shí)返回(隱式返回),那么該進(jìn)程的終止?fàn)顟B(tài)是 0。
于是: main函數(shù)中的 return 0; 等價(jià)于 exit(0);
3.2- 函數(shù) atexit
按照國(guó)際標(biāo)準(zhǔn)化組織的規(guī)定,一個(gè)進(jìn)程可以登記多至32個(gè)終止處理程序(exit handler)的函數(shù),這些函數(shù)將由 exit 自動(dòng)調(diào)用,系統(tǒng)同時(shí)調(diào)用 atexit 函數(shù)來登記這些函數(shù)。
4、命令行參數(shù)
當(dāng)執(zhí)行一個(gè)程序時(shí),調(diào)用 exec 的進(jìn)程可將命令行參數(shù)傳遞給該新程序。
5、環(huán)境表
每個(gè)程序都接收到一張環(huán)境表。
與參數(shù)表一樣,環(huán)境表也是一個(gè)字符指針,其中每個(gè)指針包含一個(gè)以 null 結(jié)束的 C 字符串的地址。
全局變量 環(huán)境指針( environ) 則包含了該指針數(shù)組的地址:
extern char **environ;
6、 C程序的存儲(chǔ)空間布局
歷史沿襲至今,C程序一直由下列幾部分組成:
? 正文段;
? 初始化數(shù)據(jù)段;
? 未初始化數(shù)據(jù)段;
? 棧;
? 堆。
7、共享庫(kù)
共享庫(kù)使得可執(zhí)行文件中不再需要包含公用的庫(kù)函數(shù),而只需在所有進(jìn)程都可引用的存儲(chǔ)區(qū)中保存這種庫(kù)例進(jìn)程的一個(gè)副本。
8、存儲(chǔ)空間分配
國(guó)際標(biāo)準(zhǔn)組織 說明了 3 個(gè)用于存儲(chǔ)空間動(dòng)態(tài)分配的函數(shù):
? malloc : 分配指定字節(jié)數(shù)的存儲(chǔ)區(qū);
? calloc : 為指定數(shù)量指定長(zhǎng)度的對(duì)象分配存儲(chǔ)空間;
? realloc : 增加或減少以前分配區(qū)的長(zhǎng)度。
9、環(huán)境變量
環(huán)境字符串的形式是:
name = value
國(guó)際標(biāo)準(zhǔn)化組織 C 定義了一個(gè)函數(shù) getenv, 可以用其取環(huán)境變量值,但是該標(biāo)準(zhǔn)又稱環(huán)境的內(nèi)容是由實(shí)現(xiàn)定義的。
10、函數(shù) setjmp 和 longjmp
在 C 中,goto 語(yǔ)句不能跨越函數(shù),而 setjmp 和 longjmp 函數(shù)可以。
這兩個(gè)函數(shù)對(duì)于處理發(fā)生在很深層嵌套函數(shù)調(diào)用中的出錯(cuò)情況是非常有用的。
11、函數(shù) getrlimit 和setrlimit
函數(shù) getrlimit 和 setrlimit 可以進(jìn)行查詢和更改資源限制。
對(duì)這兩個(gè)函數(shù)的每一次調(diào)用都指定一個(gè)資源以及一個(gè)指向下列結(jié)構(gòu)的指針。
12、小結(jié)
本章說明了:
? 一個(gè)進(jìn)程是如何啟動(dòng)和終止的;
? 如何向其傳遞參數(shù)表和環(huán)境;
? C 程序的典型存儲(chǔ)空間布局;
? 一個(gè)進(jìn)程如何動(dòng)態(tài)地分配和釋放存儲(chǔ)空間;
? setjmp 和 longjmp 函數(shù),他們提供了一種在進(jìn)程內(nèi)非局部轉(zhuǎn)移的方法。
第 8 章-進(jìn)程控制
1、引言
本章介紹 UNIX 系統(tǒng)的進(jìn)程控制,包括:創(chuàng)建新進(jìn)程、執(zhí)行程序和進(jìn)程終止。
還將說明進(jìn)程屬性的各種 ID ——實(shí)際、有效和保存的用戶 ID 和組ID ,以及它們?nèi)绾问艿竭M(jìn)程控制原語(yǔ)的影響。
2、進(jìn)程標(biāo)識(shí)
每個(gè)進(jìn)程都有一個(gè)非負(fù)整數(shù)表示的唯一進(jìn)程 ID 。P.S. 雖然進(jìn)程 ID 是唯一的,但是當(dāng)一個(gè)進(jìn)程終止后,其進(jìn)程 ID 就稱為復(fù)用的候選者。
系統(tǒng)中有一些專用進(jìn)程 ID:
? ID 為 0 的進(jìn)程通常是調(diào)度進(jìn)程,也被稱為 交換進(jìn)程(swapper) ,它并不執(zhí)行任何磁盤上的程序,因此也被稱為系統(tǒng)進(jìn)程。
? ID 為 1 通常是 init 進(jìn)程,次進(jìn)程負(fù)責(zé)在自舉內(nèi)核后啟動(dòng)一個(gè) UNIX 系統(tǒng)。
? ID 為 2 是頁(yè)守護(hù)進(jìn)程( page daemon),此進(jìn)程負(fù)責(zé)支持虛擬存儲(chǔ)器系統(tǒng)的分頁(yè)操作。
3、函數(shù) fork
fork() 系統(tǒng)調(diào)用通過復(fù)制一個(gè)現(xiàn)有進(jìn)程來創(chuàng)建一個(gè)全新的進(jìn)程(進(jìn)程的另外一個(gè)名字叫做任務(wù))。
由 fork 創(chuàng)建的新進(jìn)程被稱為 子進(jìn)程(child process)
fork 函數(shù)被調(diào)用一次,但返回兩次。兩次返回的區(qū)別是子進(jìn)程的返回值是 0 ,而父進(jìn)程的返回值則是新建子進(jìn)程的進(jìn)程 ID。
子進(jìn)程是父進(jìn)程的副本。eg. 子進(jìn)程獲得父進(jìn)程數(shù)據(jù)空間、堆和棧的副本,父進(jìn)程和子進(jìn)程并不共享這些存儲(chǔ)空間。
4、函數(shù) vfork
vfork 函數(shù)的調(diào)用序列和返回值與 fork 相同,但 兩者 的語(yǔ)義不同。
vfork 函數(shù)用于創(chuàng)建一個(gè)新進(jìn)程,而該新進(jìn)程的目的是執(zhí)行一個(gè)新程序。但是 vfork 和 fork還是有 2 點(diǎn)不同的(書 p .187)
5、函數(shù) exit
詳細(xì)介紹了 5 中正常終止及 3 種異常終止的方式。
終止函數(shù): exit 、 _exit、 _Exit,它們終止后,會(huì)給父進(jìn)程傳遞退出狀態(tài)( exit status )的參數(shù)。
僵死進(jìn)程: 一個(gè)已經(jīng)終止、但是其父進(jìn)程尚未對(duì)其進(jìn)行善后處理 (獲取終止子進(jìn)程的有關(guān)信息、釋放它仍占用的資源)的進(jìn)程被稱為僵死進(jìn)程。
6、 函數(shù) wait 和 waitpid
當(dāng)一個(gè)進(jìn)程正?;虍惓=K止時(shí),內(nèi)核就向其父進(jìn)程發(fā)送 SIGCHLD 信號(hào)。
一個(gè)進(jìn)程有多個(gè)子進(jìn)程,只要有一個(gè)子進(jìn)程終止, wait 就返回。
大家知道,當(dāng)用fork啟動(dòng)一個(gè)新的子進(jìn)程的時(shí)候,子進(jìn)程就有了新的生命周期,并將在其自己的地址空間內(nèi)獨(dú)立運(yùn)行。但有的時(shí)候,我們希望知道某一個(gè)自己創(chuàng)建的子進(jìn)程何時(shí)結(jié)束,從而方便父進(jìn)程做一些處理動(dòng)作。同樣的,在用 ptrace 去 attach 一個(gè)進(jìn)程滯后,那個(gè)被 attach 的進(jìn)程某種意義上說可以算作那個(gè) attach 它進(jìn)程的子進(jìn)程,這種情況下,有時(shí)候就想知道被調(diào)試的進(jìn)程何時(shí)停止運(yùn)行。
以上兩種情況下,都可以使用 Linux 中的 waitpid() 函數(shù)做到。先來看看waitpid函數(shù)的定義:
如果在調(diào)用waitpid()函數(shù)時(shí),當(dāng)指定等待的子進(jìn)程已經(jīng)停止運(yùn)行或結(jié)束了,則waitpid()會(huì)立即返回;但是如果子進(jìn)程還沒有停止運(yùn)行或結(jié)束,則調(diào)用waitpid()函數(shù)的父進(jìn)程則會(huì)被阻塞,暫停運(yùn)行。
調(diào)用 wait 或 waitpid 的進(jìn)程可能會(huì)放生什么?
? 如果其所有子進(jìn)程都還在運(yùn)行,則阻塞;
? 如果一個(gè)子進(jìn)程已終止,正等待父進(jìn)程獲得其終止?fàn)顟B(tài),則取得該子進(jìn)程的終止?fàn)顟B(tài)立即返回;
? 如果它沒有任何子進(jìn)程,則立即出錯(cuò)返回。
? waitpod 函數(shù)提供了 wait 函數(shù)沒有提供的 3 個(gè)功能:
7、函數(shù) waitid
UNIX 包含了另一個(gè)取得進(jìn)程終止?fàn)顟B(tài)的函數(shù)—— waitid , 此函數(shù)類似于 waitpid,但提供了更多的靈活性。
waitid 函數(shù)允許一個(gè)進(jìn)程指定要等待的子進(jìn)程。
8、函數(shù) wait3 和 wait4
大多數(shù) UNIX 系統(tǒng)實(shí)現(xiàn)提供了另外兩個(gè)函數(shù) wait3 和 wait4。
相較于函數(shù) wait 、 waitpid 、waitid 所提供的功能要多一個(gè),這與附加參數(shù)有關(guān)。該參數(shù)允許內(nèi)核返回由終止進(jìn)程及其所有子進(jìn)程使用的資源概況,包括: CPU時(shí)間總量、系統(tǒng)CPU時(shí)間總量、缺頁(yè)次數(shù)、接收到信號(hào)的次數(shù)等。
9、競(jìng)爭(zhēng)條件
當(dāng)多個(gè)進(jìn)程都企圖對(duì)共享數(shù)據(jù)進(jìn)行某種處理,而最后的結(jié)果又取決于進(jìn)程運(yùn)行的順序時(shí),我們認(rèn)為發(fā)生了競(jìng)爭(zhēng)關(guān)系( race condition )。
10、函數(shù) exec
在 “3-函數(shù) fork” 中提到:用 fork 函數(shù)創(chuàng)建新的子進(jìn)程后,子進(jìn)程往往要調(diào)用一種 exec 函數(shù)以執(zhí)行另一個(gè)程序。
當(dāng)進(jìn)程 調(diào)用一種 exec 函數(shù)時(shí),該進(jìn)程執(zhí)行的程序完全替換為 新程序,而新程序是從其 main 函數(shù)開始執(zhí)行。
因?yàn)檎{(diào)用 exec 并不創(chuàng)建新進(jìn)程,所以前后的進(jìn)程 ID 并未改變。 exec 只是用磁盤上的一個(gè)新程序替換了當(dāng)前進(jìn)程的正文段、數(shù)據(jù)段、堆段和棧段。
11、更改用戶 ID 和 更改組 ID
在 UNIX 系統(tǒng)中,特權(quán)(如能改變當(dāng)前日期的表示法)以及訪問控制(如能否讀、寫一個(gè)特定文件),是基于用戶 ID 和 組 ID 的。
當(dāng)程序需要增加特權(quán),或需要訪問當(dāng)前不允許訪問的資源時(shí),我們需要更換自己的用戶 ID 或 組 ID,使得新 ID 具有合適的特權(quán)或訪問權(quán)限。當(dāng)程序需要降低其特權(quán)或阻止對(duì)某些資源的訪問時(shí),也需要更換用戶 ID 或組 ID,新 ID 不具有相應(yīng)特權(quán)或訪問這些資源的能力。
12、解釋器文件
解釋器文件是一種文本文件。
13、函數(shù) system
ISO C定義了 system函數(shù),它總共有 3 種返回值。
相較于 fork 、 exec, system的優(yōu)點(diǎn)為: system進(jìn)行了所需的各種出錯(cuò)處理以及各種信號(hào)處理。
14、 進(jìn)程會(huì)計(jì)
大多數(shù) UNIX 系統(tǒng)提供了一個(gè)選項(xiàng)以進(jìn)行 進(jìn)程會(huì)計(jì)( process accounting )處理。
啟用該選項(xiàng)后,每當(dāng)進(jìn)程結(jié)束時(shí)內(nèi)核就寫一個(gè)會(huì)計(jì)記錄。一般進(jìn)程會(huì)計(jì)包括: 命令名、所使用的 CPU 時(shí)間總量、用戶 ID 和 組 ID 、啟動(dòng)時(shí)間等。
15、用戶標(biāo)識(shí)
任一進(jìn)程都可以得到其實(shí)際用戶 ID 和有效用戶 ID 及組 ID。
但是,有時(shí)候我們希望找到運(yùn)行該程序用戶的登陸名。系統(tǒng)通常記錄用戶登陸時(shí)使用的名字,然后用 getlogin 函數(shù)可以獲取此登錄名。
如果調(diào)用此函數(shù)的進(jìn)程沒有連接到用戶登陸時(shí)所用的終端,則函數(shù)失敗。通常稱這些進(jìn)程為 守護(hù)進(jìn)程( daemon )
16、 進(jìn)程調(diào)度
調(diào)度策略和調(diào)度優(yōu)先級(jí)是有內(nèi)核確定的。
進(jìn)程可以通過調(diào)整 友好值 選擇以更優(yōu)先級(jí)運(yùn)行(通過調(diào)整友好值降低它對(duì) CPU 的占有,因此該進(jìn)程是有好的)。只有特權(quán)進(jìn)程允許提高調(diào)度權(quán)限。
友好值越小,優(yōu)先級(jí)越高(越靠前嘛!)。
進(jìn)程可以通過 nice 函數(shù)獲取或更改它的友好值。使用這個(gè)函數(shù),進(jìn)程只能影響自己的友好值,不能影響任何其它進(jìn)程的友好值。
17、進(jìn)程時(shí)間
在 書 p16中說明了可以度量的 3 個(gè)時(shí)間:墻上時(shí)鐘時(shí)間、用戶 CPU時(shí)間 和 系統(tǒng) CPU時(shí)間。
任一進(jìn)程都可以調(diào)用 times 函數(shù)獲得它自己以及終止子進(jìn)程的上述值。
18、小結(jié)
UNIX 必須掌握的幾個(gè)函數(shù): fork、exec系列、 _exit、wait、waitpid。
? fork:用來創(chuàng)建新的進(jìn)程;
? exec:用 fork 函數(shù)創(chuàng)建新的子進(jìn)程后,子進(jìn)程往往要調(diào)用一種 exec 函數(shù)才能執(zhí)行另一個(gè)程序;
? _exit:給父進(jìn)程傳遞退出狀態(tài)( exit status )的參數(shù);
? wait、waitpid:我們希望知道某一個(gè)自己創(chuàng)建的子進(jìn)程何時(shí)結(jié)束;
? 本章說明了 system函數(shù)和進(jìn)程會(huì)計(jì),這也使我們進(jìn)一步了解所有這些進(jìn)程控制函數(shù)。
system進(jìn)行了所需的各種出錯(cuò)處理以及各種信號(hào)處理
? 本章還說明了 exec函數(shù)的另一種變體:解釋器文件及它們的工作方式。
? 對(duì)各種不同的用戶 ID 和組 ID(實(shí)際、有效 和 保存的)的理解,對(duì)編寫安全的設(shè)置用戶 ID 程序是至關(guān)重要的。
在 UNIX 系統(tǒng)中,特權(quán)(如能改變當(dāng)前日期的表示法)以及訪問控制(如能否讀、寫一個(gè)特定文件),是基于用戶 ID 和 組 ID 的。
當(dāng)程序需要增加特權(quán),或需要訪問當(dāng)前不允許訪問的資源時(shí),我們需要更換自己的用戶 ID 或 組 ID,使得新 ID 具有合適的特權(quán)或訪問權(quán)限。當(dāng)程序需要降低其特權(quán)或阻止對(duì)某些資源的訪問時(shí),也需要更換用戶 ID 或組 ID,新 ID 不具有相應(yīng)特權(quán)或訪問這些資源的能力。
第 9 章-進(jìn)程關(guān)系
1、引言
本章將詳細(xì)地說明進(jìn)程組 以及 POSIX.1(可一只操作系統(tǒng)接口)引入的會(huì)話的概念。還將介紹登陸 shell(登錄時(shí)所調(diào)用的)和所有從登陸 shell 啟動(dòng)的進(jìn)程之間的關(guān)系。
2、終端登陸
系統(tǒng)經(jīng)由內(nèi)核中的終端設(shè)備驅(qū)動(dòng)程序。
4 種終端登陸方式:
? BSD 終端登陸 ( Berkeley Software Distribution 伯克利軟件套件)
? Mac OS X 終端登陸
? Linux 終端登陸
? Solaris 終端登錄
3、網(wǎng)絡(luò)登陸
通過串行終端登錄至系統(tǒng)和經(jīng)由網(wǎng)絡(luò)登陸至系統(tǒng)兩者之間主要(物理上的)區(qū)別是:網(wǎng)絡(luò)登錄時(shí),在終端和計(jì)算機(jī)之間的連接不再是點(diǎn)到點(diǎn)的。在網(wǎng)絡(luò)登陸情況下, login僅僅是一種可用的服務(wù),這與其它網(wǎng)絡(luò)服務(wù)(如 FTP 或 SMTP)的性質(zhì)相同。
在 9.2 的終端登錄中, init 知道哪些終端設(shè)備可用來進(jìn)行登陸,并為每個(gè)設(shè)備生成一個(gè) getty 進(jìn)程。但是,對(duì)網(wǎng)絡(luò)登陸情況則有所不同,所有登陸都經(jīng)由內(nèi)核的網(wǎng)絡(luò)接口驅(qū)動(dòng)程序(如 以太網(wǎng)驅(qū)動(dòng)程序 )。
為使同一個(gè)軟件既能處理終端設(shè)備,又能處理網(wǎng)絡(luò)登陸,系統(tǒng)使用了一種稱為 偽終端(pseudo terminal )的軟件驅(qū)動(dòng)程序,它仿真串行終端的運(yùn)行行為,并將終端操作映射為網(wǎng)絡(luò)操作。
有以下四種登陸方式:
? BSD 網(wǎng)絡(luò)登陸
? Mac OS X 網(wǎng)絡(luò)登陸
? Linux 網(wǎng)絡(luò)登錄
? Solaris 網(wǎng)絡(luò)登陸
4、進(jìn)程組
每個(gè)進(jìn)程除了有一個(gè)進(jìn)程 ID 之外,還屬于一個(gè)進(jìn)程組。進(jìn)程組是一個(gè)或多個(gè)進(jìn)程的集合。
每個(gè)進(jìn)程組有一個(gè)唯一的進(jìn)程組 ID,進(jìn)程組 ID 類似于進(jìn)程 ID ——它是一個(gè)正整數(shù),并可存放在 pid_t 數(shù)據(jù)類型中。
進(jìn)程調(diào)用 setpgid 函數(shù) 可以加入一個(gè)現(xiàn)有的進(jìn)程組或者創(chuàng)建一個(gè)新進(jìn)程組。
#include <unistd.h>
int setpgid( pid_t pid, pid_t pgid);
//返回值:若成功,返回0; 若出錯(cuò),返回 -1
setpgid 函數(shù) 將 pid 進(jìn)程的進(jìn)程組 ID 設(shè)置為 pgid。
一個(gè)進(jìn)程只能為它自己或它的子進(jìn)程設(shè)置進(jìn)程組 ID。 在它的子進(jìn)程調(diào)用了 exec 后,它就不再改該子進(jìn)程的進(jìn)程組 ID。
5、會(huì)話
會(huì)話( session ) 是一個(gè)或多個(gè)進(jìn)程組的集合。
通常是由 shell 的管道將幾個(gè)進(jìn)程編成一組的。
進(jìn)程調(diào)用 setsid 函數(shù)建立一個(gè)新會(huì)話。
#include <unistd.h>
pid_t setsid(void);
//返回值:若成功,返回進(jìn)程組 ID; 若出錯(cuò),返回 -1
? 如果該調(diào)用進(jìn)程已經(jīng)是一個(gè)進(jìn)程組的組長(zhǎng),則此函數(shù)返回出錯(cuò)(有具體解決方法);
? 如果調(diào)用此函數(shù)的進(jìn)程不是一個(gè)進(jìn)程組的組長(zhǎng),則此函數(shù)創(chuàng)建一個(gè)新會(huì)話。(具體會(huì)發(fā)生 3 件事)
6、控制終端
會(huì)話 和 進(jìn)程組還有一些其它特性:
? 一個(gè)會(huì)話可以有一個(gè)控制終端( controlling terminal )。這通常是終端設(shè)備(在終端登錄情況下)或偽終端設(shè)備(在網(wǎng)絡(luò)登陸情況下)。
? 建立與控制終端連接的會(huì)話首進(jìn)程被稱為 控制進(jìn)程( controlling process )
? 一個(gè)會(huì)話的幾個(gè)進(jìn)程組可被分成一個(gè) 前臺(tái)進(jìn)程組( foreground process group )以及一個(gè)或多個(gè) 后臺(tái)進(jìn)程組( background process group )。
? 如果一個(gè)會(huì)話有一個(gè)控制終端,則它有一個(gè)前臺(tái)進(jìn)程組,其他進(jìn)程組稱為后臺(tái)進(jìn)程組。
7、函數(shù) tcgetpgrp、 tcsetpgrp 和 tcgetsid
有時(shí)候需要有一種方法來通知內(nèi)核哪一個(gè)進(jìn)程組是前臺(tái)進(jìn)程組,這樣,終端設(shè)備驅(qū)動(dòng)程序就能知道將終端輸入和終端產(chǎn)生的信號(hào)發(fā)往何處。
函數(shù) tcgetpgrp 返回前臺(tái)進(jìn)程組 ID,它與在 fd 上打開的終端相關(guān)聯(lián)。
如果進(jìn)程有一個(gè)控制終端,則該進(jìn)程可以調(diào)用 tcsetpgrp 將前臺(tái)進(jìn)程組 ID 設(shè)置為 pgrpid。
大多數(shù)應(yīng)用程序并不直接調(diào)用這兩個(gè)函數(shù)。他們通常由 作業(yè)控制 shell調(diào)用。
需要管理控制終端的應(yīng)用程序可以調(diào)用 tcgetsid 函數(shù)識(shí)別出控制終端的會(huì)話首選進(jìn)程的 會(huì)話ID (它等價(jià)于會(huì)話首進(jìn)程的進(jìn)程組 ID)
8、作業(yè)控制
作業(yè)控制是 BSD 在 1980年左右增加的一個(gè)新特性。它允許在一個(gè)終端是啟動(dòng)多個(gè)作業(yè)(進(jìn)程組),它控制哪一個(gè)作業(yè)可以訪問該終端以及哪些作業(yè)再后臺(tái)運(yùn)行。
一個(gè)作業(yè)只是幾個(gè)進(jìn)程的集合,通常是一個(gè)進(jìn)程管道。
作業(yè)控制要求一下 3 種形式的支持:
9、 shell 執(zhí)行程序
PID: (Process ID) 進(jìn)程 ID號(hào)
PPID: (Parent process) ID 父進(jìn)程 ID號(hào)
PGID: (Process Group ID) 進(jìn)程組 ID號(hào)
SID: (Session ID) 會(huì)話ID
10、孤兒進(jìn)程組
一個(gè)其父進(jìn)程已終止的進(jìn)程稱為 孤兒進(jìn)程(orphan process ),這種進(jìn)程由 init 進(jìn)程“收養(yǎng)”。
孤兒進(jìn)程組:( orphaned process group ):
? 該組中每個(gè)成員的父進(jìn)程要么是該組的一個(gè)成員,要么不是該組所屬會(huì)話的成員。
? (另一種描述)一個(gè)進(jìn)程組不是孤兒進(jìn)程組的條件是——該組中有一個(gè)進(jìn)程,其父進(jìn)程在屬于同一會(huì)話的另一個(gè)組中。如果進(jìn)程組不是孤兒進(jìn)程組,那么在屬于同一會(huì)話的另一個(gè)組中的父進(jìn)程就有機(jī)會(huì)重新啟動(dòng)該組中停止的進(jìn)程。
11、 FreeBSD 實(shí)現(xiàn)
12、小結(jié)
第 10 章- 信號(hào)
1、引言
本章先對(duì)信號(hào)機(jī)制進(jìn)行綜述,并說明每種信號(hào)的一般用法。
然后分析早期實(shí)現(xiàn)的問題。
在分析存在的問題之后再說明解決這些問題的方法,這種安排有助于加深對(duì)改進(jìn)機(jī)制的理解。
2、信號(hào)概念
定義:
信號(hào)更多的是通知事件的發(fā)生。信號(hào)產(chǎn)生之后第一時(shí)間也不是直接處理而是先存儲(chǔ)下來。
流程:
信號(hào)的產(chǎn)生—>信號(hào)的注冊(cè)—>信號(hào)的阻塞(不處理)—>信號(hào)的注銷—>信號(hào)的處理。
分類:
① 不可靠信號(hào)(非實(shí)時(shí)信號(hào)):1~31
② 可靠信號(hào)(實(shí)時(shí)信號(hào)):34~64
Linux下有62種信號(hào),使用kill -l 命令查看
每個(gè)信號(hào)都有一個(gè)名字。這些名字都以 3 個(gè)字符 SIG 開頭。
e.g. SIGABRT 是夭折信號(hào),當(dāng)進(jìn)程調(diào)用 about 函數(shù)時(shí)產(chǎn)生這種信號(hào);
SIGALRM 是鬧鐘信號(hào),由 alarm 函數(shù)設(shè)置的定時(shí)器超時(shí)后將產(chǎn)生此信號(hào);
不存在編號(hào)為 0 的信號(hào),因?yàn)?#xff08;10.9節(jié))kill 函數(shù)對(duì)信號(hào)編號(hào) 0 有特殊的應(yīng)用。
3、函數(shù) signal
UNIX 系統(tǒng)信號(hào)機(jī)制最簡(jiǎn)單的接口是 signal 函數(shù)。
當(dāng)指定函數(shù)地址時(shí),則在信號(hào)發(fā)生時(shí),調(diào)用該函數(shù),我們稱這種處理為捕捉該信號(hào),稱此函數(shù)為信號(hào)處理程序( signal handler )或信號(hào)捕捉函數(shù)( signal-catching function )。
signal 函數(shù)原型說明此函數(shù)要求兩個(gè)參數(shù),返回一個(gè)函數(shù)指針,而該指針?biāo)赶虻暮瘮?shù)無返回值( void )。
? 程序啟動(dòng)
當(dāng)執(zhí)行一個(gè)程序時(shí),所有信號(hào)的狀態(tài)都是系統(tǒng)默認(rèn)或忽略。
? 進(jìn)程創(chuàng)建
當(dāng)一個(gè)進(jìn)程調(diào)用 fork 時(shí),其子進(jìn)程繼承父進(jìn)程的信號(hào)處理方式。
4、不可靠的信號(hào)
不可靠在這里指的是:信號(hào)可能會(huì)丟失。
5、終端的系統(tǒng)調(diào)用
早期 UNIX 系統(tǒng)的一個(gè)特性是: 如果進(jìn)程在執(zhí)行一個(gè)低速系統(tǒng)調(diào)用而阻塞期間捕捉到一個(gè)信號(hào),則該系統(tǒng)調(diào)用就被終端不再繼續(xù)執(zhí)行。
該系統(tǒng)調(diào)回返回出錯(cuò),其 error 設(shè)置為 EINTR。
6、可重入函數(shù)
7、 SIGCLD語(yǔ)義
8、可靠信號(hào)術(shù)語(yǔ)和語(yǔ)義
每個(gè)進(jìn)程都有一個(gè) 信號(hào)屏蔽字( signal mask ),它規(guī)定了當(dāng)前要阻塞遞送到該進(jìn)程的信號(hào)集。
對(duì)于每種可能的信號(hào),該屏蔽字中都有一位與之對(duì)應(yīng),對(duì)于某種信號(hào),若其對(duì)應(yīng)位已設(shè)置,則它當(dāng)前是被阻塞的。進(jìn)程可以調(diào)用 sigprocmask(10.2節(jié))來檢測(cè)和更改其當(dāng)前信號(hào)屏蔽字。
9、函數(shù) kill 和 raise
kill 函數(shù)將信號(hào)發(fā)送給進(jìn)程或進(jìn)程組; raise 函數(shù)則允許進(jìn)程向自身發(fā)送信號(hào)。
如前所述,進(jìn)程將信號(hào)發(fā)送給其它進(jìn)程需要權(quán)限。超級(jí)用戶可將信號(hào)發(fā)送給任一進(jìn)程。對(duì)于非超級(jí)用戶,其基本規(guī)則是發(fā)送者的實(shí)際用戶 ID 或有效用戶 ID 必須等于接受者的實(shí)際用戶 ID 或有效用戶 ID。
10、函數(shù) alarm 和 pause
使用 alarm 函數(shù)可以設(shè)置一個(gè)定時(shí)器(鬧鐘時(shí)間),在將來的某個(gè)時(shí)刻該定時(shí)器會(huì)超時(shí)。當(dāng)定時(shí)器超時(shí)時(shí),產(chǎn)生 SIGALRM 信號(hào)。
每個(gè)進(jìn)程只能有一個(gè)鬧鐘時(shí)間。
pause 函數(shù)使調(diào)用者進(jìn)程掛起直至捕捉到一個(gè)信號(hào)。
只有執(zhí)行了一個(gè)信號(hào)處理程序并從其返回時(shí), pause才返回。在這種情況下, pause 返回 -1,errno設(shè)置為 EINTR。
11、信號(hào)集
數(shù)據(jù)集 用來告訴內(nèi)核不允許發(fā)生該信號(hào)集中的信號(hào)。
函數(shù) sigemptyset 初始化由 set 指向的信號(hào)集,清除其中所有信號(hào)。 函數(shù) sigfillset 初始化由 set 指向的信號(hào)集,使其包括所有信號(hào)。所有應(yīng)用程序在使用信號(hào)集前,要對(duì)該信號(hào)集調(diào)用 sigemptyset 或 sigfillset 一次。這是因?yàn)?C 編譯器程序?qū)?不賦初值的 外部變量和靜態(tài)變量都初始化為 0。
12、 函數(shù) sigprocmask
在(10.8)中提及一個(gè)進(jìn)程的信號(hào)屏蔽字規(guī)定了當(dāng)前阻塞而不能遞送給該進(jìn)程的信號(hào)集。調(diào)用函數(shù) sigprocmask 可以檢測(cè)或更改,或同時(shí)進(jìn)行檢測(cè)和更改進(jìn)程的信號(hào)屏蔽字。
13、函數(shù) sigpending
sigpending 函數(shù)返回一信號(hào)集,對(duì)于調(diào)用進(jìn)程而言,其中的各信號(hào)是阻塞不能遞送的,因而也一定是當(dāng)前未決的。 該信號(hào)集通過 set 參數(shù)返回。
14、函數(shù) sigaction
sigaction 函數(shù)的功能是檢查或修改(或檢查并修改)與指定信號(hào)相關(guān)聯(lián)的處理動(dòng)作。此函數(shù)取代了 UNIX 早起版本使用的 signal 函數(shù)。
15、 函數(shù) sigsetjmp 和 siglongjmp
16、函數(shù) sigsuspend
更改進(jìn)程的信號(hào)屏蔽字可以阻塞所選擇的信號(hào),或解除對(duì)它們的阻塞。使用這種技術(shù)可以保護(hù)不希望由信號(hào)中斷的代碼臨界區(qū)。
17、 函數(shù) abort
abort 函數(shù)的功能是使 程序異常終止。
該函數(shù)將 SIGABRT信號(hào)發(fā)送給調(diào)用進(jìn)程(進(jìn)程不應(yīng)忽略此信號(hào))。 ISO C 規(guī)定,調(diào)用 abort將向主機(jī)環(huán)境遞送一個(gè)未成功終止的通知,其方法是調(diào)用 raise(SIGABRT) 函數(shù)。
18、函數(shù) system
POSIX.1 要求 system 忽略 SIGINT 和 SIGQUIT ,阻塞 SIGCHLD。
system 的返回值
注意 system 的返回值,它是 shell 的終止?fàn)顟B(tài),但 shell 的終止?fàn)顟B(tài)并不總是執(zhí)行命令字符串進(jìn)程的終止?fàn)顟B(tài)。
19、函數(shù) sleep、nanosleep 和 clock_nanosleep
這本書中很多例子中都已經(jīng)使用了 sleep 函數(shù)。
nanosleep 函數(shù)與 sleep函數(shù)類似,但是它提供了納秒級(jí)的精度。這個(gè)函數(shù)掛起調(diào)用進(jìn)程,知道要求的時(shí)間已經(jīng)超時(shí)或者某個(gè)信號(hào)中斷了該函數(shù)。
20、函數(shù) sigqueue
除了對(duì)信號(hào)排隊(duì)以外,這些擴(kuò)展允許應(yīng)用程序在遞交信號(hào)時(shí)傳遞更多的信息。這些信息嵌入在 siginfo 結(jié)構(gòu)中。除了系統(tǒng)提供的信息,應(yīng)用程序還可以想信號(hào)處理程序傳遞整數(shù)或者指向包含更多信息的緩沖區(qū)指針。
sigqueue 函數(shù)只能吧信號(hào)發(fā)送給單個(gè)進(jìn)程,可以使用 value 參數(shù)想信號(hào)處理程序傳遞整數(shù)和指針值,除此之外, sigqueue 函數(shù)與 kill 函數(shù)類似。
21、作業(yè)控制信號(hào)
POSIX.1 認(rèn)為有以下 6 個(gè)與作業(yè)控制有關(guān):
除了 SIGCHLD 以外,大多數(shù)應(yīng)用程序并不處理這些信息,交互式 shell 則通常會(huì)處理這些信號(hào)的所有工作。
當(dāng)我們通知 shell 在前臺(tái)或后臺(tái)回復(fù)運(yùn)行一個(gè)作業(yè)時(shí), shell 向該作業(yè)中的所有進(jìn)程發(fā)送 SIGCONT 信號(hào)。
22、信號(hào)名和編號(hào)
可以使用 psignal 函數(shù)可移植地打印與信號(hào)編號(hào)對(duì)應(yīng)的字符串。
如果在 sigaction 信號(hào)處理程序中有 siginfo 結(jié)構(gòu),可以使用 psiginfo 函數(shù)打印信號(hào)信息。
23、小結(jié)
本章 說明了早起信號(hào)實(shí)現(xiàn)的問題以及它們是如何顯現(xiàn)出來的。
然后介紹了 POSIX.1 的可靠信號(hào)概念,以及所有相關(guān)的函數(shù)。
在此基礎(chǔ)上提供了 about、system 和 sleep 函數(shù)的 POSIX.1實(shí)現(xiàn)。
最后以觀察分析作業(yè)控制信號(hào)以及信號(hào)名和信號(hào)編號(hào)之間的轉(zhuǎn)換結(jié)束。
第 11 章-線程
1、引言
本章了解如何使用多個(gè)控制線程( 簡(jiǎn)單說就是線程 )在單進(jìn)程中執(zhí)行多個(gè)任務(wù)。一個(gè)進(jìn)程中的所有線程都可以訪問該進(jìn)程的組成部件,如文件描述符和內(nèi)存。
最后本章將討論目前可用的同步機(jī)制,防止多個(gè)線程在共享資源時(shí)出現(xiàn)不一致的問題。
有時(shí),也可以將應(yīng)用程序設(shè)計(jì)成使用多線程的(11章),從而避免使用非阻塞 I/O(14章)。
2、線程概念
典型的 UNIX 進(jìn)程可以看成只有一個(gè)控制線程:一個(gè)進(jìn)程在某一時(shí)刻只能做一件事情。有了多個(gè)控制線程以后,在程序設(shè)計(jì)時(shí)就可以把進(jìn)程設(shè)計(jì)成在某一時(shí)刻能夠不止做一件事,每個(gè)線程處理各自獨(dú)立的任務(wù)。
每個(gè)線程都包含有表示執(zhí)行環(huán)境所必須的信息,其中包括進(jìn)程中標(biāo)識(shí)線程的 線程ID、一組寄存器值、棧、調(diào)度優(yōu)先級(jí)和策略、信號(hào)屏蔽字、 errno變量(1.7節(jié))以及線程私有數(shù)據(jù)(12.6節(jié))。
一個(gè)進(jìn)程的所有信息對(duì)該進(jìn)程的所有線程都是共享的,包括可執(zhí)行程序的代碼、程序的全局內(nèi)存和堆內(nèi)存、棧以及文件描述符。
3、進(jìn)程標(biāo)識(shí)
就像每個(gè)進(jìn)程有一個(gè)進(jìn)程 ID 一樣,每個(gè)線程也有一個(gè)線程 ID。進(jìn)程 ID 在整個(gè)系統(tǒng)中是唯一的,但 線程 ID 不同,線程 ID 只有在它所屬的進(jìn)程上下文中才有意義。
線程 ID 是用 pthread_t數(shù)據(jù)類型來表示的。 用結(jié)構(gòu)表示 pthread_t 數(shù)據(jù)類型的后果是不能用一種可以只的方式打印該數(shù)據(jù)類型的值。在程序調(diào)試過程中打印線程 ID 有時(shí)是非常有用的。
線程可以通過調(diào)用 pthread_self函數(shù)獲得自身的線程 ID。
pthread_self函數(shù)可以與 pthread_equal 函數(shù)一起使用。例如,主線程可能把工作任務(wù)放在一個(gè)隊(duì)列中,用線程 ID 來控制每個(gè)工作線程處理哪些作業(yè)。
4、 線程創(chuàng)建
在傳統(tǒng) UNIX 進(jìn)程模式中,每個(gè)進(jìn)程只有一個(gè)控制線程。
新增的線程可以通過調(diào)用 pthread_create 函數(shù)創(chuàng)建(注,它不是系統(tǒng)默認(rèn)的庫(kù),需要在編譯的時(shí)候加上 -lpthread )。
線程創(chuàng)建時(shí)并不能保證哪個(gè)線程會(huì)先運(yùn)行:是新創(chuàng)建的線程,還是調(diào)用線程。
5、進(jìn)程終止
如果進(jìn)程中的任意線程調(diào)用了 exit、 _Exit 或者 _exit,那么整個(gè)進(jìn)程就會(huì)終止。與此相類似,如果默認(rèn)的動(dòng)作是終止進(jìn)程,那么,發(fā)送到線程的信息就會(huì)終止整個(gè)進(jìn)程。
單個(gè)線程可以通過 3 種方式退出,因此可以在不終止整個(gè)進(jìn)程的情況下,停止它的控制流:
?
?
? 線程調(diào)用 pthread_exit
進(jìn)程中的其它線程也可以通過調(diào)用 函數(shù) pthread_join 訪問到其它線程指針。 pthread_join 函數(shù)可以等待指定的線程終止,但并不獲取線程的終止?fàn)顟B(tài)。
當(dāng)一個(gè)線程通過調(diào)用 函數(shù)pthread_exit 退出或者簡(jiǎn)單地從啟動(dòng)歷程中返回時(shí),進(jìn)程中的其它線程可以通過調(diào)用 pthread_join 函數(shù)獲得該線程的退出狀態(tài)。
線程可以通過調(diào)用 pthread_cancel 函數(shù)來請(qǐng)求取消同一進(jìn)程中的其它線程?!咀ⅰ吭摵瘮?shù)僅僅是提出請(qǐng)求。
線程可以安排它退出時(shí)需要調(diào)用的函數(shù),這與進(jìn)程在退出時(shí)可以用 atexit函數(shù) 安排退出是類似的。這樣的函數(shù)稱為 線程清理處理程序( thread cleanup handler )。一個(gè)線程可以建立多個(gè)清理處理程序。
6、線程同步
當(dāng)多個(gè)控制線程共享相同的內(nèi)存時(shí),需要確保每個(gè)線程看到一致的數(shù)據(jù)視圖。
當(dāng)一個(gè)線程可以修改的變量,其它線程也可以讀取或者修改的時(shí)候,我們就需要對(duì)這些線程進(jìn)行同步,確保它們?cè)谠L問變量的存儲(chǔ)內(nèi)容時(shí)不會(huì)訪問到無效的值。
在變量修改時(shí)間多于一個(gè)存儲(chǔ)器訪問周期的處理器結(jié)構(gòu)中,當(dāng)存儲(chǔ)器讀與存儲(chǔ)器寫這兩個(gè)周期交叉時(shí),這種不一致就會(huì)出現(xiàn)。
為了解決這個(gè)問題,線程不得不使用 鎖 ,同一時(shí)間只允許一個(gè)線程訪問該變量。
兩個(gè)或多個(gè)線程視圖在同一時(shí)間修改同一變量時(shí),也需要進(jìn)行同步。考慮變量增量操作的情況,增量操作通常分解為以下 3 步:
1.
2. 。
3. 。
11.6.1- 互斥量
可以使用 pthread 的互斥忌口來保護(hù)數(shù)據(jù),確保同一時(shí)間只有一個(gè)線程訪問數(shù)據(jù)。
互斥量( mutex )從本質(zhì)上說是一把鎖,在訪問共享資源前對(duì)互斥量進(jìn)行 設(shè)置(加鎖),在訪問完成之后釋放(解鎖)互斥量。(通過休眠使進(jìn)程阻塞)
互斥變量是用 pthread_mutex_t 數(shù)據(jù)類型表示的。在使用互斥變量以前,必須首先對(duì)它進(jìn)行初始化。
11.6.2- 避免死鎖
?般情況下,如果同?個(gè)線程先后兩次調(diào)?lock,在第?次調(diào)?時(shí),由于鎖已經(jīng)被占?,該線程會(huì)掛起等待別的線程釋放鎖,然?鎖正是被??占?著的,該線程又被掛起?沒有機(jī)會(huì)釋放鎖,因此 就永遠(yuǎn)處于掛起等待狀態(tài)了,這叫做死鎖(Deadlock)。
另一種情況:線程A獲 得了鎖1,線程B獲得了鎖2,這時(shí)線程A調(diào)?lock試圖獲得鎖2,結(jié)果是需要掛起等待線程B釋放 鎖2,?這時(shí)線程B也調(diào)?lock試圖獲得鎖1,結(jié)果是需要掛起等待線程A釋放鎖1,于是線程A和B都 永遠(yuǎn)處于掛起狀態(tài)了。
有時(shí)候,應(yīng)用程序的結(jié)構(gòu)使得對(duì)互斥量進(jìn)行排序是很困難的。如果涉及了太多的鎖和數(shù)據(jù)結(jié)構(gòu),可用的函數(shù)并不能把它轉(zhuǎn)換成簡(jiǎn)單的層次,那么就需要采用另外到底方法。
在這匯總情況下,可以先釋放占有的鎖,然后過一點(diǎn)時(shí)間再試。這種情況可以使用 pthread_mutex_trylock接口 避免死鎖。
11.6.3- 函數(shù) pthread_mutex_timelock
當(dāng)線程試圖獲取一個(gè)已加鎖的互斥量時(shí), pthread_mutex_timelock互斥量 原語(yǔ)允許綁定線程阻塞時(shí)間。 該接口和 pthread_mutex_trylock接口 是基本等價(jià)的,但是在達(dá)到超時(shí)時(shí)間值時(shí), pthread_mutex_timelock 不會(huì)對(duì)互斥量進(jìn)行加鎖,而是返回錯(cuò)誤碼 ETIMEDOUT。
11.6.4- 讀寫鎖
讀寫鎖( reader-write lock )與互斥量類似,不過讀寫鎖允許更高的并行性。
讀寫鎖非常適合于對(duì)數(shù)據(jù)結(jié)構(gòu)讀的次數(shù)遠(yuǎn)大于寫的情況。
讀寫鎖也叫共享互斥鎖( shared-exclusive lock )。當(dāng)讀寫鎖是讀模式鎖定時(shí),就可以說城市共享模式鎖住的。當(dāng)它是寫模式鎖住的時(shí)候,就可以說成是以互斥鎖模式鎖定的。
讀寫鎖可以有 3 種狀態(tài):
? 讀模式下加鎖狀態(tài);
? 寫模式下加鎖狀態(tài);
? 不加鎖狀態(tài)。
一次只有一個(gè)線程可以占有寫模式的讀寫鎖,但是多個(gè)線程可以同時(shí)占有讀模式的讀寫鎖。
11.6.5- 帶有超時(shí)的讀寫鎖
帶有超時(shí)的讀寫鎖加鎖函數(shù)可以使應(yīng)用程序在獲取讀寫鎖時(shí)避免陷入永久阻塞狀態(tài)。
這兩個(gè)函數(shù)分別是: pthread_rwlock_timedrdlock 和 pthread_rwlock_timedwelock。
11.6.6- 條件變量
條件變量是線程可用的另一種同步機(jī)制。條件變量給多個(gè)線程提供了一個(gè)會(huì)和的場(chǎng)所。
條件變量與互斥量一起使用時(shí),允許線程以無競(jìng)爭(zhēng)的方式等待待定的條件發(fā)生。
在使用條件變量之前,必須先對(duì)它進(jìn)行初始化。由 pthread_cond_t 數(shù)據(jù)類型表示的條件變量可以用兩種方式進(jìn)行初始化。 在釋放條件變量底層的內(nèi)存空間之前,可以使用 pthread_cond_destroy 函數(shù)對(duì)條件進(jìn)行反初始化( deinitialize )。
11.6.7- 自旋鎖
自旋鎖與互斥量基本類似,但它不是通過休眠使進(jìn)程阻塞,而是在獲取鎖之前一直處于忙等(自旋)阻塞狀態(tài)。
自旋鎖可用于以下情況:鎖被持有的時(shí)間短,而且線程并不希望在重新調(diào)度上花費(fèi)太多成本。
只有一個(gè)屬性是自旋鎖所特有的:那就是 進(jìn)程共享屬性 支持線程進(jìn)程共享同步。
當(dāng)自旋鎖用在非搶占式內(nèi)核中時(shí)是非常有用的:除了提供互斥機(jī)制以外,它們會(huì)阻塞終端,這樣中斷處理程序就不會(huì)讓系統(tǒng)陷入死鎖狀態(tài),因?yàn)樗枰@取已被加鎖的自旋鎖( 把中斷想成是另一種搶占 )。
11.6.8- 屏障
屏障( barrier )是用戶協(xié)調(diào)多個(gè)線程并行工作的同步機(jī)制。屏障允許每個(gè)線程等待,知道所有的合作線程都到達(dá)某一點(diǎn),然后從該點(diǎn)繼續(xù)執(zhí)行。
屏障對(duì)象的概念很廣,它們?cè)试S任意數(shù)量的線程等待,知道所有的線程完成處理工作,而線程不需要退出。所有線程達(dá)到屏障后可以接著工作。
可以使用 pthread_barrier_init 函數(shù) 對(duì)屏障進(jìn)行初始化。
一旦達(dá)到屏障計(jì)數(shù)值,而且線程處于非阻塞狀態(tài),屏障就可以被重用。
11.7 小結(jié)
本章介紹了線程的概念,主要討論了 5 個(gè)基本的同步機(jī)制:
第 12 章- 線程控制
1、引言
本章將介紹控制線程行為方面的內(nèi)容:
? 線程屬性;
? 同步原語(yǔ)屬性;
? 同一進(jìn)程中的多個(gè)線程之間如何保持?jǐn)?shù)據(jù)的私有性;
? 基于進(jìn)程系統(tǒng)調(diào)用如何與線程進(jìn)行交互。
2、線程限制
線程限制的使用時(shí)為了增強(qiáng)應(yīng)用程序在不同的操作系統(tǒng)實(shí)現(xiàn)之間的可移植性。
3、線程屬性
ptherd接口允許我們通過設(shè)置每個(gè)對(duì)象關(guān)聯(lián)的不同屬性來細(xì)調(diào)線程和同步對(duì)象的行為。通常,管理這些屬性的函數(shù)都遵循相同的模式:
4、同步屬性
就像線程具有屬性一樣,線程同步對(duì)象也有屬性。
在 11.6.7中介紹了 自旋鎖的屬性: 進(jìn)程共享屬性。接下來介紹另外四種同步機(jī)制的屬性。
在談到屬性時(shí)一般談及:
12.4.1- 互斥量屬性
在進(jìn)程中,多個(gè)線程可以訪問同一個(gè)同步對(duì)象?;コ饬坑?3 個(gè)屬性。
進(jìn)程共享互斥量屬性需設(shè)置為 PTHREAD_PROCESS_PRIVATE。如果進(jìn)程共享互斥量屬性社會(huì)為 PTHREAD_PROCESS_SHARED,從多個(gè)進(jìn)程彼此之間共享的內(nèi)存數(shù)據(jù)塊中分配的互斥量就可以用于這些進(jìn)程的同步。
互斥量健壯屬性 與在多個(gè)進(jìn)程間共享的互斥量有關(guān)。 這意味著,當(dāng)持有互斥量的進(jìn)程終止時(shí),需要解決不吃量狀態(tài)恢復(fù)的問題。這種情況發(fā)生時(shí),互斥量處于鎖定狀態(tài),恢復(fù)起來很困難。
類型互斥量屬性 控制著互斥量的鎖定特性。
12.4.2- 讀寫鎖屬性
讀寫鎖支持的唯一屬性是 進(jìn)程共享屬性。
它與互斥量的 進(jìn)程共享屬性 是相同的。就像互斥量的進(jìn)程共享屬性一樣,有一堆函數(shù)用于讀取和設(shè)置讀寫鎖的進(jìn)程共享屬性。
12.4.3- 條件變量屬性
條件變量有 2 個(gè)屬性: 進(jìn)程共享屬性 和 時(shí)鐘屬性。與其它的屬性對(duì)象一樣,條件變量屬性也有一對(duì)函數(shù)用于初始化和反初始化條件變量屬性。
進(jìn)程共享屬性: 它控制著條件變量是可以被單進(jìn)程的多個(gè)線程使用,還是可以被多進(jìn)程的線程使用。
時(shí)鐘屬性: 控制計(jì)算 pthread_cond_timedwait 函數(shù)的超時(shí)參數(shù) (tsptr)時(shí)采用的是哪個(gè)時(shí)鐘。
12.4.4- 屏障屬性
屏障屬性 目前只有 進(jìn)程共享屬性:它控制著屏障是可以被多進(jìn)程的線程使用,還是只能被初始化屏障的進(jìn)程內(nèi)的多線程使用。
12.5、 重入
如果一個(gè)函數(shù)在相同的時(shí)間點(diǎn)可以被多個(gè)線程安全地調(diào)用,就稱該函數(shù)是 線程安全 的。
如果一個(gè)函數(shù)對(duì)多個(gè)線程來說是可 重入 的,就說這個(gè)函數(shù)就是線程安全的。但這并不能說明 對(duì)信號(hào)處理程序來說該函數(shù)也是可重入的。如果函數(shù)對(duì)異步信號(hào)處理程序的重入是安全的,那么就可以說函數(shù)是 異步信號(hào)安全 的。
12.6、 線程特定數(shù)據(jù)
線程特定數(shù)據(jù)( thread-specific data ),也稱為 線程私有數(shù)據(jù)( thread-private data )是存儲(chǔ)和查詢某個(gè)特定線程相關(guān)數(shù)據(jù)的一種機(jī)制。 因?yàn)槲覀兿M總€(gè)線程可以訪問它自己?jiǎn)为?dú)的數(shù)據(jù)副本,而不需要擔(dān)心與其它線程的同步訪問問題。
優(yōu)點(diǎn):
我們知道,一個(gè)進(jìn)程中的所有線程都可以訪問這個(gè)進(jìn)程的整個(gè)地址空間。 除了使用寄存器以外,一個(gè)線程沒有辦法阻止另一個(gè)線程訪問它的數(shù)據(jù)。線程特定數(shù)據(jù)也不例外。
雖然底層的實(shí)現(xiàn)部分并不能阻止這種訪問能力,但管理線程特定數(shù)據(jù)的函數(shù)可以提高線程間的數(shù)據(jù)獨(dú)立性,使得線程不太容訪問到其它線程的線程特定數(shù)據(jù)。
12.7、 取消選項(xiàng)
可取消狀態(tài) 和 可取消類型 這兩個(gè)屬性影響著線程在響應(yīng) pthread_cancel 函數(shù)調(diào)用時(shí)所 呈現(xiàn)的行為。
可取消狀態(tài)屬性 可以是 PTHREAD_CANCEL_ENABLE, 也可以是 PTHREAD_CANCEL_DISABLE。線程可以通過調(diào)用 pthread_setcancelstate 修改它的 可取消狀態(tài)。
在默認(rèn)情況下,線程在取消請(qǐng)求發(fā)出以后還是繼續(xù)運(yùn)行,直到線程到達(dá)某個(gè)取消點(diǎn)。 取消點(diǎn)是線程檢查它是都被取消的一個(gè)位置,如果取消了,則按照要求行事。
線程啟動(dòng)時(shí)默認(rèn)的 可取消狀態(tài)是 PTHREAD_CANCEL_ENABLE。
異步取消 與 推遲取消不同,因?yàn)槭褂卯惒饺∠麜r(shí),線程可以在任意時(shí)間撤銷,不是非得遇到取消點(diǎn)才能被取消。
12.8、 線程 和 信號(hào)
每個(gè)線程都有自己的信號(hào)屏蔽字,但是信號(hào)的處理是進(jìn)程中所有線程共享的。
12.9、 線程 和 fork
當(dāng)線程調(diào)用 fork 時(shí),就為子進(jìn)程創(chuàng)建了整個(gè)進(jìn)程地址空間的副本。
在多線程的進(jìn)程中沒了避免不一致狀態(tài)的問題,在 fork 返回和子進(jìn)程調(diào)用其中一個(gè) exec 函數(shù)之間,子進(jìn)程只能調(diào)用異步信號(hào)安全的函數(shù)。這就限制了在調(diào)用 exec 之前子進(jìn)程能做什么,但不涉及子進(jìn)程中鎖狀態(tài)的問題。
用 pthread_atfork 函數(shù)最多可以安裝 3 個(gè)幫助清理鎖的函數(shù)。
? prepare fork 處理程序由父進(jìn)程在 fork 創(chuàng)建子進(jìn)程前調(diào)用。這個(gè) fork 處理程序的任務(wù)是獲取父進(jìn)程定義的所有鎖。
? parent fork處理程序是在 fork創(chuàng)建子進(jìn)程以后、返回之前在父進(jìn)程上下文中調(diào)用的。
? child fork 處理程序在 fork 返回之前在子進(jìn)程上下文中調(diào)用。
12.10、 線程 和 I/0
很多函數(shù)( eg. 3.11節(jié)中提及的 pread 、 pwrite )在多線程環(huán)境下也是非常有用的,因?yàn)檫M(jìn)程中的所有線程共享相同的文件描述符。
12.11、 小結(jié)
本章中了解了:
? 如何調(diào)整線程和它們的同步原語(yǔ)
? 討論了線程的可重入性
? 還學(xué)習(xí)了線程如何與其它面向進(jìn)程的系統(tǒng)調(diào)用進(jìn)行交互。
第 13 章- 守護(hù)進(jìn)程
1、引言
守護(hù)進(jìn)程常常用作服務(wù)器進(jìn)程。
守護(hù)進(jìn)程( daemon )是生存期長(zhǎng)的一種進(jìn)程。 它們常常在系統(tǒng)引導(dǎo)裝入時(shí)啟動(dòng),僅在系統(tǒng)關(guān)閉時(shí)才終止。因?yàn)樗鼈儧]有控制終端,所以說它們是在后臺(tái)運(yùn)行的。
2、守護(hù)進(jìn)程的特征
大多數(shù)守護(hù)進(jìn)程都以超級(jí)用戶( root )特權(quán)運(yùn)行。所有的守護(hù)進(jìn)程都沒有控制終端,其終端名設(shè)置為問號(hào)。大多數(shù)用戶層守護(hù)進(jìn)程都是進(jìn)程組的組長(zhǎng)進(jìn)程以及會(huì)話的首進(jìn)程,而且是這些進(jìn)程組和會(huì)話中的唯一進(jìn)程。
3、編程規(guī)則
在編寫守護(hù)進(jìn)程程序時(shí)需要遵循一些基本規(guī)則,以防止產(chǎn)生不必要的交互作用。
? 首先要做的是調(diào)用 umask 將文件模式創(chuàng)建屏蔽字設(shè)置為一個(gè)已知值(通常是0);
? 調(diào)用 fork,然后使父進(jìn)程 exit;
? 調(diào)用 setsid 創(chuàng)建一個(gè)新會(huì)話;
? 將當(dāng)前工作目錄更改為根目錄;
? 關(guān)閉不再需要的文件描述符;
? 某些守護(hù)進(jìn)程打開 /dev/null 使其具有文件描述符 0、1 和 2 ,這樣,任何一個(gè)試圖讀標(biāo)準(zhǔn)輸入、寫標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯(cuò)誤的庫(kù)歷程都不會(huì)產(chǎn)生任何效果。
4、出錯(cuò)記錄
守護(hù)進(jìn)程存在的一個(gè)問題是如何處理出錯(cuò)消息。 因?yàn)樗揪筒粦?yīng)該有控制終端,所以不能只是簡(jiǎn)單地寫到標(biāo)準(zhǔn)錯(cuò)誤上。
有以下 3 種產(chǎn)生日志消息的方法:
? 內(nèi)核例程可以調(diào)用 log 函數(shù);
? 大多數(shù)用戶進(jìn)程(守護(hù)進(jìn)程 )調(diào)用 syslog(3)函數(shù)來產(chǎn)生日志消息。
? 無論一個(gè)用戶進(jìn)程是在此主機(jī)上,還是在通過 TCP/IP網(wǎng)絡(luò)連接到此主機(jī)的其它主機(jī)上,都可以將日志消息發(fā)向 UDP端口 514。
5、單實(shí)例守護(hù)進(jìn)程
為了正常運(yùn)作,某些守護(hù)進(jìn)程會(huì)實(shí)現(xiàn)為,在任一時(shí)刻只運(yùn)行該守護(hù)進(jìn)程的一個(gè)副本。
如果守護(hù)進(jìn)程需要訪問一個(gè)設(shè)備,而該設(shè)備驅(qū)動(dòng)程序有時(shí)會(huì)阻止想要多次打開 /dev 目錄下相應(yīng)設(shè)備節(jié)點(diǎn)的嘗試。這就限制了在一個(gè)時(shí)刻只能運(yùn)行守護(hù)進(jìn)程的一個(gè)副本。
文件 和 記錄鎖機(jī)制為一種方法提供了基礎(chǔ),該方法保證一個(gè)守護(hù)進(jìn)程只有一個(gè)副本在運(yùn)行。
文件和記錄鎖提供了一種方便的互斥機(jī)制。
守護(hù)進(jìn)程的每個(gè)副本都將試圖創(chuàng)建一個(gè)文件,并將其進(jìn)程 ID 寫到該文件中。
6、守護(hù)進(jìn)程的慣例
在 UNIX 系統(tǒng)中,守護(hù)進(jìn)程遵循下列通用慣例:
? 若守護(hù)進(jìn)程使用鎖文件,那么該文件通常存儲(chǔ)在 /var/run 目錄中;
? 若守護(hù)進(jìn)程支持配置選項(xiàng),那么配置文件通常存放在 /etc 目錄中;
? 守護(hù)進(jìn)程可用命令行啟動(dòng),但通常它們是由系統(tǒng)初始化腳本之一( /etc/rc* 或 /etc/init.d/* )啟動(dòng)的;
? 若一個(gè)守護(hù)進(jìn)程有一個(gè)配置文件,那么當(dāng)該守護(hù)進(jìn)程啟動(dòng)時(shí)會(huì)讀該文件,但在此之后一般就不會(huì)再查看它。
7、客戶進(jìn)程-服務(wù)器進(jìn)程 模型
守護(hù)進(jìn)程常常用作服務(wù)器進(jìn)程。
一般而言, 服務(wù)器進(jìn)程 等待 客戶進(jìn)程 與其通信,提出某種類型的服務(wù)要求。有的通信是單向的,有的通信是雙向的。
8、小結(jié)
在大多數(shù) UNIX 系統(tǒng)中,守護(hù)進(jìn)程是一直運(yùn)行的。
為了初始化我們自己的進(jìn)程,使之作為守護(hù)進(jìn)程運(yùn)行,需要一些審慎的思索并理解第 9 章 說明的進(jìn)程之間的關(guān)系。
本章還討論了守護(hù)進(jìn)程記錄 出錯(cuò)消息 的幾種方法。
第 14 章- 高級(jí) I/O
1、引言
本章會(huì)講到:
? 非阻塞 I/O;
? 記錄鎖;
? I/O多路轉(zhuǎn)換( select 和 poll 函數(shù) );
? 異步 I/O;
? readv 和 writev 函數(shù);
? 存儲(chǔ)映射 I/O( mmap );
2、非阻塞 I/O
10.5節(jié)中曾將系統(tǒng)調(diào)用分成兩類:“低速”系統(tǒng)調(diào)用 和 其它。 低速系統(tǒng)調(diào)用是可能會(huì)使進(jìn)程永遠(yuǎn)阻塞的一類系統(tǒng)調(diào)用。
非阻塞 I/O 使我們可以發(fā)出 open、read 和 write這樣的 I/O 操作,并使這些操作不會(huì)永遠(yuǎn)阻塞。
有時(shí),也可以將應(yīng)用程序設(shè)計(jì)成使用多線程的(11章),從而避免使用非阻塞 I/O。如若我們能在其它線程中繼續(xù)進(jìn)行,則可以允許單個(gè)線程在 I/O 調(diào)用中阻塞。
3、記錄鎖
商用 UNIX 系統(tǒng)提供了記錄鎖機(jī)制。
記錄鎖( record locking )的功能是:當(dāng)?shù)谝粋€(gè)進(jìn)程正在讀或修改文件的某個(gè)部分時(shí),使用記錄鎖可以阻止其它進(jìn)程修改同一文件區(qū)。(“記錄”這個(gè)詞并不準(zhǔn)確,因?yàn)?UNIX 系統(tǒng)內(nèi)核沒有使用文件記錄這種說法。 一個(gè)更合適的術(shù)語(yǔ)是 “字節(jié)范圍鎖”( byte-range locking ),因?yàn)樗i定的只是文件中的一個(gè)區(qū)域 )。
14.3.1- 歷史
早起 UNIX 是沒有 記錄鎖的。
14.3.2- fcnt1 記錄鎖
主要分為兩種類型的鎖: 共享讀鎖( l_type 為 L_RDLCK )和獨(dú)占性寫鎖( L_WRLCK )。基本規(guī)則是:任意多個(gè)進(jìn)程在一個(gè)給定的字節(jié)上可以有一把共享的讀鎖,但是在一個(gè)給定字節(jié)上只能有一個(gè)進(jìn)程有一把獨(dú)占寫鎖。
14.3.3- 鎖的隱含繼承和釋放
關(guān)于記錄鎖的自動(dòng)繼承和釋放有 3 條規(guī)則:
? 鎖與進(jìn)程和文件兩者相關(guān)聯(lián);
? 由 fork 產(chǎn)生的子進(jìn)程不繼承父進(jìn)程所設(shè)置的鎖;
? 在執(zhí)行 exec后,新程序可以繼承原執(zhí)行程序的鎖。
14.3.4- FreeBSD 實(shí)現(xiàn)
14.3.5- 在文件尾端加鎖
14.3.6- 建議性鎖 和 強(qiáng)制性鎖
考慮數(shù)據(jù)庫(kù)訪問例程庫(kù)。
如果該庫(kù)中所有函數(shù)都以一致的方法處理記錄鎖,則稱使用這些函數(shù)訪問數(shù)據(jù)庫(kù)的進(jìn)程集為 合作進(jìn)程( cooperating process )。如果這些函數(shù)是唯一地用來訪問數(shù)據(jù)庫(kù)的函數(shù),那么它們使用建議性鎖是可行的。但是建議性鎖并不能阻止對(duì)數(shù)據(jù)庫(kù)文件有寫權(quán)限的任何其它進(jìn)程寫這個(gè)數(shù)據(jù)庫(kù)文件。
4、 I/O 多路轉(zhuǎn)接
當(dāng)從一個(gè)描述符讀,然后又寫到另一個(gè)描述符時(shí),可以在下列形式的循環(huán)中使用阻塞 I/O: … 這種形式的阻塞 I/O 到處可見。但是如果必須從兩個(gè)描述符讀,又將如何呢?
? 法1- 將一個(gè)進(jìn)程變成兩個(gè)進(jìn)程(用 fork),每個(gè)進(jìn)程處理一條數(shù)據(jù)通路。如果使用兩個(gè)進(jìn)程,則可使每個(gè)進(jìn)程都執(zhí)行阻塞 read。
? 法2- 我們可以不使用 兩個(gè)進(jìn)程,而是用一個(gè)進(jìn)程中的兩個(gè)線程。 雖然避免了終止的復(fù)雜性,但卻要求處理兩個(gè)線程之間的同步。
? 法3- 仍舊使用一個(gè)進(jìn)程執(zhí)行該程序,但使用非阻塞 I/O讀取數(shù)據(jù)。
? 法4- 還有一種技術(shù)成為 異步I/O( asynchronous I/O )。
? 法5- 一種比較好的技術(shù)是使用 I/O多路轉(zhuǎn)接( I/O multiplexing )。為了使用這種技術(shù),先構(gòu)造一張我們感興趣的描述符(通常不止一個(gè))的列表,然后調(diào)用一個(gè)函數(shù),知道這些描述符中的一個(gè)已準(zhǔn)備好進(jìn)行 I/O時(shí),該函數(shù)才返回。
14.4.1- 函數(shù) select 和 pselect
select函數(shù)使我們可以執(zhí)行 I/O多路轉(zhuǎn)接。
pselect 是 select函數(shù)的變體。
14.4.2- 函數(shù) poll
poll 函數(shù)類似于 select,但是程序員接口有所不同。 雖然 poll 函數(shù)是 System V 引入進(jìn)來支持 STREAMS 子系統(tǒng)的,但是 poll函數(shù)可用于任何類型的文件描述符。
5、異步 I/O
14.5.1- System V 異步 I/O
14.5.2- BSD 異步 I/O
14.5.3- POSIX 異步 I/O
6、 函數(shù) readv 和 writev
readv 和 writev 函數(shù)用于在一次函數(shù)調(diào)用中讀、寫多個(gè)非連續(xù)緩沖區(qū)。有時(shí)也將這兩個(gè)函數(shù)稱為 散步讀( scatter read )和聚集寫( gather write )
7、函數(shù) readn 和 writen
函數(shù) readn 和 writen 的功能分別是讀、寫指定的 N 字節(jié)數(shù)據(jù),并處理返回值可能小于要求值的情況。這兩個(gè)函數(shù)只是按需多次調(diào)用 read 和 wtire直至讀、寫了 N 字節(jié)數(shù)據(jù)。
8、存儲(chǔ)映射 I/O
存儲(chǔ)映射 I/O( memory-mapped I/O )能將一個(gè)磁盤文件映射到存儲(chǔ)空間中的一個(gè)緩沖區(qū)上,于是,當(dāng)從緩沖區(qū)取數(shù)據(jù)時(shí),就相當(dāng)于讀文件中的相應(yīng)字節(jié)。與此類似,將數(shù)據(jù)存入緩沖區(qū)時(shí),相應(yīng)字節(jié)就自動(dòng)寫入文件。這樣就可以在不使用 read 和 write 的情況下執(zhí)行 I/O。
9、小結(jié)
本章描述了很多高級(jí) I/O 功能:
? 非阻塞 I/O——發(fā)一個(gè) I/O操作,不使其阻塞;
? 記錄鎖
? I/O多路轉(zhuǎn)接—— select 和 poll 函數(shù)
? readv 和 writev 函數(shù)
? 存儲(chǔ)映射 I/O(mmap)
第 15 章- 進(jìn)程間通信
1、引言
進(jìn)程間通信( InterProcess Communication, IPC )。
本章討論經(jīng)典的 IPC: 管道(pipe)、FIFO、消息隊(duì)列、信號(hào)量、共享存儲(chǔ)。
2、管道
管道是 UNIX 系統(tǒng) IPC 的最古老的形式,一般是半雙工的(即數(shù)據(jù)只能在一個(gè)方向時(shí)流動(dòng)),現(xiàn)在某些系統(tǒng)也提供全雙工管道。
每當(dāng)在管道中鍵入一個(gè)命令序列, 讓 shell 執(zhí)行時(shí), shell 都會(huì)為每一條命令單獨(dú)創(chuàng)建一個(gè)進(jìn)程,然后用管道將前一條命令進(jìn)程的標(biāo)準(zhǔn)輸出與后一條命令的標(biāo)準(zhǔn)輸入相連接。
管道通常用 pipe 函數(shù)創(chuàng)建。
fstat 函數(shù)對(duì)管道的每一端都返回一個(gè) FIFO(也稱命名管道) 類型的文件描述符。
3、函數(shù) popen 和 pclose
常見的操作是創(chuàng)建一個(gè)連接到另一個(gè)進(jìn)程的管道,然后讀其輸出或向其輸入端發(fā)送數(shù)據(jù)。
為此,標(biāo)準(zhǔn) I/O 庫(kù)提供了兩個(gè)函數(shù) popen 和 pclose : 創(chuàng)建一個(gè)管道, fork 一個(gè)子進(jìn)程,關(guān)閉未使用的管道端,執(zhí)行一個(gè) shell 運(yùn)行命令,然后等待命令終止。
4、協(xié)同進(jìn)程
UNIX 系統(tǒng)過濾程序從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù),向標(biāo)準(zhǔn)輸出寫數(shù)據(jù)。幾個(gè)過濾程序通常在 shell 管道中線性連接。當(dāng)一個(gè)過濾程序即產(chǎn)生某個(gè)過濾程序的輸入,又讀取該過濾程序的輸出時(shí),它就變成了 協(xié)同進(jìn)程( coprocess )。
協(xié)同進(jìn)程通常在 shell 的后臺(tái)運(yùn)行,其標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出通過管道連接到另一個(gè)程序。
popen 只提供連接到另一個(gè)進(jìn)程的標(biāo)準(zhǔn)輸入或標(biāo)準(zhǔn)輸出的一個(gè)單向管道;
而協(xié)同進(jìn)程則有連接到另一個(gè)進(jìn)程的兩個(gè)單項(xiàng)管道:一個(gè)接到其標(biāo)準(zhǔn)輸入,另一個(gè)則來自其標(biāo)準(zhǔn)輸出。
5、FIFO
FIFO 有時(shí)被稱為 命名管道。
未命名的管道只能在兩個(gè)相關(guān)的進(jìn)程之間使用,而且這兩個(gè)相關(guān)的進(jìn)程還有有一個(gè)共同的創(chuàng)建了它們的祖先進(jìn)程。
但是通過 FIFO,可以用于非線性連接,不相關(guān)的進(jìn)程也能交換數(shù)據(jù)!
FIFO 有以下兩種用途:
FIFO 可以用于復(fù)制一系列 shell 命令中的輸出流,這就防止了將數(shù)據(jù)寫向中間磁盤文件。
6、 XSI IPC
有 3 種稱為 XSI IPC 的 IPC:消息隊(duì)列、信號(hào)量、共享存儲(chǔ)器。
15.6.1- 標(biāo)識(shí)符和鍵
每個(gè)內(nèi)核中的 IPC 結(jié)構(gòu)(消息隊(duì)列、信號(hào)量 或 共享存儲(chǔ)段)都用一個(gè)非負(fù)整數(shù)的 標(biāo)識(shí)符( identifier )加以引用。(例如,要向一個(gè)消息隊(duì)列發(fā)送消息或者從一個(gè)消息隊(duì)列取消息,只需要知道其隊(duì)列標(biāo)識(shí)符)。
為此,每個(gè) IPC 對(duì)象都與一個(gè) 鍵( key )相關(guān)聯(lián),將這個(gè)鍵作為該對(duì)象的外部名。
15.6.2- 權(quán)限結(jié)構(gòu)
XSI IPC 為每一個(gè) IPC 結(jié)構(gòu)關(guān)聯(lián)了一個(gè) ipc_perm 結(jié)構(gòu)。該結(jié)構(gòu)規(guī)定了權(quán)限和所有者。
15.6.3- 結(jié)構(gòu)限制
所有 3 中形式的 XSI IPC 都有內(nèi)置限制。大多數(shù)限制可以通過重新配置內(nèi)核來改變。
15.6.4- 優(yōu)點(diǎn)和缺點(diǎn)
7、消息隊(duì)列
消息隊(duì)列是消息的鏈接表,存儲(chǔ)在內(nèi)核中,由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí)。
msgget 用于創(chuàng)建一個(gè)新隊(duì)列或打開一個(gè)現(xiàn)有隊(duì)列;
msgsnd 將新消息添加到隊(duì)列尾端。
8、 信號(hào)量
信號(hào)量 與已經(jīng)介紹過的 IPC機(jī)構(gòu)( 管道、FIFO、以及消息隊(duì)列 )不同。它是一個(gè)計(jì)數(shù)器,用于為多個(gè)進(jìn)程提供對(duì)共享數(shù)據(jù)對(duì)象的訪問。
為了正確地實(shí)現(xiàn)信號(hào)量,信號(hào)量值的測(cè)試及減 1 操作應(yīng)當(dāng)是原子操作。為此,信號(hào)量通常是在內(nèi)核中實(shí)現(xiàn)的。
常用的信號(hào)量形式被稱為 二元信號(hào)量( binary semaphore )。一般而言,信號(hào)量的初值可以使任意一個(gè)正值,該值表明有多少個(gè)共享資源單位可供共享應(yīng)用。
9、共享存儲(chǔ)
共享存儲(chǔ) 允許兩個(gè)或多個(gè)進(jìn)程共享一個(gè)給定的存儲(chǔ)區(qū)。
因?yàn)閿?shù)據(jù)不需要在客戶進(jìn)程和服務(wù)器進(jìn)程之間復(fù)制,所以這是最快的一種 IPC。
使用共享村粗時(shí)要掌握的唯一竅門是,在多個(gè)進(jìn)程之間同步訪問一個(gè)給定的存儲(chǔ)區(qū)。
10、 POSIX 信號(hào)量
11、 客戶進(jìn)程-服務(wù)器進(jìn)程 屬性
12、小結(jié)
本章詳細(xì)說明了進(jìn)程間通信的多種形式: 管道、命名管道( FIFO )、通常稱為 XSI IPC 的 3 種形式的 IPC( 消息隊(duì)列、信號(hào)量 和 共享存儲(chǔ) ),以及 POSIX 提供的替代信號(hào)量機(jī)制。
給出了一下建議:
? 要學(xué)會(huì)使用管道 和 FIFO;
? 要盡可能避免使用消息隊(duì)列以及信號(hào)量;
? 應(yīng)該考慮全雙工管道和記錄鎖;
? 共享存儲(chǔ)仍然有它的用途, 雖然通過 mmap 函數(shù)也可能提供通項(xiàng)的功能。
第 16 章- 網(wǎng)絡(luò) IPC: 套接字
進(jìn)程間通信( InterProcess Communication, IPC )。
1、引言
本章將考察不同計(jì)算機(jī)(通過網(wǎng)絡(luò)相連)上的進(jìn)程相互通信的機(jī)制:網(wǎng)絡(luò)進(jìn)程間通信( network IPC ).
在本章中,將描述套接字網(wǎng)絡(luò)進(jìn)程間通信接口,進(jìn)程用改接口能夠和其它進(jìn)程通信,無論它們是在同一臺(tái)計(jì)算機(jī)上還是不同的計(jì)算機(jī)上。本章僅是一個(gè)套接字 API 的概述。
2、 套接字描述符
套接字是通信端點(diǎn)的抽象。
套接字描述符在 UNIX 系統(tǒng)中被當(dāng)做是一種文件描述符。
為創(chuàng)建一個(gè)套接字,調(diào)用 socket 函數(shù)。
對(duì)于數(shù)據(jù)報(bào)( SOCK_DGRAM )接口,兩個(gè)對(duì)等進(jìn)程之間通信時(shí)不需要邏輯連接。只需要向?qū)Φ冗M(jìn)程所使用的套接字送出一個(gè)報(bào)文。因此 數(shù)據(jù)報(bào)提供了一個(gè)無連接的服務(wù)。
另一方面,字節(jié)流( SOCK_STREAM )要求在交換數(shù)據(jù)之前,在本地套接字和通信的對(duì)等進(jìn)程的套接字之間建立一個(gè)邏輯連接。
流控制傳輸協(xié)議( Stream Control Transmission Protocol, SCTP )提供了因特網(wǎng)域上的順序數(shù)據(jù)包服務(wù)。
調(diào)用 socket 與調(diào)用 open 相類似。在兩種情況下,均可獲得用于 I/O 的文件描述符。當(dāng)不再需要該文件描述符時(shí),調(diào)用 close 來關(guān)閉對(duì)文件或套接字的訪問,并且釋放該描述符以便重新使用。
套接字通信是雙向的。 可以采用 shutdown 函數(shù)來禁止一個(gè)套接字的 I/O。
3、尋址
進(jìn)程標(biāo)識(shí)由兩部分組成:
? 計(jì)算機(jī)的網(wǎng)絡(luò)地址;
? 計(jì)算機(jī)上用 端口號(hào) 表示的服務(wù)。
16.3.1- 字節(jié)序
字節(jié)序 是一個(gè)處理器架構(gòu)特性,用于指示像整數(shù)這樣的大數(shù)據(jù)類型內(nèi)部的字節(jié)如何排序。
如果處理器架構(gòu)支持 大端( big-endian )字節(jié)序:那么最大字節(jié)地址出現(xiàn)在最低有效字節(jié)( Least Significant Byte, LSB )上。
小端( little-endian )字節(jié)序:最低有效字節(jié)包含最小字節(jié)地址。
【注】不管字節(jié)如何排序,自最高有效字節(jié)(Most Significant Byte, MSB)總在左邊,最低有效字節(jié)總是在右邊。
網(wǎng)絡(luò)協(xié)議指定了字節(jié)序,因此異構(gòu)計(jì)算機(jī)系統(tǒng)能夠交換協(xié)議信息而不會(huì)被字節(jié)序所混淆。 TCP/IP 協(xié)議棧使用大端字節(jié)序。對(duì)于TCP/IP,地址用網(wǎng)絡(luò)字節(jié)序來表示,所以應(yīng)用程序有時(shí)需要在處理器的字節(jié)序與網(wǎng)絡(luò)字節(jié)序之間轉(zhuǎn)換他們。
16.3.2- 地址格式
一個(gè)地址標(biāo)識(shí)一個(gè)特定通信域的套接字端點(diǎn),地址格式與這個(gè)特定的通信域相關(guān)。
16.3.3- 地址查詢
16.3.4- 將套接字與地址關(guān)聯(lián)
對(duì)于服務(wù)器,需要給一個(gè)接受客戶端請(qǐng)求的服務(wù)器套接字關(guān)聯(lián)上一個(gè)眾所周知的地址。
客戶端應(yīng)有一種方法來發(fā)現(xiàn)連接服務(wù)器所需要的地址,最簡(jiǎn)單的方法就是服務(wù)器保留一個(gè)地址并且注冊(cè)在 /etc/services 或者某個(gè)名字服務(wù)中。
4、建立連接
如果要處理一個(gè)面向連接的 網(wǎng)絡(luò)服務(wù)( SOCK_STREAM 或 SOCK_SEQPACKET ),那么在開始叫喚數(shù)據(jù)以前,需要在請(qǐng)求服務(wù)的進(jìn)程套接字(客戶端)和提供服務(wù)的進(jìn)程套接字(服務(wù)器)之間建立一個(gè)連接。 使用 connect 函數(shù) 來建立連接。
5、數(shù)據(jù)傳輸
既然一個(gè)套接字端點(diǎn)表示為一個(gè)文件描述符,那么只要建立連接,就可以使用 read 和 write來通過套接字通信。
在套接字描述符上使用 read 和 write 是非常有意義的,因?yàn)檫@意味著可以將套接字描述符傳遞給那些原先為處理本地文件而設(shè)計(jì)的函數(shù)。 而且還可以安排將套接字描述符傳遞給子進(jìn)程,而該子進(jìn)程執(zhí)行的程序并不了解套接字。
6、套接字選項(xiàng)
套接字機(jī)制提供了兩個(gè)套接字選項(xiàng)接口來控制套接字行為:
? 一個(gè)接口用來設(shè)置選項(xiàng);
? 另一個(gè)接口可以查詢選項(xiàng)的狀態(tài)。
可以獲取 or 設(shè)以下 3 種選項(xiàng):
? 通用選項(xiàng),工作在所有套接字類型上;
? 在套接字層次管理的選項(xiàng),但是依賴于下層協(xié)議的支持;
? 特定于某協(xié)議的選項(xiàng),每個(gè)協(xié)議獨(dú)有的。
可以使用 setsockopt 函數(shù)來設(shè)置套接字選項(xiàng)。
7、帶外數(shù)據(jù)
帶外數(shù)據(jù)( out-of-band data )是一些通信協(xié)議所支持的可選功能,與普通數(shù)據(jù)相比,它允許更好優(yōu)先級(jí)的數(shù)據(jù)傳輸。帶外數(shù)據(jù)先行傳輸,即使傳輸隊(duì)列已經(jīng)有數(shù)據(jù)。
TCP 支持帶外數(shù)據(jù),但是 UDP 不支持。 套接字口對(duì)帶外數(shù)據(jù)的支持很大程度上受 TCP 帶外數(shù)據(jù)具體實(shí)現(xiàn)的影響。
TCP 將帶外數(shù)據(jù)成為 緊急數(shù)據(jù)( urgent data )。 TCP 僅支持一個(gè)字節(jié)的緊急數(shù)據(jù),但是允許緊急數(shù)據(jù)在普通數(shù)據(jù)傳遞機(jī)制數(shù)據(jù)流之外傳輸。
8、非阻塞和異步 I/O
9、小結(jié)
本章討論了:
? 套接字端點(diǎn)如何命名,在連接服務(wù)器時(shí),如何發(fā)現(xiàn)所用的地址。
? 給出了采用無連接的(即基于數(shù)據(jù)報(bào)的)套接字和面向連接的套接字的客戶端和服務(wù)器的實(shí)例;
? 討論了 異步和 阻塞的套接字 I/O;
? 用于管理套接字選項(xiàng)的接口。
第 17 章-高級(jí)進(jìn)程間通信
1、引言
本章將會(huì):
? 介紹一種高級(jí) IPC(進(jìn)程間通信)——UNIX 域套接字機(jī)制,并說明它的應(yīng)用方法。這種形式的 IPC 可以在同一計(jì)算機(jī)系統(tǒng)上運(yùn)行的兩個(gè)進(jìn)程之間傳送打開文件描述符。
? 服務(wù)進(jìn)程可以使它們打開的文件描述符與指定的名字相關(guān)聯(lián),同一系統(tǒng)上運(yùn)行的客戶進(jìn)程可以使用這些名字與服務(wù)器進(jìn)程匯聚。
? 還會(huì)了解到操作系統(tǒng)如何為每一個(gè)客戶進(jìn)程提供一個(gè)獨(dú)用的 IPC 通道。
2、 UNIX域套接字
UNIX 域套接字用于在同一臺(tái)計(jì)算機(jī)上運(yùn)行的進(jìn)程之間的通信。
雖然因特網(wǎng)網(wǎng)域套接字可以用于同一目的,但 UNIX 域套接字的效率更高。
UNIX 域套接字提供流和數(shù)據(jù)報(bào)兩種接口,UNIX 域套接字更像是套接字和管道的混合。
3、唯一連接
服務(wù)器進(jìn)程可以使用標(biāo)準(zhǔn) blind、listen 和 accept函數(shù),為客戶進(jìn)程安排一個(gè)唯一 UNIX 域連接。客戶進(jìn)程使用 connect 與服務(wù)器進(jìn)程聯(lián)系。在服務(wù)器進(jìn)程接受了 connect 請(qǐng)求后,在服務(wù)器進(jìn)程和客戶進(jìn)程之間就存在了 唯一連接。
4、傳送文件描述符
傳送文件描述符可以使一個(gè)進(jìn)程(通常是服務(wù)器進(jìn)程)能夠處理打開一個(gè)文件所要做的一切操作(包括將網(wǎng)絡(luò)名翻譯為網(wǎng)絡(luò)地址、撥號(hào)調(diào)制調(diào)節(jié)器、協(xié)商文件鎖等)以及向調(diào)用進(jìn)程送回一個(gè)描述符,該描述符可被用于以后的所有 I/O 函數(shù)。
5、 打開服務(wù)器進(jìn)程第 1 版
6、 打開服務(wù)器進(jìn)程第 2 版
7、小結(jié)
如何在兩個(gè)進(jìn)程之間傳送文件描述符,以及服務(wù)器進(jìn)程如何接受來自客戶進(jìn)程的唯一連接。
了解了如何用它們來實(shí)現(xiàn)一個(gè)全雙工的管道以及如何利用它們來適應(yīng) 14.4 節(jié)的 I/O多路轉(zhuǎn)接函數(shù)以間接地用于 XSI 消息隊(duì)列中。
第 18 章- 終端 I/O
總結(jié)
以上是生活随笔為你收集整理的《UNIX环境高级编程——APUE》的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iSAM1论文推导学习--第二节QR部分
- 下一篇: 计算机考研数学和英语考什么,考研英语几与