c、筆記-語(yǔ)言基礎(chǔ)linux-sys播客1_第1頁(yè)
c、筆記-語(yǔ)言基礎(chǔ)linux-sys播客1_第2頁(yè)
c、筆記-語(yǔ)言基礎(chǔ)linux-sys播客1_第3頁(yè)
c、筆記-語(yǔ)言基礎(chǔ)linux-sys播客1_第4頁(yè)
c、筆記-語(yǔ)言基礎(chǔ)linux-sys播客1_第5頁(yè)
已閱讀5頁(yè),還剩188頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

前學(xué)習(xí)目成為linux/unix系統(tǒng)程學(xué)習(xí)態(tài)謙嚴(yán)勤善學(xué)習(xí)方從本章開(kāi)始學(xué)習(xí)各種Linux系統(tǒng)函數(shù),這些函數(shù)的用法必須結(jié)合Linux內(nèi)核的工作原理來(lái)理解,因?yàn)橄到y(tǒng)函數(shù)正是內(nèi)核提供給應(yīng)用程序的接口,而要理解內(nèi)核的工作原理,必須熟練掌握C語(yǔ)言,因?yàn)閮?nèi)核也是用C語(yǔ)言寫(xiě)的,我們?cè)诿枋鰞?nèi)核工作原理時(shí)必然要用“指針”、“結(jié)構(gòu)體”、“鏈表”這些名詞來(lái)組織語(yǔ)言,就像只有掌握了英語(yǔ)才能看懂英文書(shū)一樣,只有學(xué)好了C語(yǔ)言才能看懂我描述的內(nèi)核工作原理讀者看到這里應(yīng)該已經(jīng)熟練掌握了C語(yǔ)言了,所以應(yīng)該有一個(gè)很好的起點(diǎn)了我們?cè)诮榻BC標(biāo)準(zhǔn)庫(kù)時(shí)并不試圖把所有庫(kù)函數(shù)講一遍,而是通過(guò)介紹一部分常用函數(shù)讓讀者把握庫(kù)函數(shù)的基本用法,在掌握了方法之后,書(shū)上沒(méi)講的庫(kù)函數(shù)讀者應(yīng)該自己查ManPage學(xué)會(huì)使用同樣,本書(shū)的第三部分也并不試圖把所有的系統(tǒng)函數(shù)講一遍,而是通過(guò)介紹一部分系統(tǒng)函數(shù)讓讀者理解操作系統(tǒng)各部分的工作原理,在有了這個(gè)基礎(chǔ)之后就應(yīng)該能夠看懂ManPage學(xué)習(xí)其它系統(tǒng)函數(shù)的用法讀者可以結(jié)合[APUE2e]學(xué)習(xí)本書(shū)的第三部分,該書(shū)在講解系統(tǒng)函數(shù)方面更加全面,但對(duì)于內(nèi)核工作原理涉及得不夠深入,而且假定讀者具有一定的操作系統(tǒng)基礎(chǔ)知識(shí),所以并不適合初學(xué)者該書(shū)還有一點(diǎn)非常不適合初學(xué)者,作者不辭勞苦,在N多種UNIX系統(tǒng)上做了實(shí)驗(yàn),分析了它們的內(nèi)核代碼,把每個(gè)系統(tǒng)函數(shù)在各種UNIX系統(tǒng)上的不兼容特性總結(jié)得非常詳細(xì),很多開(kāi)發(fā)者需要編寫(xiě)可移植的應(yīng)用程序,一定愛(ài)死他了,但初學(xué)者看了大段大段的這種描述(某某函數(shù)在4.2BSD上怎么樣,到4.4BSD又改成怎么樣了,在SVR4上怎么樣,到Solaris又改成怎么樣了,現(xiàn)在POSIX標(biāo)準(zhǔn)是怎么的,還有哪些系統(tǒng)沒(méi)有完全遵守POSIX標(biāo)準(zhǔn))只會(huì)一頭霧水,不看倒還明白,越看越不明白了也正因?yàn)樵摃?shū)要兼顧各種UNIX系統(tǒng),所以沒(méi)法深入講解內(nèi)核的工作原理,因?yàn)槊糠NUNIX系統(tǒng)的內(nèi)核都不一樣而本書(shū)的側(cè)重點(diǎn)則不同,只講Linux平臺(tái)的特性,只講Linux內(nèi)核的工作原理,涉及體系結(jié)構(gòu)時(shí)只講x86平臺(tái),對(duì)于初學(xué)者來(lái)說(shuō),綁定到一個(gè)明確的平臺(tái)上學(xué)習(xí)就不會(huì)覺(jué)得太抽象了當(dāng)然本書(shū)的代碼也會(huì)盡量兼顧可移植性,避免依賴(lài)于Linux平臺(tái)特有的一些特性只聽(tīng)不練肯定學(xué)不會(huì)Linux,每個(gè)知識(shí)點(diǎn)都需要去動(dòng)前i1文件11.1C1 效 程序的跨平臺(tái) PCB概 taskstruct結(jié)構(gòu) filesstruct結(jié)構(gòu) 文件描述 最大打開(kāi)文件個(gè) 阻塞和非阻 阻塞讀終 非阻塞讀終 用fcntl改變FileStatus 習(xí) 數(shù)據(jù)塊尋 ........... 操 ldir/seekdir 遞歸遍 VFS虛擬文件系 練 3進(jìn)3.3.1fork....3.3.2exec族3.3.3wait/waitpid...4pipefifo內(nèi)存共享4.3.1mmap/munmap...4.3.2進(jìn)程間共享通信UnixSocket....5信號(hào)5.1.1信號(hào)5.1.2信號(hào)機(jī)制5.1.3信號(hào)產(chǎn)生種類(lèi)5.1.4信號(hào)產(chǎn)生原因PCB的信號(hào) 信號(hào)捕捉設(shè) 利用SIGUSR1和SIGUSR2實(shí)現(xiàn)父子進(jìn)程同步輸 C標(biāo)準(zhǔn)庫(kù)信號(hào)處理函 可重入函 信號(hào)引起的競(jìng)態(tài)和異步 時(shí)序競(jìng) 全局變量異步 可重入函 避免異步I/O的類(lèi) SIGCHLD信號(hào)處 SIGCHLD的產(chǎn)生條 status處理方 向信號(hào)捕捉函數(shù)傳 信號(hào)中斷系統(tǒng)調(diào) 練 進(jìn)程間關(guān) 終 網(wǎng)絡(luò)終 進(jìn)程 會(huì) 概 模 習(xí) 線(xiàn)程概 8.1.1線(xiàn) 線(xiàn)程和進(jìn)程的關(guān) 線(xiàn)程間共享資 線(xiàn)程間非共享資 線(xiàn)程優(yōu)缺 pthread 線(xiàn)程原 pthread pthread pthread pthread pthread pthread pthread 線(xiàn)程終止方 線(xiàn)程屬 線(xiàn)程屬性初始 線(xiàn)程屬性控制實(shí) 細(xì)節(jié)注 練 線(xiàn)程同 線(xiàn)程為什么要同 互斥 臨界區(qū)的選 互斥量實(shí) 死 讀寫(xiě) 條件變 信號(hào) 進(jìn)程間 進(jìn)程間pthread 文件 習(xí) 網(wǎng)絡(luò)基 模 OSI七層模 TCP/IP四層模 通信過(guò) 協(xié)議格 數(shù)據(jù)包封 以太網(wǎng)幀格 ARP數(shù)據(jù)報(bào)格 IP段格 UDP數(shù)據(jù)抱格 TCP數(shù)據(jù)報(bào)格 再議 tcp狀態(tài)轉(zhuǎn)換 TCP流量控制(滑動(dòng)窗口 TCP半狀 檢測(cè)tcp一端是否異常斷 名詞術(shù)語(yǔ)解 10.5.1路由 路由器工作原 路由表(Routing 以太網(wǎng)交換機(jī)工作原 hub工作原 半雙工/全雙 DNS服務(wù) 局域網(wǎng)(localarea 廣域網(wǎng)(widearea 端 常見(jiàn)網(wǎng)絡(luò)知識(shí)面試 預(yù)備知 網(wǎng)絡(luò)字節(jié) IP地址轉(zhuǎn)換函 sockaddr數(shù)據(jù)結(jié) 網(wǎng)絡(luò)套接字函 C/S模型 C/S模型 出錯(cuò)處理封裝函 練 高并發(fā)服務(wù) 多進(jìn)程并發(fā)服務(wù) 多線(xiàn)程并發(fā)服務(wù) 多路I/O轉(zhuǎn)接服務(wù) 三種模型性能分 線(xiàn)程池并發(fā)服務(wù) UDP局域網(wǎng)服務(wù) 多 socket 其它常用函 名字與地址轉(zhuǎn) 套街口和地址關(guān) 練 s編 正則表達(dá) 錯(cuò)誤處理機(jī) 命令行參 時(shí)間函 文件時(shí) cpu使用時(shí) 工 網(wǎng)絡(luò)工 175 設(shè)置 小項(xiàng)目實(shí) 多線(xiàn)程 哲學(xué)家就 數(shù)字多機(jī)系 云計(jì)算,wifi內(nèi)出紅蜘蛛,局域網(wǎng)分 網(wǎng)絡(luò)云盤(pán),云盤(pán),網(wǎng)絡(luò)云高并發(fā)即時(shí)通信服務(wù) web服務(wù) 基本HTTP協(xié) 執(zhí)行CGI程 大項(xiàng)目實(shí) 研發(fā) 1文件C標(biāo)準(zhǔn)函數(shù)與系統(tǒng)函圖1.1:帶緩沖區(qū)的c標(biāo)準(zhǔn)函I/O每一個(gè)FILE文件流都有一個(gè)緩沖區(qū)buffer,默認(rèn)大小效事實(shí)上UnbufferedI/O這個(gè)名詞是有些誤導(dǎo)的,雖然write系統(tǒng)調(diào)用位于C標(biāo)準(zhǔn)庫(kù)I/O緩沖區(qū)的底層,但在write的底層也可以分配一個(gè)內(nèi)核I/O緩沖區(qū),所以write也不一定是直接寫(xiě)到文件的,也可能寫(xiě)到內(nèi)核I/O緩沖區(qū)中,至于究竟寫(xiě)到了文件中還是內(nèi)核緩沖區(qū)中對(duì)于進(jìn)程來(lái)說(shuō)是沒(méi)有差別的,如果進(jìn)程A和進(jìn)程B打開(kāi)同一文件,進(jìn)程A寫(xiě)到內(nèi)核I/O緩沖區(qū)中的數(shù)據(jù)從進(jìn)程B也能讀到,而C標(biāo)準(zhǔn)庫(kù)的I/O緩沖區(qū)則不具有這一特性(想為什么)PCB概task_struct結(jié)構(gòu)/usr/src/linuxfiles_struct結(jié)構(gòu)圖1.2:一個(gè)進(jìn)程默認(rèn)打開(kāi)3個(gè)文件STDINSTDINFILENOSTDOUTFILENOSTDERRFILENO#include<sys/types.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>intopen(constchar*pathname,intintopen(constchar*pathname,intflags,modet返回值成功返回新分配的文件描述符,出錯(cuò)返回-1并設(shè)置在ManPage中open函數(shù)有兩種形式,一種帶兩個(gè)參數(shù),一種帶三個(gè)參數(shù),其實(shí)在C代碼pathname參數(shù)是要打開(kāi)或創(chuàng)建的文件名,和fopen一樣,pathname既可以是相可以是絕對(duì)路徑flags參數(shù)有一系列常數(shù)值可供選擇,可以同時(shí)選擇多個(gè)常數(shù)用按位或運(yùn)算符連接起來(lái),所以這些常數(shù)的宏定義都以O(shè)開(kāi)頭,表示or必選項(xiàng):以下三個(gè)常數(shù)中必須指定一個(gè),且僅允ORDONLYOWRONLYORDWR以下可選項(xiàng)可以同時(shí)指定0個(gè)或多個(gè),和必選項(xiàng)按位或起來(lái)作為flags參數(shù)可選項(xiàng)有很多,這里只介紹一部分,其它選項(xiàng)可參考o(jì)pen(2)的ManPage:OAPPEND表示追加如果文件已有內(nèi)容,這次打開(kāi)文件所寫(xiě)的數(shù)據(jù)附加到文件的末尾OCREATmode,表示該OTRUNC(TrunONONBLOCKONONBLOCK方式打開(kāi)可以做非阻塞I/O(NonblockI/注意open函數(shù)與C標(biāo)準(zhǔn)I/O庫(kù)的fopen函數(shù)有些細(xì)微的以可寫(xiě)的方式fopen一個(gè)文件時(shí),如果文件不存在會(huì)自動(dòng)創(chuàng)建,而open一個(gè)文件時(shí)必須明確指定OCREAT才會(huì)創(chuàng)建文件,否則文件不存在就出錯(cuò)返回以w或w+方式fopen一個(gè)文件時(shí),如果文件已存在就截?cái)酁?字節(jié),而open一個(gè)文件時(shí)必須明確指定OTRUNC才會(huì)截?cái)辔募?,否則直接在原來(lái)的數(shù)據(jù)上改寫(xiě)第三個(gè)參數(shù)mode指定文件權(quán)限,可以用八進(jìn)制數(shù)表示,比如0644表示rwrr–,也可以用SIRUSR、SIWUSR等宏定義按位或起來(lái)表示,詳見(jiàn)open(2)的ManPage要注意的是,補(bǔ)充說(shuō)明一下S的umask命令S進(jìn)程的umask掩碼可以用umask命令查看用touch命令創(chuàng)建一個(gè)文件時(shí),創(chuàng)建權(quán)限是0666,而touch進(jìn)程繼承了S進(jìn)程的mak掩碼,所以最終的文件權(quán)限是0666&~022=0644$$touch$ls-l-rw-rw-r--1xingwenpeng09112348同樣道理,用gcc編譯生成一個(gè)可執(zhí)行文件時(shí),創(chuàng)建權(quán)限是0777,而最終的文件權(quán)限0777&~022=xingwenpeng@ubuntu~$umaskxingwenpeng@ubuntu~$umaskxingwenpeng@ubuntu~$gccmain.cxingwenpeng@ubuntu~$ls-la.out-rwxrwxr-x1xingwenpengxingwenpeng71589112351我們看到的都是被umask掩碼修改之后的權(quán)限,那么如何證明touch或gcc創(chuàng)建文件的權(quán)限本來(lái)應(yīng)該是0666和0777呢?我們可以把S進(jìn)程的umask改成0,再重復(fù)上述實(shí)驗(yàn):$rmfile123$umask$touch$ls-l-rw-rw-rw-1xingwenpeng$gcc$ls-l

09112352-rwxrwxr-x1xingwenpengxingwenpeng71589112352現(xiàn)在我們自己寫(xiě)一個(gè)程序,在其中調(diào)用open(“somefile”,OWRONLY|OCREAT,close函數(shù)關(guān)閉一個(gè)已打開(kāi)intclose(intfd);返回值成功返回0,出錯(cuò)返回-1并設(shè)置參數(shù)fd是要關(guān)閉的文件描述符需要說(shuō)明的是,當(dāng)一個(gè)進(jìn)程終止時(shí),內(nèi)核對(duì)該進(jìn)程所有尚未關(guān)閉的文件描述符調(diào)用close關(guān)閉,所以即使用戶(hù)程序不調(diào)用close,在終止時(shí)內(nèi)核也會(huì)自動(dòng)關(guān)閉它打開(kāi)的所有文件但是對(duì)于一個(gè)長(zhǎng)年累月運(yùn)行的程序(比如網(wǎng)絡(luò)服務(wù)器),打開(kāi)的文件描述符一定要記得關(guān)閉,否則隨著打開(kāi)的文件越來(lái)越多,會(huì)占用大量文件描述符和系統(tǒng)資源由open返回的文件描述符一定是該進(jìn)程尚未使用的最小描述符由于程序啟動(dòng)時(shí)自動(dòng)打開(kāi)文件描述符0、1、2,因此第一次調(diào)用open打開(kāi)文件通常會(huì)返回描述符3,再調(diào)用open就會(huì)返回4可以利用這一點(diǎn)在標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯(cuò)誤輸出上打開(kāi)一個(gè)新文件,實(shí)現(xiàn)重定向的功能例如,首先調(diào)用close關(guān)閉文件描述符1,然后調(diào)用open打開(kāi)一個(gè)常規(guī)文件,則一定會(huì)返回文件描述符1,這時(shí)候標(biāo)準(zhǔn)輸出就不再是終端,而是一個(gè)常規(guī)文件了,再調(diào)用printf就不會(huì)打印到屏幕上,而是寫(xiě)到這個(gè)文件中了后面要講的dup2函數(shù)提供了另外一種辦法在指定的文件描述符上打開(kāi)文件查看當(dāng)前系統(tǒng)允許打開(kāi)最大文當(dāng)前默認(rèn)設(shè)置最大打開(kāi)文件個(gè)數(shù)ulimitulimit-修改默認(rèn)設(shè)置最大打開(kāi)文件個(gè)數(shù)為ulimitulimit-nread函數(shù)從打開(kāi)的設(shè)備或文件中數(shù)#include#includessizetread(intfd,void*buf,sizet返回 成功返 的字節(jié)數(shù),出錯(cuò)返回-1并設(shè)置errno,如果在調(diào)read之前已到達(dá)文件末尾,則這次read返回參數(shù)count是請(qǐng)求的字節(jié)數(shù),讀上來(lái)的數(shù)據(jù)保存在緩沖區(qū)buf中,同時(shí)文件的當(dāng)前讀寫(xiě)位置向后移注意這個(gè)讀寫(xiě)位置和使用C標(biāo)準(zhǔn)I/O庫(kù)時(shí)的讀寫(xiě)位置有可能不同,這個(gè)讀寫(xiě)位置是記在內(nèi)核中的,而使用C標(biāo)準(zhǔn)I/O庫(kù)時(shí)的讀寫(xiě)位置是用戶(hù)空間I/O緩沖區(qū)中的位置比如用fgetc讀一個(gè)字節(jié),fgetc有可能從內(nèi)核中預(yù)讀1024個(gè)字節(jié)到I/O緩沖區(qū)中,再返回第一個(gè)字節(jié),這時(shí)該文件在內(nèi)核中記錄的讀寫(xiě)位置是1024,而在FILE結(jié)構(gòu)體中記錄的讀寫(xiě)位置是1注意返回值類(lèi)型是ssizet,表示有符號(hào)的sizet,這樣既可以返回正的字節(jié)數(shù)、0(表示到達(dá)文件末尾)也可以返回負(fù)值1(表示出錯(cuò))read函數(shù)返回時(shí),返回值說(shuō)明了buf中前多少個(gè)字節(jié)是剛讀上來(lái)的有些情況下,實(shí)際讀到的字節(jié)數(shù)(返回值)會(huì)小于請(qǐng)求讀的字讀常規(guī)文件時(shí),在讀到count個(gè)字節(jié)之前已到達(dá)文件末尾例如,距文件末尾還有30個(gè)從終端設(shè)備讀,通常以行為單位,讀到換行符就返回了從網(wǎng)絡(luò)讀,根據(jù)不同的傳輸層協(xié)議和內(nèi)核緩存機(jī)制,返回值可能小于請(qǐng)求的字節(jié)數(shù),后面socket編程部分會(huì)詳細(xì)講解write函數(shù)向打開(kāi)的設(shè)備或文件中#include#includessizetwrite(intfd,constvoid*buf,sizet返回值返回寫(xiě)入的字節(jié)數(shù),出錯(cuò)返回-1并設(shè)置寫(xiě)常規(guī)文件時(shí),write的返回值通常等于請(qǐng)求寫(xiě)的字節(jié)數(shù)count,而向終端設(shè)備或網(wǎng)絡(luò)寫(xiě)則不一定阻塞和非阻讀常規(guī)文件是不會(huì)阻塞的,不管讀多少字節(jié),read一定會(huì)在有限的時(shí)間內(nèi)返回從終端設(shè)備或網(wǎng)絡(luò)讀則不一定,如果從終端輸入的數(shù)據(jù)沒(méi)有換行符,調(diào)用read讀終端設(shè)備就會(huì)阻塞,如果網(wǎng)絡(luò)上沒(méi)有接收到數(shù)據(jù)包,調(diào)用read從網(wǎng)絡(luò)讀就會(huì)阻塞,至于會(huì)阻塞多長(zhǎng)時(shí)間也是不確定的,如果一直沒(méi)有數(shù)據(jù)到達(dá)就一直阻塞在那里同樣,寫(xiě)常規(guī)文件是不會(huì)阻塞的,而向終端設(shè)備或網(wǎng)絡(luò)寫(xiě)則不一定現(xiàn)在明確一下阻塞(Block)這個(gè)概念當(dāng)進(jìn)程調(diào)用一個(gè)阻塞的系統(tǒng)函數(shù)時(shí),該進(jìn)程被置于睡眠(Sleep)狀態(tài),這時(shí)內(nèi)核調(diào)度其它進(jìn)程運(yùn)行,直到該進(jìn)程等待的事件發(fā)生了(比如網(wǎng)絡(luò)上接收到數(shù)據(jù)包,或者調(diào)用sleep指定的睡眠時(shí)間到了)它才有可能繼續(xù)運(yùn)行與睡眠狀態(tài)相對(duì)的是運(yùn)行(Running)狀態(tài),在Linux內(nèi)核中,處于運(yùn)行狀態(tài)的進(jìn)程分為兩種情況:正在被調(diào)度執(zhí)行CPU處于該進(jìn)程的上下文環(huán)境中,程序計(jì)數(shù)器(eip)里保存著該進(jìn)程的指令地址,通用寄存器里保存著該進(jìn)程運(yùn)算過(guò)程的中間結(jié)果,正在執(zhí)行該進(jìn)程的指令,正在讀寫(xiě)該進(jìn)程的地址空間就緒狀態(tài)該進(jìn)程不需要等待什么事件發(fā)生,隨時(shí)都可以執(zhí)行,但CPU暫時(shí)還在執(zhí)行另一個(gè)進(jìn)程,所以該進(jìn)程在一個(gè)就緒隊(duì)列中等待被內(nèi)核調(diào)度系統(tǒng)中可能同時(shí)有多個(gè)就緒的進(jìn)程,那么該調(diào)度誰(shuí)執(zhí)行呢??jī)?nèi)核的調(diào)度算法是基于優(yōu)先級(jí)和時(shí)間片的,而且會(huì)根據(jù)每個(gè)進(jìn)程的運(yùn)行情況動(dòng)態(tài)調(diào)整它的優(yōu)先級(jí)和時(shí)間片,讓每個(gè)進(jìn)程都能比較公平地得到機(jī)會(huì)執(zhí)行,同時(shí)要兼顧用戶(hù)體驗(yàn),不能讓和用戶(hù)交互的進(jìn)程響應(yīng)太慢下面這個(gè)小程序從終端讀數(shù)據(jù)再寫(xiě)回終端#includeint{intn;n=read(STDINFILENO,buf,if(n<0)perror("readSTDINFILENO");}write(STDOUTFILENO,buf,n);return0;}執(zhí)行結(jié)果如下:$$$oworld(回車(chē)oworl$bashdcommandnotS進(jìn)程創(chuàng)建a.out進(jìn)程,a.out進(jìn)程開(kāi)始執(zhí)行,而S進(jìn)程睡眠等待a.out進(jìn)程出a.out調(diào)用read時(shí)睡眠等待,直到終端設(shè)備輸入了換行符才從read返回,read只讀走個(gè)字符,剩下的字符仍然保存在內(nèi)核的終端設(shè)備輸入緩沖區(qū)中a.out進(jìn)程打印并退出,這時(shí)S進(jìn)程恢復(fù)運(yùn)行,S繼續(xù)從終端用戶(hù)輸入令,于是讀走了終端設(shè)備輸入緩沖區(qū)中剩下的字符d和換行符,把它當(dāng)成一條命令解釋執(zhí)行,結(jié)果發(fā)現(xiàn)執(zhí)行不了,沒(méi)有d這個(gè)命令如果在open一個(gè)設(shè)備時(shí)指定了ONONBLOCK標(biāo)志,read/write就不會(huì)阻塞以read為例,如果設(shè)備暫時(shí)沒(méi)有數(shù)據(jù)可讀就返回1,同時(shí)置errno為EWOULDBLOCK(或者EAGAIN,這兩個(gè)宏定義的值相同),表示本來(lái)應(yīng)該阻塞在這里(wouldblock,虛擬語(yǔ)氣),事實(shí)上并沒(méi)有阻塞而是直接返回錯(cuò)誤,調(diào)用者應(yīng)該試著再讀一次(again)這種行為方式稱(chēng)為輪詢(xún)(Poll),調(diào)用者只是查詢(xún)一下,而不是阻塞在這里死等,這樣可以同時(shí)監(jiān)視多個(gè)設(shè)備:while(1)while(1)if(設(shè)備1處理數(shù)據(jù)if(設(shè)備2處理數(shù)據(jù)}如果read(設(shè)備1)是阻塞的,那么只要設(shè)備1沒(méi)有數(shù)據(jù)到達(dá)就會(huì)一直阻塞在設(shè)備1的read調(diào)用上,即使設(shè)備2有數(shù)據(jù)到達(dá)也不能處理,使用非阻塞I/O就可以避免設(shè)備2得不到及時(shí)處理用功,如果阻塞在那里,操作系統(tǒng)可以調(diào)度別的進(jìn)程執(zhí)行,就不會(huì)做無(wú)用功了在使用非阻塞I/O時(shí),通常不會(huì)在一個(gè)while循環(huán)中一直不停地查詢(xún)(這稱(chēng)為T(mén)ightLoop),而是每延遲while(1)while(1)if(設(shè)備1處理數(shù)據(jù)if(設(shè)備2}這樣做的問(wèn)題是,設(shè)備1有數(shù)據(jù)到達(dá)時(shí)可能不處理,最長(zhǎng)需延遲n秒才能處理,而且反復(fù)查詢(xún)還是做了很多無(wú)用功以后要學(xué)習(xí)的select(2)函數(shù)可以阻塞地同時(shí)監(jiān)視多個(gè)設(shè)備,還可以設(shè)定阻塞等待的超時(shí)時(shí)間,從而地解決了這個(gè)問(wèn)題以下是一個(gè)非阻塞I/O的例子目前我們學(xué)過(guò)的可能引起阻塞的設(shè)備只有終端,所以我們用終端來(lái)做這個(gè)實(shí)驗(yàn)程序開(kāi)始執(zhí)行時(shí)在0、1、2文件描述符上自動(dòng)打開(kāi)的文件就是終端,但是沒(méi)有ONONBLOCK標(biāo)志所以就像例28.2的我們可以重新打開(kāi)一遍設(shè)備文件/dev/tty(表示當(dāng)前終端),在打開(kāi)時(shí)指定ONONBLOCK非阻塞讀終端#include<fcntl.h>#include<errno.h>#include#defineMSGTRY"tryagain\n"intmain(void){intfd,n;fd=open("/dev/tty",ORDONLYONONBLOCK);if(fd<0){perror("open/dev/tty");}n=read(fd,buf,10);if(n<0){if(errno=={write(STDOUTFILENO,MSGTRY,strlen(MSGgoto}perror("read/dev/tty");}write(STDOUTFILENO,buf,n);return}以下是用非阻塞I/O實(shí)現(xiàn)等待超時(shí)的例子既保證了超時(shí)退出的邏輯又保證了有數(shù)據(jù)到#include<fcntl.h>#include<errno.h>#include#defineMSGTRY"tryagain\n"#defineMSGTIMEOUT"timeout\n"int{intfd,n,fd=open("/dev/tty",ORDONLYONONBLOCK);if(fd<0){perror("open/dev/tty");}for(i=0;i<5;i++)n=read(fd,buf,10);perror("read/dev/tty");}write(STDOUTFILENO,MSGTRY,strlen(MSG}write(STDOUTFILENO,MSGTIMEOUT,strlen(MSGwrite(STDOUTFILENO,buf,n);return}常讀寫(xiě)多少個(gè)字節(jié)就會(huì)將讀寫(xiě)位置往后移多少個(gè)字節(jié)但是有一個(gè)例外,如果以O(shè)APPEND方式打開(kāi),每次寫(xiě)操作都會(huì)在文件末尾追加數(shù)據(jù),然后將讀寫(xiě)位置移到新的文件末尾lseek#include#include#include#includeofftlseek(intfd,offtoffset,int參數(shù)offset和whence的含義和fseek函數(shù)完全相同只不過(guò)第一個(gè)參數(shù)換成了文件描述符和fseek一樣,偏移量允許超過(guò)文件末尾,這種情況下對(duì)該文件的下一次寫(xiě)操作將延長(zhǎng)若lseek成功執(zhí)行,則返回新的偏移量,因此可用以下方法確定一個(gè)打開(kāi)文件的當(dāng)前偏移量:offofftcurrpos=lseek(fd,0,SEEK這種方法也可用來(lái)確定文件或設(shè)備是否可以設(shè)置偏移量,常規(guī)文件都可以設(shè)置偏移量,而設(shè)備一般是不可以設(shè)置偏移量的如果設(shè)備不支持lseek,則lseek返回1,并將errno設(shè)置為ESPIPE注意fseek和lseek在返回值上有細(xì)微的差別,fseek成功時(shí)返回0失敗時(shí)返回1,要返回當(dāng)前偏移量需調(diào)用fl,而lseek成功時(shí)返回當(dāng)前偏移量失敗時(shí)返回1先前我們以read終端設(shè)備為例介紹了非阻塞I/O,為什么我們不直接對(duì)STDINFILENO做非阻塞read,而要重新open一遍/dev/tty呢?因?yàn)镾TDINFILENO在程序啟動(dòng)時(shí)已經(jīng)被自動(dòng)打開(kāi)了,而我們需要在調(diào)用open時(shí)指定ONONBLOCK標(biāo)志這里介紹另外一種辦法,可以用fcntl函數(shù)改變一個(gè)已打開(kāi)的文件的屬性,可以重新設(shè)置讀、寫(xiě)、追加、非阻塞等標(biāo)志(這些標(biāo)志稱(chēng)為FileStatusFlag),而不必重新open文件#include<fcntl.h>intfcntl(intfd,intintfcntl(intfd,intcmd,longintfcntl(intfd,intcmd,structflockcmd參數(shù)下面的例子使用FGETFL和F這兩種tl命令改變STDINFILENO的屬性,加上ONONBLOCK選項(xiàng),實(shí)現(xiàn)和例28.3“非阻塞讀終端”同樣的功能用fcntl改變FileStatus#include<fcntl.h>#include<errno.h>#include#defineMSGTRY"tryagain\n"intmain(void){intn;intflags=fcntl(STDINFILENO,FGETFL);flags=ONONBLOCK;if(fcntl(STDINFILENO,FSETFL,flags)==-}n=read(STDINFILENO,buf,if(n<0)if(errno=={write(STDOUTFILENO,MSGTRY,strlen(MSGgoto}}write(STDOUTFILENO,buf,n);return0;}ioctl用于向設(shè)備發(fā)控制和配置命令,有些命令也需要讀寫(xiě)一些數(shù)據(jù),但這些數(shù)據(jù)是不能用read/write讀寫(xiě)的,稱(chēng)為Outofband數(shù)據(jù)也就是說(shuō),read/write讀寫(xiě)的數(shù)據(jù)是inband數(shù)據(jù),是I/O操作的主體,而ioctl命令傳送的是控制信息,其中的數(shù)據(jù)是輔助的數(shù)據(jù)例如,在串口線(xiàn)上收發(fā)數(shù)據(jù)通過(guò)read/write操作,而串口的波特率、校驗(yàn)位、停止位通過(guò)ioctl設(shè)置,A/D轉(zhuǎn)換的結(jié)果通過(guò)read,而A/D轉(zhuǎn)換的精度和工作頻率通過(guò)ioctl設(shè)置#include#includeintioctl(intd,intrequest,d是某個(gè)設(shè)備的文件描述符request是ioctl令,可變參數(shù)取決于request,通常是1,若成功則返回其他值,返回值也是取決于以下程序使用TIOCGWINSZ命令獲得終端設(shè)備的窗#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>int{structwinsizeif(isatty(STDOUTFILENO)==if(ioctl(STDOUTFILENO,TIOCGWINSZ,&size)<0)}printf("%drows,%dcolumns\n",size.wsrow,size.wscol);return0;}在圖形界面的終端里多次改變終端窗口的大小并運(yùn)行該程序,觀(guān)察結(jié)果習(xí)在系統(tǒng)頭文件中查找flags和mode請(qǐng)按照下述要求分別寫(xiě)出相應(yīng)的open打開(kāi)文件/home/xingwenpeng/.txt用于寫(xiě)操作,以追加方式打打開(kāi)文件/home/xingwenpeng/.txt用于寫(xiě)操作,如果該文件不存在則創(chuàng)建打開(kāi)文件/home/xingwenpeng/.txt用于寫(xiě)操作,如果該文件已存在則報(bào)錯(cuò)退出,如果該文件不存在則創(chuàng)建它創(chuàng)建一個(gè)10M的空文實(shí)現(xiàn)輸出重定向,當(dāng)C標(biāo)準(zhǔn)printf打印時(shí),打印到你指定的testmycp拷貝命令實(shí)現(xiàn)(思考如何拷 獲取圖1.3:2文件系ext2文件系圖2.1:ex2文件系我們知道,一個(gè)磁盤(pán)可以劃分成多個(gè)分區(qū),每個(gè)分區(qū)必須先用格式化工具(例如某種mkfs命令)格式化成某種格式的文件系統(tǒng),然后才能文件,格式化的過(guò)程會(huì)在磁盤(pán)上寫(xiě)一些管理布局的信息下圖是一個(gè)磁盤(pán)分區(qū)格式化成ext2文件系統(tǒng)后的布局文件系統(tǒng)中的最小單位是塊(Block),一個(gè)塊究竟多大是在格式化時(shí)確定的,例如mke2fs的b選項(xiàng)可以設(shè)定塊大小為1024、2048或4096字節(jié)而上圖中啟動(dòng)塊(BootBlock)的大小是確定的,就是1KB,啟動(dòng)塊是由PC標(biāo)準(zhǔn)規(guī)定的,用來(lái)磁盤(pán)分區(qū)信息和啟動(dòng)信息,任何文件系統(tǒng)都不能使用啟動(dòng)塊啟動(dòng)塊之后才是ext2文件系統(tǒng)的開(kāi)始,ext2文件系統(tǒng)將整個(gè)分區(qū)劃成若干個(gè)同樣大小的塊組(BlockGroup),每個(gè)塊組都由以下部分組成超級(jí)塊(SuperBlock)描述整個(gè)分區(qū)的文件系統(tǒng)信息,例如塊大小、文件系統(tǒng)版本號(hào)、上次mount的時(shí)間等等超級(jí)塊在每個(gè)塊組的開(kāi)頭都有一份拷貝塊組描述符表(GDT,GroupDescriptorTable)由很多塊組描述符組成,整個(gè)分區(qū)分成多少個(gè)塊組就對(duì)應(yīng)有多少個(gè)塊組描述符每個(gè)塊組描述符(GroupDescriptor)一個(gè)塊組的描述信息,例如在這個(gè)塊組中從哪里開(kāi)始是inode表,從哪里開(kāi)始是數(shù)據(jù)塊,空閑的inode和數(shù)據(jù)塊還有多少個(gè)等等和超級(jí)塊類(lèi)似,塊組描述符表在每個(gè)塊組的開(kāi)頭也都有一份拷貝,這些信息是非常重要的,一旦超級(jí)塊意外損壞就會(huì)丟失整個(gè)分區(qū)的數(shù)據(jù),一旦塊組描述符意外損壞就會(huì)丟失整個(gè)塊組的數(shù)據(jù),因此它們都有多份拷貝通常內(nèi)核只用到第0個(gè)塊組中的拷貝,當(dāng)執(zhí)行e2fsck檢查文件系致性時(shí),第0個(gè)塊組中的超級(jí)塊和塊組描述符表就會(huì)拷貝到其它塊組,這樣當(dāng)?shù)?個(gè)塊組的開(kāi)頭意外損壞時(shí)就可以用其它拷貝來(lái)恢復(fù),從而減少損失塊位圖(BlockBitmap)一個(gè)塊組中的塊是這樣利用的:數(shù)據(jù)塊所有文件的數(shù)據(jù),比如某個(gè)分區(qū)的塊大小是1024字節(jié),某個(gè)文件是2049字節(jié),那么就需要三個(gè)數(shù)據(jù)塊來(lái)存,即使第三個(gè)塊只存了一個(gè)字節(jié)也需要占用一個(gè)整塊;超級(jí)塊、塊組描述符表、塊位圖、inode位圖、inode表這幾部分該塊組的描述信息那么如何知道哪些塊已經(jīng)用來(lái)存儲(chǔ)文件數(shù)據(jù)或其它描述信息,哪些塊仍然空閑可用呢?塊位圖就是整個(gè)塊組中哪些塊已用哪些塊空閑的,它本身占一個(gè)塊,其中的每個(gè)bit代表本塊組中的一個(gè)塊,這個(gè)bit為1表示該塊已用,這個(gè)bit為0表示該塊空閑可用為什么用df命令統(tǒng)計(jì)整個(gè)磁盤(pán)的已用空間非??炷兀恳?yàn)橹恍枰榭疵總€(gè)塊組的塊位圖即可,而不需要搜遍整個(gè)分區(qū)相反,用du命令查看一個(gè)較大的已用空間就非常慢,因?yàn)椴豢杀苊獾匾驯檎麄€(gè)的所有文件與此相聯(lián)系的另一個(gè)問(wèn)題是:在格式化一個(gè)分區(qū)時(shí)究竟會(huì)劃出多少個(gè)塊組呢?主要的限制在于塊位圖本身必須只占一個(gè)塊用mke2fs格式化時(shí)默認(rèn)塊大小是1024字節(jié),可以用b參數(shù)指定塊大小,現(xiàn)在設(shè)塊大小指定為b字節(jié),那么一個(gè)塊可以有8b個(gè)bit,這樣大小的一個(gè)塊位圖就可以表示8b個(gè)塊的占用情況,因此一個(gè)塊組最多可以有8b個(gè)塊,如果整個(gè)分區(qū)有s個(gè)塊,那么就可以有s/(8b)個(gè)塊組格式化時(shí)可以用g參數(shù)指定一個(gè)塊組有多少個(gè)塊,但是通常不需要手動(dòng)指定,mke2fs工具會(huì)計(jì)算出最優(yōu)的數(shù)值inode位圖(inodeBitmap)和塊位圖類(lèi)似,本身占一個(gè)塊,其中每個(gè)bitinode表(inodeTable)我們知道,一個(gè)文件除了數(shù)據(jù)需要之外,一些描述信息也需要,例如文件類(lèi)型(常規(guī)、、符號(hào)等),權(quán)限,文件大小,創(chuàng)建/修改/訪(fǎng)問(wèn)時(shí)間等,也就是lsl命令看到的那些信息,這些信息存在inode中而不是數(shù)據(jù)塊中每個(gè)inode表占多少個(gè)塊在格式化時(shí)就要決定并寫(xiě)入塊組描述符中,mke2fs格式化工具的默認(rèn)策略是一個(gè)塊組有多少個(gè)8KB就分配多少個(gè)inode由于數(shù)據(jù)塊占了整個(gè)塊組的絕大部分,也可以近似認(rèn)為數(shù)據(jù)塊有多少個(gè)8KB就分配多少個(gè)inode,換句話(huà)說(shuō),如果平均每個(gè)文件的大小是8KB,當(dāng)分區(qū)存滿(mǎn)的時(shí)候inode表會(huì)得到比較充分的利用,數(shù)據(jù)塊也不浪費(fèi)如果這個(gè)分區(qū)存的都是很大的文件(比如),則數(shù)據(jù)塊用完的時(shí)候inode會(huì)有一些浪費(fèi),如果這個(gè)分區(qū)存的都是很小的文件(比如源代碼),則有可能數(shù)據(jù)塊還沒(méi)用完inode就已經(jīng)用完了,數(shù)據(jù)塊可能有很大的浪費(fèi)如果用戶(hù)在格式化時(shí)能夠?qū)@個(gè)分區(qū)以后要的文件大小做一個(gè),也可以用mke2fs的i參數(shù)手動(dòng)指定每多少個(gè)字節(jié)分配一個(gè)inode數(shù)據(jù)塊(DataBlock)對(duì)于 ,該 下的所有文件名和 名在數(shù)據(jù)塊中,注意文件名保存在它所在的數(shù)據(jù)塊中,除文件名之外,lsl命令看到的其它信息都保存在該文件的inode中注意這個(gè)概念 也是一種文件,是一種特殊類(lèi)型的文對(duì)于符號(hào),如果目標(biāo)路徑名較短則直接保存在inode中以便更快地查找,如果目標(biāo)路徑名較長(zhǎng)則分配一個(gè)數(shù)據(jù)塊來(lái)保存設(shè)備文件、FIFO和socket等特殊文件沒(méi)有數(shù)據(jù)塊,設(shè)備文件的主設(shè)備號(hào)和次設(shè)備號(hào)保存在inode中編編碼件類(lèi)RegularCharacterBlock5567Named圖2.2:數(shù)據(jù)塊尋從上圖可以看出,索引項(xiàng)Blocks[13]指向兩級(jí)的間接尋址塊,最多可表示(b/4)2+b/4+12個(gè)數(shù)據(jù)塊,對(duì)于1K的塊大小最大可表示64.26MB的文件索引項(xiàng)Blocks[14]指向三級(jí)的間接尋址塊,最多可表示(b/4)3+(b/4)2+b/4+12個(gè)數(shù)據(jù)塊,對(duì)于1K的塊大小最大可表示可見(jiàn),這種尋址方式對(duì)于不超過(guò)12個(gè)數(shù)據(jù)塊的小文件是非??斓?,文件中的任意數(shù)據(jù)只需要兩次讀盤(pán)操作,一次讀inode(也就是讀索引項(xiàng))一次讀數(shù)據(jù)塊而大文件中的數(shù)據(jù)則需要最多五次讀盤(pán)操作:inode、一級(jí)間接尋址塊、二級(jí)間接尋址塊、三級(jí)間接尋址塊、數(shù)據(jù)塊實(shí)際上,磁盤(pán)中的inode和數(shù)據(jù)塊往往已經(jīng)被內(nèi)核緩存了,讀大文件的效率也不會(huì)太低#include<sys/types.h>#include<sys/types.h>#include<sys/stat.h>#include<unistd.h>intstat(constchar*path,structstat*buf);intfstat(intfd,structstat*buf);intlstat(constchar*path,structstatstructstatdevt stdev; /*IDofdevicecontainingfile*/inot stino; /*inodenumber*/mode st /*protectionnlinkt stnlink; /*numberofhardlinks*/uidt stuid; /*userIDofowner*/gidt stgid; /*groupIDofowner*/devt strdev; /*deviceID(ifspecialfile)*/offt stsize; /*totalsize,inbytes*/blksizetstblksize;/*blocksizeforfilesystemI/Otttstblocks;/*numberof512Bblocksallocated*/timet statime; /*timeoflastaccess*/timet stmtime; /*timeoflastmodification*/time st /*timeoflaststatuschangestat既有命令也有同名函數(shù),用來(lái)獲取文件Inode里主要信息,stat符號(hào)鏈stat里面時(shí)atime(最近時(shí)間):mtime(最近更改時(shí)間):指最近修改文件內(nèi)容的時(shí)間ctime(最近改動(dòng)時(shí)間):指最近改動(dòng)Inode的時(shí)間##access#include#includeintaccess(constchar*pathname,intWOK否有寫(xiě)權(quán)XOK否有執(zhí)行權(quán)FOK測(cè)試個(gè)文件是否存有效用戶(hù)#include#include od(constchar*path,modetmode);intf od(intfd,modetmode);#include#includeintchown(constchar*path,uidtowner,gidtgroup);intfchown(intfd,uidtowner,gidtgroup);intlchown(constchar*path,uidtowner,gidtchown使用時(shí)必須擁有root權(quán)圖2.3:mode標(biāo)#include<unistd.h>#include<unistd.h>#include<sys/types.h>inttruncate(constchar*path,offtlength);intftruncate(intfd,offtlength);創(chuàng)建一個(gè)硬 下的記錄項(xiàng)和把inode硬計(jì)數(shù)減1,當(dāng)硬計(jì)數(shù)#include#includeintlink(constchar*oldpath,constchar *硬通常要求位于同一文件系統(tǒng)中,POSIX允許夸文件系符號(hào)沒(méi)有文件系統(tǒng)限通常不允許創(chuàng) 的硬,某些unix系統(tǒng)下超級(jí)用戶(hù)可以創(chuàng) 的硬創(chuàng) 項(xiàng)以及增加硬計(jì)數(shù)應(yīng)當(dāng)是一個(gè)原子操intintsymlink(constchar*oldpath,constchar 讀符號(hào)所指向的文件名字,不讀文件內(nèi)ssizessizetreadlink(constchar*path,char*buf,sizetintintunlink(constchar如果是,刪除符,數(shù)減1,當(dāng)減為0數(shù)據(jù)塊和數(shù)為0,但有進(jìn)程已打開(kāi)該文件,并持有文件描述符,則等該進(jìn)程關(guān)閉該文件時(shí),kernel4用該特性創(chuàng)建臨時(shí)文件,先open或creat創(chuàng)建個(gè)文件,馬上unlink此文文件#include#includeintrename(constchar*oldpath,constchar #include#includeintchdir(constchar*path);intfchdir(intfd);改變當(dāng)前進(jìn)程的工獲取當(dāng)前進(jìn)程的工#include#includechar*getcwd(char*buf,sizet#include#includelongfpathconf(intfd,intname);longpathconf(char*path,intname);操#include<sys/stat.h>#include<sys/stat.h>intmkdir(constchar*pathname,modet#include#includeintrmdir(constchar#include<sys/types.h>#include<sys/types.h>#include<dirent.h>DIR*opendir(constchar*name);DIR*fdopendir(intfd);#include#includestructdirent*readdir(DIRstructdirentinooffddunsignedshortdunsignedchard/*inodenumber/*offsettothenextdirent/*lengthofthisrecord/*typeoffile;notsupportedbyallfilesystemtypes dname[256];/*filenamereaddir每次返回一條記錄項(xiàng),DIR*指針指向下一條#include<sys/types.h>#include<sys/types.h>#include<dirent.h>voidrewinddir(DIR 指針恢復(fù) 的起始位#include<dirent.h>#include<dirent.h> #includevoidseekdir(DIR*dirp,long#include<sys/types.h>#include<sys/types.h>#include<dirent.h>intclosedir(DIR遞歸列 中的文件列#include<sys/stat.h>#include<unistd.h>#include<dirent.h>#include<stdio.h>#include<string.h>#defineMAXPATH/*dirwalk applyfcntoallfilesindir*/voiddirwalk(char*dir,void(*fcn)(char{structdirent*dp;DIR*dfd;if((dfd=opendir(dir))==NULL)fprintf(stderr,"dirwalkcantopen%s\n",dir);}while((dp=readdir(dfd))={if(strcmp(dp->dname,".")==strcmp(dp->dname,"..")==0) /*skipselfandparent*/if(strlen(dir)+strlen(dp->dname)+2>sizeof(name))fprintf(stderr,"dirwalkname%s%stoodir,dp->delsesprintf(name,"%s/%s",dir,dp->dname);}}}/*fsize printthesizeandnameoffile"name"*/voidfsize(char*name){structstatif(stat(name,&stbuf)==-1)fprintf(stderr,"fsizecantaccess%s\n",name);}if((stbuf.stmode&SIFMT)==SIFDIR)dirwalk(name,fsize);printf("%8ld%s\n",stbuf.stsize,}intmain(intargc,char{if(argc==1)/*defaultcurrentdirectory*/while(--argc>return0;} 環(huán)VFS虛擬文件系Linux支持各種各樣的文件系統(tǒng)格式,如ext2、ext3、reiserfs、FAFS、iso9660等等,不同的磁盤(pán)分區(qū)、光盤(pán)或其它設(shè)備都有不同的文件系統(tǒng)格式,然而這些文件系統(tǒng)都可以mount到某個(gè)下,使我們看到一個(gè)的樹(shù),各種文件系統(tǒng)上的和文件我們用ls命令看起來(lái)是一樣的,讀寫(xiě)操作用起來(lái)也都是一樣的,這是怎么做到的呢?Linux內(nèi)核在各種不同的文件系統(tǒng)格式之上做了一個(gè)抽象層,使得文件、、讀寫(xiě)等概念成為抽象層的概念,因此各種文件系統(tǒng)看起來(lái)用起來(lái)都一樣,這個(gè)抽象層稱(chēng)為虛擬文件系統(tǒng)(VFS,VirtualFilesystem)圖2.4:虛擬文件系#include<unistd.h>#include<unistd.h>intdup(intintdup2(intoldfd,intdup和dup2都可用來(lái)一個(gè)現(xiàn)存的文件描述符,使兩個(gè)文件描述符指向同一個(gè)file結(jié)構(gòu)體如果兩個(gè)文件描述符指向同一個(gè)file結(jié)構(gòu)體,F(xiàn)ileStatusFlag和讀寫(xiě)位置只保存一份在file結(jié)構(gòu)體中,并且file結(jié)構(gòu)體的計(jì)數(shù)是2如果兩次open同一文件得到兩個(gè)文件描述符,則每個(gè)描述符對(duì)應(yīng)一個(gè)不同的file結(jié)構(gòu)體,可以有不同的FileStatusFlag和讀寫(xiě)位置請(qǐng)注意區(qū)分這兩種情況#include<unistd.h>#include<unistd.h>#include<fcntl.h>#include<stdio.h>#include<stdlib.h>#include<string.h>int{intfd,savecharmsg[]="Thisisafd=open("somefile",ORDWROCREAT,SIRUSRSIWUSR);if(fd<0){}savefd=dup(STDOUTFILENO);dup2(fd,STDOUTFILENO);write(STDOUTFILENO,msg,strlen(msg));dup2(savefd,STDOUTFILENO);write(STDOUTFILENO,msg,strlen(msg));close(savefd);return}圖2.5:練實(shí)實(shí)現(xiàn)ls-l實(shí)現(xiàn) -l功能,可以解析出文件所有者和文件所有組。(偏難實(shí)現(xiàn)rm刪除命令,rmrm*注意,千萬(wàn)不要在你有代碼的 下做測(cè)試,防止刪除你的有用文件,友情提示(rmdir/unlink和遞歸遍歷目錄)從文件里面讀出1000個(gè)隨機(jī)數(shù),進(jìn)行排序,再寫(xiě)到 文件中。(考慮使用重定向3進(jìn)我們知道,每個(gè)進(jìn)程在內(nèi)核中都有一個(gè)進(jìn)程控制塊(PCB)來(lái)進(jìn)程相關(guān)的信息,Linux內(nèi)核的進(jìn)程控制塊是taskstruct結(jié)構(gòu)體現(xiàn)在我們?nèi)媪私庖幌缕渲卸加心男┬胚M(jìn)程idid,在C語(yǔ)言中用pidt類(lèi)型表示,其實(shí)就是一個(gè)非進(jìn)程切換時(shí)需要保存和恢復(fù)的一些CPU寄存當(dāng)前工 (CurrentWorkingumask文件描述符表,包含很多指向file用戶(hù)id和組控制終端、Session和進(jìn)進(jìn)程可以使用的資源上限(Resource目前大家并不需要理解這些信息的細(xì)節(jié),在隨后課講到某一項(xiàng)時(shí)會(huì)再次提醒大家它是保存在PCB中的fork和exec是本章要介紹的兩個(gè)重要的系統(tǒng)調(diào)用fork的作用是根據(jù)一個(gè)現(xiàn)有的進(jìn)程復(fù)制出一個(gè)新進(jìn)程,原來(lái)的進(jìn)程稱(chēng)為父進(jìn)程(ParentProcess),新進(jìn)程稱(chēng)為子進(jìn)程(ChildProcess)系統(tǒng)中同時(shí)運(yùn)行著很多進(jìn)程,這些進(jìn)程都是從最初只有一個(gè)進(jìn)程開(kāi)始一個(gè)一個(gè)出來(lái)的在S下輸入命令可以運(yùn)行一個(gè)程序,是因?yàn)镾進(jìn)程在用戶(hù)輸入我們知道一個(gè)程序可以多次加載到內(nèi)存,成為同時(shí)運(yùn)行的多個(gè)進(jìn)程,例如可以同時(shí)開(kāi)多個(gè)終端窗口運(yùn)行/bin/bash,另一方面,一個(gè)進(jìn)程在調(diào)用exec前后也可以分別執(zhí)行兩個(gè)不同的程序,例如在S提示符下輸入命令ls,首先f(wàn)ork創(chuàng)建子進(jìn)程,這時(shí)子進(jìn)程仍在執(zhí)行/bin/bash程序,然后子進(jìn)程調(diào)用exec執(zhí)行新的程序/bin/ls圖進(jìn)程環(huán)libc中定義的全局變量environ指向環(huán)境變量表,environ沒(méi)有包含在任何頭文件中,所以在使用時(shí)要用extern例如:intmain(void){inti;for(i=0;environ[i]=NULL;i++)printf("%s\n",return}由于父進(jìn)程在調(diào)用fork創(chuàng)建子進(jìn)程時(shí)會(huì)把自己的環(huán)境變量表也給子進(jìn)程,所按照慣例,環(huán)境變量字符串都是name=value這樣的形式,大多數(shù)name由大寫(xiě)字母加下劃線(xiàn)組成,一般把name的部分叫做環(huán)境變量,value的部分則是環(huán)境變量的值環(huán)境變量定義了進(jìn)程的運(yùn)行環(huán)境,一些比較重要的環(huán)境變量的含義如下:可執(zhí)行文件的搜索路徑ls命令也是一個(gè)程序,執(zhí)行它不需要提供完整的路徑名/bin/ls,然而通常我們執(zhí)行當(dāng)前下的程序a.out卻需要提供完整的路徑名./a.out,這是因?yàn)镻ATH環(huán)境變量的值里面包含了ls命令所在的/bin,卻不包含a.out所在的目錄PATH環(huán)境變量的值可以包含多個(gè),用:號(hào)隔開(kāi)在S中用echo命令可以查$echoS當(dāng)前終端類(lèi)型,在圖形界面終端下它的值通常是xterm,終端類(lèi)型決定了一些程序的輸出顯示方式,比如圖形界面終端可以顯示漢字,而字符終端一般不行 用environ指針可以查看所有環(huán)境變量字符串,但是不夠方便,如果給出name要在環(huán)境變量表中查找它對(duì)應(yīng)的value,可以用getenv函數(shù)#include#include getenv的返回值是指向value的指針,若未找到則為NULL修改環(huán)境變量可以#include#includeintsetenv(constchar*name,constchar*value,intrewrite);voidunsetenv(constchar*name);putenv和setenv函數(shù)若成功則返回為0,若出錯(cuò)則返回非0setenv將環(huán)境變量name的值設(shè)置為value如果已存在環(huán)境變量name,那么unsetenv刪除name的定義即使name沒(méi)有定義也不返回錯(cuò)誤例修改環(huán)境變量#include<stdio.h>int{printf("PATH=%s\n",getenv("PATH"));setenv("PATH"," o",1);printf("PATH=%s\n",getenv("PATH"));return0;}進(jìn)程狀畫(huà)圖演修改進(jìn)程資源限制,軟限制可改,最大值過(guò)硬限制,硬限制只有root用戶(hù)可以改#include<sys/time.h>#include<sys/time.h>#include<sys/resource.h>intgetrlimit(intresource,structrlimitintsetrlimit(intresource,conststructrlimit查看進(jìn)程資ulimit-a進(jìn)程原pidtfork(void);++父 返回子進(jìn)程+子 返回#include<sys/types.h>#include<sys/types.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>int{pidtpid;pidtpid;intn;pid=fork();if(pid<0)}if(pid==0)message="Thisisthechild\n";n=6;}elsemessage="Thisistheparent\n";n=3;}for(;n>0;n--}return}圖3.2:#include<sys/types.h>#include<sys/types.h>#include<unistd.h>pidt //返回調(diào)用進(jìn)程的PIDpidtgetppid(void回調(diào)用進(jìn)程父進(jìn)程的PID#include<unistd.h>#include<unistd.h>uidtgetuid(void);//返回實(shí)際用戶(hù)ID#include<unistd.h>#include<sys/types.h>#include<unistd.h>#include<sys/types.h>gidtgetgid(void回實(shí)際用戶(hù)組IDgidtgetegid(void返回有效用戶(hù)組用于fork后馬上調(diào)用exec父子進(jìn)程,共用同一地址空間,子進(jìn)程如果沒(méi)有馬上exec而是修改了父進(jìn)程出得到的變量值,此修改會(huì)在父進(jìn)生效現(xiàn)在fork已經(jīng)具備讀時(shí)共享寫(xiě)時(shí)機(jī)制,vfork逐漸廢exec用fork創(chuàng)建子進(jìn)程后執(zhí)行的是和父進(jìn)程相同的程序(但有可能執(zhí)行不同的代碼分支),子進(jìn)程往往要調(diào)用一種exec函數(shù)以執(zhí)行另一個(gè)程序當(dāng)進(jìn)程調(diào)用一種exec函數(shù)時(shí),該進(jìn)程的用戶(hù)空間代碼和數(shù)據(jù)完全被新程序替換,從新程序的啟動(dòng)例程開(kāi)始執(zhí)行調(diào)用exec并不創(chuàng)建新進(jìn)程,所以調(diào)用exec前后該進(jìn)程的id并未改變其實(shí)有六種以exec開(kāi)頭的函數(shù),統(tǒng)稱(chēng)exec函數(shù)#include#includeintexecl(constchar*path,constchar*arg,...);intexeclp(constchar*file,constchar*arg,intexecle(constchar*path,constchar*arg,...,char*constenvp[]);intexecv(constchar*path,char*constargv[]);intexecvp(constchar*file,char*constintexecve(constchar*path,char*constargv[],char*const這些函數(shù)如果調(diào)用成功則加載新的程序從啟動(dòng)代碼開(kāi)始執(zhí)行,不再返回,如果調(diào)用出錯(cuò)則返回1,所以exec函數(shù)只有出錯(cuò)的返回值而沒(méi)有成功的返回值這些函數(shù)原型看起來(lái)很容易混,但只要掌握了規(guī)律就很好記不帶字母p(表示path)的exec函數(shù)第一個(gè)參數(shù)必須是程序的相對(duì)路徑或絕對(duì)路徑,例如“/bin/ls”或“./a.out”,而不能是“l(fā)s”或“a.out”對(duì)于帶字母p的函數(shù):如果參數(shù)中包含/,則將其視為路徑名否則視為不帶路徑的程序名,在PATH環(huán)境變量 列表中搜索這個(gè)程帶有字母l(表示list)的exec函數(shù)要求將新程序的每個(gè)命令行參數(shù)都當(dāng)作一個(gè)參數(shù)傳給它,命令行參數(shù)的個(gè)數(shù)是可變的,因此函數(shù)原型中有…,…中的最后一個(gè)可變參數(shù)應(yīng)該是NULL,起sentinel的作用對(duì)于帶有字母v(表示vector)的函數(shù),則應(yīng)該先構(gòu)造一個(gè)指向各參數(shù)的指針數(shù)組,然后將該數(shù)組的首地址當(dāng)作參數(shù)傳給它,數(shù)組中的最后一個(gè)指針也應(yīng)該是NULL,就像main函數(shù)的argv參數(shù)或者環(huán)境變量表一樣對(duì)于以e(表示environment)結(jié)尾的exec函數(shù),可以把一份新的環(huán)境變量表傳給它,其他exec函數(shù)仍使用當(dāng)前的環(huán)境變量表執(zhí)行新程序exec調(diào)用舉charchar*constpsargv[]={"ps","-o",m",char*constpsenvp[]={"PATH=/bin/usr/bin","TERM=console",execl("/bin/ps","ps","-o",execv("/bin/ps",psargv);execle("/bin/ps","ps","-o","pid,ppid,pgrpexecve("/bin/ps",psargv,psenvp);execlp("ps","ps","-o","pid,ppid,pgrpexecvp("ps",psargv);m",m",NULL,psm",事實(shí)上,只有execve是真正的系統(tǒng)調(diào)用,其它五個(gè)函數(shù)最終都調(diào)用execve,所以exece在man手冊(cè)第2節(jié),其它函數(shù)在man手冊(cè)第3節(jié)這些函數(shù)之間的關(guān)系如下圖所示圖3.3:exec函數(shù)一個(gè)完整的#include#include#includeint{execlp("ps","ps","-o","pid,ppid,pgrpperror("execps");}m",由于exec函數(shù)只有錯(cuò)誤返回值,只要返回了一定是出錯(cuò)了,所以不需要判斷它的返回值直接在后面調(diào)用perror注意在調(diào)用execlp時(shí)傳了兩個(gè)“ps”參數(shù),第一個(gè)“ps”是程序名,execlp函數(shù)要在PATH環(huán)境變量中找到這個(gè)程序并執(zhí)行它,而第二個(gè)“ps”是第一個(gè)命令行參數(shù),execlp函數(shù)并不關(guān)心它的值,只是簡(jiǎn)單地把它傳給ps程調(diào)用exec后,原來(lái)打開(kāi)的文件描述符仍然是打開(kāi)的利用這一點(diǎn)可以實(shí)現(xiàn)I/O重定向/*upper.c*//*upper.c*/int{intwhile((ch=getchar())={}return}/*wrapper.c*//*wrapper.c*/#include<stdio.h>#include<fcntl.h>intmain(intargc,char{intif(argc=2)fputs("usagewrapperfile\n",stderr);}fd=open(argv[1],ORDONLY);if(fd<0){}dup2(fd,STDINexecl("./upper","upper",NULL);perror("exec./upper");}wrapper程序?qū)⒚钚袇?shù)當(dāng)作文件名打開(kāi),將標(biāo)準(zhǔn)輸入重定向到這個(gè)文件,然后調(diào)用exec執(zhí)

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論