一要出、Linux系統(tǒng)概述
不加引號(hào)可理解為宏洗贰,直接替換垮斯,單引號(hào)中特殊字符會(huì)被解釋為普通字符秸妥,雙引號(hào)中$,,'還是特殊逗载。反引號(hào)可嵌套但里面的要加\腾降。幫助命令man ls或ls --help碌奉。netstat -a顯示所有連接中的socket劝贸。
二潮罪、C語(yǔ)言編程基礎(chǔ)
逗號(hào)運(yùn)算符功能是把兩個(gè)表達(dá)式連接起來(lái)組成一個(gè)表達(dá)式康谆,分別求值并把表達(dá)式2的值作為整個(gè)逗號(hào)表達(dá)式的值 。char* str = "AA BB"; char string[] = "AA BB"; 字符串指針是個(gè)變量可以改變指向不同的位置但不能改其值嫉到,但可以改變字符串?dāng)?shù)組的值沃暗。getchar(),putchar()。printf里的%d與%i效果一樣何恶。%g以%f%e中較短的輸出寬度輸出孽锥。scanf對(duì)于%f可指定數(shù)據(jù)寬度但不能指定精度如%10f正確便%10.2f錯(cuò)誤,對(duì)long必須是%ld,對(duì)double必須是%lf或%le惜辑。而%f/s/d中的表使對(duì)應(yīng)的輸入數(shù)據(jù)不賦值給相應(yīng)的變量唬涧。外部變量(即全局變量)是定義在函數(shù)外部的,如不在文件開頭定義則作用域?yàn)槎x位置到結(jié)尾盛撑,可在使用前用extern進(jìn)行聲明就可以正常使用碎节。只有局部自動(dòng)變量和形參可以作為寄存器變量,且不能使用&求寄存器變量的地址抵卫。puts,gets(fgets)(不是以空格為結(jié)束而是以換行為結(jié)束標(biāo)志)狮荔。strlen不含\0的實(shí)際長(zhǎng)度。 指向函數(shù)的指針:返回值類型 (指針變量名) ()介粘,()優(yōu)先級(jí)比高殖氏,不加()指針變量名首先會(huì)與后面的()結(jié)合。 指向結(jié)構(gòu)體的指針使用時(shí)如(ptr_struct).name中()不可省略碗短,因?yàn)?的優(yōu)先級(jí)高于受葛。不能引用共用體變量,只能引用其成員偎谁。共用體中起作用的成員是最后一次存放的成員总滩,在存入一個(gè)新成員后,原有成員就失去作用巡雨,共用體變量的地址和它的各成員的地址都是同一地址闰渔,不能在定義共用體變量時(shí)對(duì)它時(shí)行初始化,不能對(duì)共用體變量名賦值铐望,也不能企圖引用變量名來(lái)得到一個(gè)值冈涧。更改值時(shí)直接對(duì)其成員賦值即可。 建立鏈表typedef struct node{ int data; struct node* link;}NODE; NODE* head; 位域:為節(jié)省空間正蛙,把一個(gè)字節(jié)中的二進(jìn)制位劃分為幾個(gè)不同的區(qū)域督弓,并說(shuō)明每個(gè)區(qū)域的位置,每個(gè)域有一個(gè)域名乒验,允許在程序中按域名進(jìn)行操作愚隧。struct a{char b : 3; char c : 3; char d :2;};一個(gè)位域必須存儲(chǔ)在同一個(gè)字節(jié)中,不能跨兩個(gè)字節(jié)锻全,使用時(shí)和結(jié)構(gòu)體方式一樣狂塘。 預(yù)處理可提高編程效率,它們不是C語(yǔ)言本身的組成部分鳄厌,不能直接編譯荞胡,帶#的表預(yù)處理命令。#error error-message 強(qiáng)制編譯停止了嚎,并輸出錯(cuò)誤信息泪漂,控制編譯,用于判斷編譯是按設(shè)定的流程進(jìn)行的。#line number "filename"改變LINE和FILE的內(nèi)容窖梁。
三赘风、vi與Emacs編輯器
vi即Visual Interface。輸入":"纵刘、"/"邀窃、"?"進(jìn)入底行模式。插入模式下任何輸入都作為文件內(nèi)容假哎,不能當(dāng)作底行命令瞬捕,反之亦然,因此兩者切換要經(jīng)過(guò)命令行模式舵抹。命令模式下:移動(dòng)光標(biāo):^,,dd,ndd,nd+上/下方向鍵惧蛹。其他命令:r扇救,R多次,u香嗓,U迅腔,.(重復(fù)上次命令),ZZ靠娱,%沧烈。插入模式下:i,I,a,A,o,O。底行模式也叫最后行模式像云,用于搜尋锌雀、替代、保存迅诬、退出腋逆,q,q!,x,x!,w,w!,wq,w filename,w! filename,r filename,bn, bp,s/pattern1/pattern2/g,%s/pattern1/pattern2/g,g/pattern1/s//pattern2, num1,num2s/pattern1/pattern2/g,/向后查找侈贷,?前闲礼,N繼續(xù)查找下一個(gè)。在替換功能中說(shuō)明:%指所有行铐维,g表對(duì)行中每個(gè)匹配都替換,否則只替換行的第一個(gè)匹配慎菲,s表示其后是一個(gè)替換命令嫁蛇。 Emacs即Editor Macros編輯器宏的縮寫。退出是Ctrl+X露该,Ctrl+C睬棚。Emacs默認(rèn)工作目錄是當(dāng)前用戶的主目錄。當(dāng)打開或編輯一個(gè)文件時(shí),Emacs將自動(dòng)創(chuàng)建一個(gè)緩沖區(qū)抑党,一個(gè)文件對(duì)應(yīng)一個(gè)緩沖區(qū)包警,用戶在窗口進(jìn)行編輯操作,輸入的字符將會(huì)被暫存在緩沖區(qū)底靠,當(dāng)用戶執(zhí)行保存操作時(shí)害晦,Emacs會(huì)自動(dòng)將緩沖區(qū)中的內(nèi)容保存到當(dāng)前打開文件中,Emacs允許一次打開多個(gè)文件暑中,即使用多個(gè)緩沖區(qū)壹瘟。文件操作:C-x,C-f,C-x d,C-x i,Cx
四鳄逾、gcc編譯器與gdb調(diào)試器
gcc(GNU Compiler Collection)稻轨,預(yù)處理、編譯雕凹、匯編殴俱、鏈接。后綴名.c,.a(由目標(biāo)文件構(gòu)成的檔案庫(kù)文件),.C(.cc,.cxx)C++源文件,.h,.i經(jīng)預(yù)處理的C源文件,.ii,,.o,s匯編語(yǔ)言源代碼文件,.S經(jīng)預(yù)處理的匯編語(yǔ)言源代碼文件枚抵。編譯選項(xiàng):-o,-c,-S,-E,-g,-v,-I dir(在頭文件的搜索路徑列表中添加dir目錄) ,-L dir, -static,-library,-O,-O2,-O3,-Wall(顯示所有警告),-Werror(警告當(dāng)作錯(cuò)誤),-w,-pedantic线欲。先調(diào)用預(yù)處理程序cpp(#include,#ifdef俄精,#definet等询筏,gcc -E h.c -o h.i),再調(diào)用ccl竖慧、as編譯出目標(biāo)代碼(gcc -S h.i -o h.s進(jìn)行編譯生成匯編語(yǔ)言嫌套,gcc -c h.s -o h.o進(jìn)行匯編把前面生成的文件匯編成具體CPU上的目標(biāo)代碼模塊,生成機(jī)器語(yǔ)言.o)圾旨,最后調(diào)用ld生成可執(zhí)行程序(gcc h.o -o hello)踱讨。優(yōu)化:-O即-O1,一般包括線程跳轉(zhuǎn)和延遲退棧砍的,-O2除O1外還包括調(diào)整處理器指令調(diào)度等痹筛,-O3在O2上還包括循環(huán)展開等。time ./h可得到程序運(yùn)行大致時(shí)間廓鞠,time的結(jié)果中帚稠,real指進(jìn)程總時(shí)間,它和系統(tǒng)負(fù)載有關(guān)(包括進(jìn)程調(diào)度床佳、切換的時(shí)間)滋早,user指進(jìn)程中用戶指令的時(shí)間,sys指進(jìn)程中內(nèi)核代用戶指令的時(shí)間砌们,user和sys的和被稱為CPU時(shí)間杆麸。 程序開發(fā)的時(shí)候搁进,優(yōu)化會(huì)增加編譯時(shí)間先不優(yōu)化,發(fā)布的時(shí)候再優(yōu)化昔头,內(nèi)存小的時(shí)候如嵌入式設(shè)備不要優(yōu)化饼问,會(huì)增加代碼體積。函數(shù)庫(kù)實(shí)質(zhì)上是一些頭文件和庫(kù)文件(.so揭斧,.a)的集合莱革。Linux下大多函數(shù)的頭文件默認(rèn)路徑是/usr/include,庫(kù)文件/usr/lib未蝌,添加頭文件路徑:gcc foo.c -I /home/chiguo/include -o foo驮吱。對(duì)于庫(kù)文件,如有個(gè)libfoo.so萧吠,gcc foo.c -L /home/chiguo/lib -lfoo -o foo左冬,其中-lfoo,中-l指示gcc鏈接libfoo.so纸型。Linux下庫(kù)命令應(yīng)以lib開頭拇砰。動(dòng)態(tài)庫(kù)是在運(yùn)行時(shí)動(dòng)態(tài)加載的,靜態(tài)庫(kù)是在編譯時(shí)靜態(tài)加載的狰腌。默認(rèn)gcc優(yōu)先使用動(dòng)態(tài)庫(kù)除破,不存在時(shí),才考慮用靜態(tài)琼腔,也可-static強(qiáng)制使用靜態(tài)瑰枫,如目錄下有l(wèi)ibfoo.so,libfoo.a,可用gcc foo.c -L /home/chiguo/lib -static -lfoo -o foo丹莲。gcc f1.c f2.c f3.c -o f相當(dāng)于先對(duì)每個(gè)生成.o光坝,再鏈接,即最后gcc f1.o f2.o f3.o -o f甥材。使用管道盯另,可避免生成臨時(shí)文件,它實(shí)質(zhì)是進(jìn)程間的通信方式洲赵,可用來(lái)同時(shí)連接兩個(gè)程序(進(jìn)程)其中一個(gè)輸出直接作為另一個(gè)的輸入鸳惯,避免生成臨時(shí)文件,但卻要消耗更多的內(nèi)存叠萍,大型程序效果很明顯芝发,gcc -pipe -Wall foo.c -o foo。 使用-g,-ggdb增加調(diào)試信息苛谷,也分級(jí)-g1,-g2(默認(rèn)),-g3后德,g1只包含回溯跟蹤、堆棧等抄腔,g2在g1上增加包括行號(hào)瓢湃、局部或外部變量信息等,g3在g2上增加源代碼中定義的宏赫蛇。GDB,GNU Debugger绵患。file,run,kill,step,next,break,print(還可對(duì)變量賦值),display,list,quit,watch,backtrace,frame n(定位到發(fā)生錯(cuò)誤代碼段,結(jié)合bt使用),examine,jump,singal,return,call,make,shell悟耘。設(shè)置斷點(diǎn):break(b或list,jump)后接 fun,line,+-offset,filename:linenum(func),address, ...if <condition> 打印變量:print [</f>] <expr>落蝙,f是格式,如/x表16進(jìn)制暂幼,d,u,o,t,a,c,f筏勒,當(dāng)使用print命令時(shí)每個(gè)print都會(huì)被gdb刻錄下來(lái),并被以2...方式給每個(gè)print命令編號(hào)旺嬉,如果變量較長(zhǎng)管行,可直接引用此。pc時(shí)i=0旷祸。環(huán)境變量沒(méi)有類型。 查看寄存器的值info registers/all-registers/registers<regname,...>讼昆,也可直接print $ip托享。 查看源代碼:list line/fun/-,set listsize <count>,show listsize,list<first>,<last>,list,<last>,list+ 查看源代碼的內(nèi)存:info line加line/fun/filename:line/filename:fun浸赫。 disassemble fun可顯示匯編闰围。 在調(diào)試時(shí)修改變量值:print x=8。 跳轉(zhuǎn)執(zhí)行:jump linespec(可是文件行號(hào)既峡,可是file:line格式羡榴,可是+num這種偏移量)也可jump addr。jump不會(huì)改變程序棧中的內(nèi)容运敢,所以從一個(gè)函數(shù)跳到另一個(gè)時(shí)校仑,當(dāng)函數(shù)運(yùn)行完返回進(jìn)行彈棧操作時(shí)必然發(fā)生錯(cuò)誤所以最好在同一函數(shù)中跳轉(zhuǎn)忠售。 使用single <single>可產(chǎn)生信號(hào)量給被調(diào)試的程序如中斷Ctrl+C,shell的kill命令發(fā)信息給被調(diào)試程序時(shí)迄沫,由gdb截獲稻扬,而singal直接發(fā)給被調(diào)試程序。 強(qiáng)制函數(shù)返回可用return <expr>羊瘩。強(qiáng)制函數(shù)調(diào)用call <expr>泰佳。
五、make的使用和Makefile的編寫
make執(zhí)行后有3個(gè)退出碼尘吗,成功逝她,出錯(cuò)返回1,如果用了-q選項(xiàng)且make使得一些目標(biāo)不需要更新睬捶,返回2黔宛。名字為Makfile或makefile或makefile文件夾下文件。規(guī)則:target ...:prerequisites ... command ...... 侧戴。make [options] [target] ...其中選項(xiàng)可是-f filename,-C dirname,-e,-k,-n,-p,-r,-s,-S,-t,-I,-V宁昭。一條規(guī)則或命令在一行中寫不完時(shí)用""表?yè)Q行 。清空中間文件make clean酗宋。目標(biāo)名稱慣例:all,clean,distclean,install积仗。命令要以tab開頭(或與prerequisites在同行,用蜕猫;分隔)寂曹。make會(huì)比較targets文件和prerequisites文件的修改日期,如果prerequisites更新或targets不存在回右,make會(huì)執(zhí)行后續(xù)定義的命令隆圆,如自定義標(biāo)簽但不定義依賴性,則不會(huì)自己執(zhí)行翔烁,可make命令后顯示指出此標(biāo)簽來(lái)執(zhí)行渺氧,如用于clean中間文件,或打包蹬屹、備份等侣背。makefile里的第一條規(guī)則中的第一個(gè)目錄會(huì)作為最終目標(biāo)。Makefile主要包含5方面內(nèi)容:顯示規(guī)則慨默、隱式規(guī)則贩耐、變量定義(類似于宏)、文件指示(一個(gè)Makefile包含另一個(gè)厦取,類似于include潮太,二個(gè)根據(jù)條件指定Makefile中有效部分,類似于#ifdef,三個(gè)是定義一個(gè)多行的命令)和注釋#铡买。通配符*,?,[]更鲁。Makefile中變量其實(shí)是C++中的宏,不會(huì)展開奇钞,如果想展開要用objects := @蒂胞,它表規(guī)則中所有的目標(biāo)的集合,如bigoutput littleoutput: text.g generate text.g -@) >; ,最好用()或{}引起來(lái)松逊,使用真實(shí)的<"屬于規(guī)則型變量所森,這種變量的值依賴于規(guī)則的目標(biāo)和依賴目標(biāo)的定義囱持,也可為某個(gè)目標(biāo)設(shè)置局部變量,這種變量稱為目標(biāo)變量焕济,它的作用范圍只在這條規(guī)則及連帶規(guī)則中纷妆,語(yǔ)法:<target ...> : [override] <variable-assignment>。模式變量(Pattern-specific Variable)是目標(biāo)變量的一種吼蚁,可以給定一種模式凭需,把變量定義在符合這種模式的所有目標(biāo)中,<pattern ...> : [override] <variable-assignment>肝匆。 函數(shù)調(diào)用(subst <from>, <to>, <text>),模式字符串替換:(findstring <find>, <in>)枯怖,過(guò)濾函數(shù):(filter-out <pattern...>, <text>)能曾,排序函數(shù):(word <n>,<text>),取單詞串函數(shù):(words <text>)蕊程,首單詞:(dir <names...>)驼唱,取文件名:(suffix <names...>),前綴:(addsuffix <suffix>,<names...>)辨赐,加前綴:(join <list1>, <list2>),
循環(huán)函數(shù):@"放入到表達(dá)式中,因?yàn)樽詣?dòng)化變量是在運(yùn)行時(shí)才有的不恭。(call <exp>,<para1>,<para2>,<para3>...),call是唯一一個(gè)可以用來(lái)創(chuàng)建新的參數(shù)化的函數(shù)换吧,<exp>參數(shù)中的變量如(2)等會(huì)被參數(shù)para1,para2依次取代浑娜。(shell ...)式散。控制make的函數(shù):$(error<warning> <text...>)打颤。 隱式規(guī)則:Makefile預(yù)先約定好不用再寫出來(lái)的規(guī)則如把.c文件編譯成.o文件不用寫暴拄,make會(huì)自動(dòng)推導(dǎo)并生成所需的.o文件。make的隱式規(guī)則庫(kù)中编饺,每條隱式規(guī)則都有其順序乖篷,越靠前越經(jīng)常被使用,所以有時(shí)即使顯示指定了目標(biāo)依賴透且,make也不會(huì)理會(huì)(必須同時(shí)給出相應(yīng)的命令)撕蔼。隱式規(guī)則中的變量分兩種,一是命令相關(guān)的如"CC"秽誊,另一種是參數(shù)相關(guān)的如"CFLAGS"鲸沮。可以使用模式規(guī)則來(lái)定義一個(gè)隱式規(guī)則锅论。
六讼溺、文件I/O操作
文件結(jié)構(gòu)是文件存放在磁盤等存儲(chǔ)設(shè)備中組織方法。管道文件主要用于在進(jìn)程間傳遞數(shù)據(jù)最易,把管道作為文件進(jìn)行處理可分為匿名管道和命令管道怒坯。套接口文件分三種,流式TCP藻懒,數(shù)據(jù)報(bào)UDP和原始SOCK_RAW剔猿。基于文件描述符是不帶緩沖的IO嬉荆,UNIX shell將文件描述符0與進(jìn)程的標(biāo)準(zhǔn)輸入相結(jié)合归敬,1與輸出相結(jié)合,2與標(biāo)準(zhǔn)出錯(cuò)相結(jié)合,使用STDIN_FIFLNO,STDOUT_FILENO,STDERR_FILENO弄慰,文件描述符是由無(wú)符號(hào)整數(shù)表示的句柄第美,進(jìn)程使用它來(lái)標(biāo)識(shí)打開的文件,它與包括相關(guān)信息(如打開模式等)的文件對(duì)象相關(guān)聯(lián)陆爽,這些信息被做文件的上下文什往。重定向輸入:command 1> filename是把標(biāo)準(zhǔn)輸出重定向到一個(gè)文件中(1和>中不能有空格,此方式同command > filename)直接> file相當(dāng)于創(chuàng)建空文件。 command << delimiter從標(biāo)準(zhǔn)輸入中讀入奔脐,直到遇到delimiter分界符铜幽。重定向標(biāo)準(zhǔn)錯(cuò)誤出錯(cuò):command 2>(>>) filename。command > filnename 2> &1把標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)出錯(cuò)一起重定向到filename中省古,如在命令行下輸入xxx會(huì)把bash:xxx:command not found就是標(biāo)準(zhǔn)出錯(cuò),可xxx 2 > tmp丧失。 函數(shù):open(open返回的文件描述符一定是最小的末用描述符數(shù)字豺妓,如先關(guān)閉標(biāo)準(zhǔn)輸入1,再打開一個(gè)文件則其描述符一定是1),creat(建議直接用open),close(當(dāng)一個(gè)進(jìn)程終止時(shí)布讹,它所有的打開文件都由內(nèi)核自動(dòng)關(guān)閉琳拭,不建議的也可用這種方式不顯示的close文件),當(dāng)前文件位移量:off_t lseek (int fd, off_t offset, int whence);其中l(wèi)表long是長(zhǎng)整型。文件空洞(seek)不要求占用內(nèi)存空間(但是占長(zhǎng)度)描验,read,write白嘁。od -c 文件名 以字符方式打印文件內(nèi)容。 改變權(quán)限:chmod,fchmod函數(shù)膘流。改變所有者(可同時(shí)改組):chown,fchown,lchown(只針對(duì)鏈接文件)函數(shù)絮缅。重命名函數(shù):rename。修改文件長(zhǎng)度:stat結(jié)果的st_size呼股,對(duì)符號(hào)鏈接其文件長(zhǎng)度是文件名中的實(shí)際字節(jié)數(shù)耕魄。截?cái)嗪瘮?shù):truncate,ftruncate。Linux系統(tǒng)中所有文件都有一個(gè)與之對(duì)應(yīng)的索引節(jié)點(diǎn)彭谁,包含了文件的相關(guān)信息屎开,并保存在stat結(jié)構(gòu)中,stat,fstat,lstat马靠。復(fù)制一個(gè)現(xiàn)存的文件描述符:dup,dup2奄抽。改變已打開文件的性質(zhì):fcnt1。寫入文件:sync將所有修改過(guò)的塊的緩存排入寫隊(duì)列然后返回甩鳄,不等待實(shí)際I/O操作結(jié)束逞度,系統(tǒng)進(jìn)程(通常稱為updae)一般每隔30s調(diào)用一次sync,fsync只引用單個(gè)文件妙啃,且等待I/O結(jié)束返回档泽。 目錄文件的操作:mkdir函數(shù)俊戳,rmdir函數(shù),opendir(返回DIR類型它是用于指向目錄文件的結(jié)構(gòu)指針),closedir,readdir馆匿,當(dāng)前目錄:(每個(gè)進(jìn)程都有一個(gè)當(dāng)前目錄)chdir,fchdir,getcwd函數(shù)抑胎。 鏈接文件:硬鏈接link函數(shù),刪除鏈接unlink(也可用remove渐北,對(duì)文件其功能與unlink相同阿逃,對(duì)目錄功能同mkdir)。硬鏈接要求鏈接和文件位于同一文件系統(tǒng)中赃蛛,且只有超級(jí)用戶能創(chuàng)建恃锉。它直接指向文件的i節(jié)點(diǎn)。symlink軟鏈接呕臂,readlink是讀取鏈接本身破托,而不是其指向的文件。管道文件int pipe(int filedes[2]);歧蒋。
七土砂、基于流的I/O操作
當(dāng)使用流I/O時(shí),stdin,stdout,stderr也會(huì)自動(dòng)打開谜洽。有三種類型緩存瘟芝,全、行與無(wú)緩存(標(biāo)準(zhǔn)出錯(cuò)用的這種)褥琐。設(shè)置緩存的屬性(包括緩沖區(qū)的類型、大形钪!):setbuf,setbuffer,setlinebuf,setvbuf(最好用此)敌呈,最好剛開始還未使用時(shí)進(jìn)行設(shè)置。緩存的沖洗:將I/O操作寫入緩存中的內(nèi)容清空(可丟掉fpurge或保存到文件flush)造寝。流的打開:fopen,freopen,fdopen(取一個(gè)現(xiàn)在的文件描述符并使一個(gè)標(biāo)準(zhǔn)的I/O流與該描述符相結(jié)合)磕洪。Linux系統(tǒng)不區(qū)分二進(jìn)制文件和文本文件,它們都是普通文件诫龙。未調(diào)用fclose可能會(huì)導(dǎo)致數(shù)據(jù)停在緩沖區(qū)析显,而沒(méi)有保存到文件中。fprintf向流中寫签赃。一旦打開了流谷异,可在4種不同類型的I/O中進(jìn)行選擇來(lái)對(duì)其讀寫:基于字符(getc,fgetc,getchar,putc,fputc,putchar)、行(fgets,gets,fputs,puts)锦聊、直接I/O(fread,fwrite)用這種方式可以直接讀取一個(gè)結(jié)構(gòu)如一個(gè)struct歹嘹,格式化I/O(printf,fprintf,sprintf,snprintf,scanf,fscanf,sscanf)。判斷讀入出錯(cuò)還是結(jié)束:ferror,feof孔庭。
八尺上、進(jìn)程控制
Linux系統(tǒng)中所有進(jìn)程除了初始化進(jìn)程外都有一個(gè)父進(jìn)程材蛛,新進(jìn)程不是被創(chuàng)建,而是被復(fù)制或從以前的進(jìn)程復(fù)制而來(lái)怎抛。Linux系統(tǒng)中所有進(jìn)程都是由進(jìn)程號(hào)為1的init進(jìn)程衍生而來(lái)的卑吭,進(jìn)程樹。一個(gè)正在執(zhí)行的進(jìn)程稱為一個(gè)作業(yè)马绝,而作業(yè)可以包含一個(gè)或多個(gè)進(jìn)程豆赏,尤其是使用了管道和重定向命令的時(shí)候。在Linux中每個(gè)進(jìn)程在創(chuàng)建時(shí)都會(huì)被分配一個(gè)數(shù)據(jù)結(jié)構(gòu)迹淌,稱為進(jìn)程控制塊(PCB)河绽。進(jìn)程ID 0是調(diào)度進(jìn)程,常被稱為交換進(jìn)程(swapper)唉窃,它是內(nèi)核的一部分(系統(tǒng)進(jìn)程)耙饰,init的ID為1,它是普通的用戶進(jìn)程纹份,但它以超級(jí)用戶特權(quán)運(yùn)行苟跪。多個(gè)進(jìn)程合成進(jìn)程組,多個(gè)進(jìn)程組合成一個(gè)會(huì)話蔓涧,getpid()件已。創(chuàng)建進(jìn)程pid_t fork();成功父進(jìn)程返回子進(jìn)程ID,子進(jìn)程返回0元暴,出錯(cuò)返回-1篷扩。子進(jìn)程從父進(jìn)程得到數(shù)據(jù)段和堆棧段的拷貝,這些需要分配新的內(nèi)存≤哉担現(xiàn)在很多是采用寫時(shí)復(fù)制(Copy-On-Write,COW)技術(shù)鉴未,運(yùn)行后兩進(jìn)程獨(dú)立,同一個(gè)變量不相通(不是共享數(shù)據(jù)段)鸠姨。vfork不需要完全拷貝父進(jìn)程的數(shù)據(jù)段(如果先copy會(huì)大量拷貝數(shù)據(jù)子進(jìn)行exec時(shí)會(huì)再清除這些數(shù)據(jù)铜秆,因而先不copy,真正用時(shí)再copy)讶迁,在子進(jìn)程沒(méi)有調(diào)用exec或exit之前连茧,子、父進(jìn)程共享數(shù)據(jù)段巍糯,且子進(jìn)程先運(yùn)行啸驯,父進(jìn)程掛起,直到子進(jìn)程調(diào)用了exec或exit之后父子進(jìn)程的執(zhí)行次序才不再有限制祟峦,vfork創(chuàng)建出來(lái)的是一個(gè)線程(沒(méi)有獨(dú)立的內(nèi)存資源)坯汤,共享數(shù)據(jù)段,一個(gè)改變會(huì)影響另一個(gè)搀愧。 Linux使用exec來(lái)執(zhí)行新的程序惰聂,以新的子進(jìn)程來(lái)完全代替原有的進(jìn)程疆偿。execl,execlp,execle,execv,execvp,execve,只有execve才是真正意義上的系統(tǒng)調(diào)用搓幌,其他都是在此基礎(chǔ)上經(jīng)過(guò)包裝的庫(kù)函數(shù)杆故,exec函數(shù)族的作用是根據(jù)指定的文件名找到可執(zhí)行文件,并用它來(lái)取代調(diào)用進(jìn)程的內(nèi)容溉愁。exec執(zhí)行成功不會(huì)返回处铛,只有失敗了才返回-1。perror用來(lái)將上一個(gè)函數(shù)發(fā)生錯(cuò)誤的原因輸出到標(biāo)準(zhǔn)錯(cuò)誤拐揭,原型是void perror(const char* s)撤蟆,參數(shù)s所指字符串先打印后面再加上錯(cuò)誤原因。 退出程序可以有返回值用wait接收并處理堂污。_exit立即進(jìn)入內(nèi)核家肯,exit則先執(zhí)行一些清除處理(包括調(diào)用執(zhí)行各終止處理程序,關(guān)閉所有標(biāo)準(zhǔn)I/O流盟猖,把緩沖區(qū)中的內(nèi)容寫回文件等如果此時(shí)直接調(diào)用_exit會(huì)丟失緩沖區(qū)中的內(nèi)容讨衣,如調(diào)用printf沒(méi)有\(zhòng)n換行及fopen,fread,fwrite等),然后進(jìn)入內(nèi)核式镐。進(jìn)程調(diào)用exit后反镇,并非馬上就消失掉,而是留下一個(gè)稱為僵尸進(jìn)程的數(shù)據(jù)結(jié)構(gòu)娘汞,處理方法是調(diào)用wait和waitpid成功返回子進(jìn)程ID歹茶,失敗返回-1。僵尸進(jìn)程放棄了幾乎所有內(nèi)存空間你弦,沒(méi)有任何可執(zhí)行代碼惊豺,也不能被調(diào)度僅在進(jìn)程列表中保留一個(gè)位置,記載該進(jìn)程的退出狀態(tài)等信息供其他進(jìn)程收集鳖目,除此外不再占用任何內(nèi)存空間。進(jìn)程一旦調(diào)用wait就立即阻塞自己缤弦,并由wait分析是否當(dāng)前進(jìn)程的某個(gè)子進(jìn)程已經(jīng)退出领迈,如果找到這樣一個(gè)僵尸子進(jìn)程,會(huì)將它徹底銷毀并返回碍沐,如果沒(méi)找到wait就一直阻塞狸捅,直到有一個(gè)出現(xiàn)為止,如果出現(xiàn)wait就會(huì)收集這個(gè)子進(jìn)程的信息并把它徹底銷毀后返回累提,如果只想把僵尸進(jìn)程消滅掉而對(duì)進(jìn)程如何死掉毫不在意可直接pid=wait(NULL)尘喝,而不用pid=wait(int* status)。通過(guò)WIFEXITED(status)(用來(lái)指示子進(jìn)程是否為正常退出),WEXITSTATUS(status)(如果WIFEXITED返回非0用此來(lái)提取子進(jìn)程的返回值)兩個(gè)宏可獲取子進(jìn)程退出時(shí)的返回值斋陪。還有wait3,wait4功能同wait但多個(gè)rusage是一個(gè)結(jié)構(gòu)指針朽褪,可獲取進(jìn)行及其子進(jìn)程所占用resuorces的情況置吓。 進(jìn)程用戶ID,用戶組ID缔赠,實(shí)際衍锚、有效、保存的設(shè)置用戶(組)ID嗤堰。getuid,getgid(前兩個(gè)是實(shí)際),gettuid,getegid,setuid,setgid,seteuid,setegid戴质。只有超級(jí)用戶進(jìn)程可以更改實(shí)際用戶ID,setreuid,setregid用于交換實(shí)際及有效用戶(組)ID踢匣。system執(zhí)行系統(tǒng)命令也是調(diào)用程序創(chuàng)建進(jìn)程來(lái)實(shí)現(xiàn)的(通過(guò)fork,exec,waitpid)告匠,system(const char* cmdstring)。獲取進(jìn)程組ID离唬,pid_t getpgrp();每個(gè)進(jìn)程組有一個(gè)組長(zhǎng)進(jìn)程leader后专,其標(biāo)識(shí)是進(jìn)程組ID等于其進(jìn)程ID,進(jìn)程組長(zhǎng)可創(chuàng)建一個(gè)進(jìn)程組成可創(chuàng)建該組中的進(jìn)程男娄,進(jìn)程組中最后一個(gè)進(jìn)程可終止或加入到另一進(jìn)程組中行贪,setpgid可加入另一組或創(chuàng)建新組。會(huì)話期是一個(gè)或多個(gè)進(jìn)程組的集合模闲。建立對(duì)話期:setsid建瘫,獲得與設(shè)置前臺(tái)進(jìn)程組ID:tcgetpgrp,tcsetpgrp。
九尸折、信號(hào)
信號(hào)全稱為軟中斷信號(hào)啰脚,它是一種進(jìn)程間通信的方法,應(yīng)用于異步事件的處理实夹。處理信號(hào)的方式可類似中斷的處理指定處理函數(shù)橄浓,或忽略,或?qū)υ撔盘?hào)的處理保留系統(tǒng)的默認(rèn)值亮航,這種默認(rèn)操作大多數(shù)是使進(jìn)程終止荸实。在進(jìn)程表的表項(xiàng)中有一個(gè)軟中斷信號(hào)域,該域中每一位對(duì)應(yīng)一個(gè)信號(hào)當(dāng)有信號(hào)發(fā)來(lái)時(shí)對(duì)應(yīng)位置位缴淋,進(jìn)程對(duì)不同信號(hào)可同時(shí)保留但對(duì)同一信號(hào)并不知道在處理前來(lái)過(guò)多少個(gè)准给。當(dāng)進(jìn)程調(diào)用abort函數(shù)時(shí)會(huì)產(chǎn)生SIGABRT信號(hào)。用kill -l查看重抖,131為傳統(tǒng)UNIX支持的信號(hào)是不可靠(非實(shí)時(shí))的露氮,3263是后來(lái)擴(kuò)充的,是可靠信號(hào)(實(shí)時(shí)信號(hào))區(qū)別在于前者不支持排隊(duì)可能會(huì)造成信號(hào)丟失钟沛。 SIGTERM是程序結(jié)束信號(hào)畔规,與SIGKILL不同的是該信號(hào)可被阻塞和處理,通常用來(lái)要求程序自己正常退出恨统,shell命令kill默認(rèn)產(chǎn)生這個(gè)信號(hào)如果終止不了才嘗試SIGKILL叁扫。SIGCHLD是子進(jìn)程結(jié)束時(shí)父進(jìn)程收到的信號(hào)三妈,若父進(jìn)程沒(méi)有處理此信號(hào)也沒(méi)等待子進(jìn)程,子進(jìn)程終止后還會(huì)在內(nèi)核進(jìn)程表中占有表項(xiàng)即僵尸進(jìn)程陌兑,可父進(jìn)程先終止這時(shí)子進(jìn)程的終止自動(dòng)由init進(jìn)程來(lái)接管沈跨。SIGSTOP暫停執(zhí)行,同SIGKILL一樣不能被阻塞處理或忽略(為了使系統(tǒng)管理員在任何時(shí)候結(jié)束或停止某一特定進(jìn)程的執(zhí)行)兔综。SIGSTP可被處理和忽略(即Ctrl+Z發(fā)出的信號(hào))饿凛。 內(nèi)核給一個(gè)進(jìn)程發(fā)送軟中斷信號(hào)的方法是在進(jìn)程所在的進(jìn)程表項(xiàng)的信號(hào)設(shè)置對(duì)應(yīng)于該信號(hào)的位(內(nèi)核通過(guò)在進(jìn)程的struct tast_struct結(jié)構(gòu)中的信號(hào)域中設(shè)置相應(yīng)的位來(lái)實(shí)現(xiàn)向一個(gè)進(jìn)程發(fā)送信號(hào))。若信號(hào)發(fā)送給一個(gè)正在睡眠的進(jìn)程要看該進(jìn)程進(jìn)入睡眠的優(yōu)先級(jí)软驰,若進(jìn)程睡眠在可被中斷的優(yōu)先級(jí)上則喚醒否則設(shè)置進(jìn)程表中信號(hào)域相應(yīng)的位而不喚醒進(jìn)程涧窒,這一點(diǎn)比較重要,因?yàn)檫M(jìn)程檢查是否收到信號(hào)的時(shí)機(jī)是一個(gè)進(jìn)程在即將從內(nèi)核態(tài)返回到用戶態(tài)時(shí)或在一個(gè)進(jìn)程要進(jìn)入或離開一個(gè)適應(yīng)的低調(diào)度優(yōu)先級(jí)睡眠狀態(tài)時(shí)锭亏。內(nèi)核處理一個(gè)進(jìn)程收到信號(hào)的時(shí)機(jī)是在一個(gè)進(jìn)程從內(nèi)核態(tài)返回用戶態(tài)時(shí)纠吴,所以一個(gè)進(jìn)程在內(nèi)核態(tài)運(yùn)行時(shí)軟中斷信號(hào)并不立即起作用,進(jìn)程只有處理完信號(hào)才會(huì)返回用戶態(tài)慧瘤,進(jìn)程在用戶態(tài)不會(huì)有未處理完的信號(hào)戴已。內(nèi)核處理一個(gè)進(jìn)程收到的軟中斷信號(hào)是在該進(jìn)程的上下文中,因此進(jìn)程必須處于運(yùn)行狀態(tài)锅减。內(nèi)核執(zhí)行用戶定義的函數(shù)方法是內(nèi)核在用戶棧上創(chuàng)建一個(gè)新的層糖儡,該層中將返回地址的值設(shè)置成用戶定義的處理函數(shù)的地址這樣進(jìn)程從內(nèi)核返回彈出棧頂時(shí)就返回到用戶定義的函數(shù)處,從函數(shù)返回再?gòu)棾鰲m敃r(shí)才返回原先進(jìn)入內(nèi)核的地方怔匣,這樣做的原因是用戶定義的處理函數(shù)不能且不允許在內(nèi)核態(tài)下執(zhí)行(如果用戶定義的函數(shù)在內(nèi)核態(tài)運(yùn)行的話用戶就可以獲得任何權(quán)限)握联。如果要捕捉的信號(hào)發(fā)生于進(jìn)程正在一個(gè)系統(tǒng)調(diào)用中時(shí),且該進(jìn)程睡眠在可中斷的優(yōu)先級(jí)上每瞒,這時(shí)該信號(hào)引起進(jìn)程做一次longjmp調(diào)用金闽,跳出睡眠狀態(tài),返回用戶態(tài)并執(zhí)行信號(hào)處理例程剿骨。有些地方要求進(jìn)程在檢查收到的信號(hào)后從原來(lái)系統(tǒng)調(diào)用中直接返回而不是等到該調(diào)用完成代芜,這種進(jìn)程突然改變其上下文的情況就是使用setjmp和longjmp的結(jié)果。setjmp將保存的上下文存入用戶區(qū)并繼續(xù)在舊的上下文中執(zhí)行浓利。進(jìn)程執(zhí)行一個(gè)系統(tǒng)調(diào)用挤庇,當(dāng)因?yàn)橘Y源或其他原因要去睡眠時(shí)內(nèi)核為進(jìn)程做了一次setjmp,如果睡眠中的信號(hào)被喚醒進(jìn)程不能再進(jìn)入睡眠時(shí)荞膘,內(nèi)核為進(jìn)程調(diào)用longjmp罚随,該操作是內(nèi)核為進(jìn)程將原先setjmp調(diào)用保存在進(jìn)程用戶區(qū)的上下文恢復(fù)成現(xiàn)在的上下文玉工,這樣就使得進(jìn)程可以恢復(fù)等待資源前的狀態(tài)羽资,而且內(nèi)核為setjmp返回1,使得進(jìn)程知道該次系統(tǒng)調(diào)用失敗遵班。通過(guò)調(diào)用signal函數(shù)來(lái)注冊(cè)某個(gè)特定信號(hào)的處理程序屠升,原型為void (signal (int signum, void (handler) (int))) (int);其中signum是SIG開頭的那些潮改,handle是SIG_IGN或SIG_DFL或接收到此信號(hào)后要調(diào)用的函數(shù)地址。此函數(shù)兩個(gè)參數(shù)一個(gè)是整型signum一個(gè)是函數(shù)指針腹暖。如signal(SIGINT, SIG_IGN)汇在,或void sign_handler(int sign_num){printf("Capture signal number:%d\n", sig_num);然后signal(SIG_INT, sign_handler);(Ctrl+C產(chǎn)生SIGINT)。信號(hào)是進(jìn)程間通信機(jī)制中唯一的異步通信機(jī)制脏答。int sigaction(int signum, const struct sigaction* act, struct sigaction* oldact);檢查糕殉、修改與指定信號(hào)相關(guān)聯(lián)的處理動(dòng)作,完全可替代signal函數(shù)殖告。處理函數(shù):void handler(int sign_num, siginfo_t* p_sign_info, void* p_reserved); 一個(gè)用戶進(jìn)程常常需要對(duì)多個(gè)信號(hào)進(jìn)行處理阿蝶,為方便同時(shí)處理引入信號(hào)集(signal set)的概念,類型sigset_t黄绩。sigemptyset,sigfillset,sigaddset,sigdelset,sigismember羡洁。發(fā)送信號(hào)函數(shù):kill,raise,sigqueue,alarm,setitimer,abort。(1)爽丹、其中int kill(int pid_t pid, int signum)向進(jìn)程(組)發(fā)送信號(hào)筑煮,signum為0時(shí)(即空信號(hào))實(shí)際上并不發(fā)送信號(hào)只檢查如目標(biāo)進(jìn)程是否存在、當(dāng)前進(jìn)程是否有向目標(biāo)發(fā)送信號(hào)的權(quán)限等(非root的進(jìn)程只能向?qū)儆谕粋€(gè)session或同一個(gè)用戶的進(jìn)程發(fā)送信號(hào))粤蝎;WIFEXITED(status)若此值非0表明進(jìn)程正常結(jié)束真仲,可通過(guò)WEXITSTASTUS(status)獲取進(jìn)程退出狀態(tài)(exit時(shí)參數(shù))。WIFSIGNALED(status)為非0表進(jìn)程異常終止可用WTERMSIG(status)獲取信號(hào)編號(hào)诽里。(2)袒餐、int raise(int signum)向自身的進(jìn)程發(fā)送一個(gè)SIGABRT信號(hào),使進(jìn)程非正常結(jié)束谤狡。(3)灸眼、int sigqueue(pid_t pid, int signum, const union signal val);主要針對(duì)實(shí)時(shí)信號(hào)提出的(也支持前32種)支持信號(hào)帶有參數(shù),通常與sigaction配合使用墓懂,功能同kill焰宣,也可signum=0但不能發(fā)信號(hào)給進(jìn)程組,在調(diào)用sigqueue時(shí)sigval_t指定的信息會(huì)拷貝到3參數(shù)信號(hào)處理函數(shù)(3參數(shù)信號(hào)處理函數(shù)指信號(hào)處理函數(shù)由sigaction安裝并設(shè)定sa_sigaction指針捕仔。sigqueue發(fā)送非實(shí)時(shí)信號(hào)時(shí)仍不支持排隊(duì)即在信號(hào)處理函數(shù)執(zhí)行過(guò)程中到來(lái)的所有相同信號(hào)都被合并為一個(gè)信號(hào)匕积。 (4)、alarm函數(shù)專門為SIGALRM信號(hào)而設(shè)使系統(tǒng)在一定時(shí)間之后發(fā)送信號(hào)榜跌,原型unsigned int alarm(unsigned int seconds);如果調(diào)用alarm之前進(jìn)程中已經(jīng)設(shè)置了鬧鐘時(shí)間則返回上一個(gè)鬧鐘時(shí)間的剩余時(shí)間否則返回0闪唆。調(diào)用alarm后任何以前的alarm調(diào)用都將無(wú)效,如果參數(shù)seconds為0钓葫,那么進(jìn)程內(nèi)將不再包含任何鬧鐘時(shí)間悄蕾。 (5)、setitimer功能類似alarm但功能更強(qiáng)大,int setitmer(int witch, const stuct itimerval* value, struct itimerval* oldvalue);witch是定時(shí)器類型帆调,可設(shè)定絕對(duì)時(shí)間(系統(tǒng)時(shí)間)奠骄、程序執(zhí)行時(shí)間(只有在用戶模式下才可跟蹤時(shí)間)和從用戶進(jìn)程開始后開始計(jì)時(shí)。 (6)番刊、abort含鳞。 有時(shí)不希望進(jìn)程接收到信號(hào)時(shí)立刻中斷也不希望信號(hào)被忽略,可用阻塞信號(hào)方法實(shí)現(xiàn)芹务,有sigprocmask,sigsuspend蝉绷。其中sigprocmask可用于檢測(cè)或更改進(jìn)程的信號(hào)掩碼,信號(hào)掩碼是由被阻塞的發(fā)送給當(dāng)前進(jìn)程信號(hào)組成的信號(hào)集枣抱。函數(shù)sigaction中設(shè)置被阻塞信號(hào)集合只是針對(duì)要處理的信號(hào)潜必,而sigpromask是全程阻塞,被阻塞的信號(hào)將不能再被信號(hào)處理函數(shù)捕捉直到重新設(shè)置阻塞信號(hào)集合沃但,原型如下:int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);how可取SIG_BLOCK,SIG_UNBLOCK,SIG_SETMASK磁滚。sigsuspend函數(shù)使進(jìn)程掛起int sigsuspend(sigset_t* sigmask); Linux下有兩個(gè)睡眠函數(shù)unsigned int sleep(unsigned int seconds);和void usleep(unsigned long usec);其實(shí)sleep內(nèi)部是用信號(hào)機(jī)制進(jìn)行處理的,用到alarm和int pause(void);其中pause將自身進(jìn)程掛起宵晚,直到有信號(hào)才從pause返回垂攘。 Linux為每個(gè)進(jìn)程維護(hù)3個(gè)計(jì)時(shí)器,分別是真實(shí)計(jì)時(shí)器(程序運(yùn)行的實(shí)際時(shí)間)淤刃、虛擬計(jì)時(shí)器(運(yùn)行在用戶態(tài)的時(shí)間晒他,可認(rèn)為實(shí)際時(shí)間減系統(tǒng)調(diào)用和程序睡眠時(shí)間)和實(shí)用計(jì)時(shí)器(程序處于用戶態(tài)和內(nèi)核態(tài)所消耗的時(shí)間之和)。設(shè)定好計(jì)時(shí)器后該計(jì)時(shí)器就會(huì)定時(shí)向進(jìn)程發(fā)送時(shí)鐘信號(hào)分別為SIGALRM,SIGVTALRM,SIGPROF逸贾,用到的函數(shù)有int getitime(int which, struct itimerval* value)和int setitimer(int which, const struct itimerval* value, struct itimer val* ovalue);
十陨仅、進(jìn)程間通信
進(jìn)程間通信(InterProcess Communication,IPC)铝侵。進(jìn)程的用戶空間是互相獨(dú)立的一般不能互相訪問(wèn)灼伤,但共享內(nèi)存區(qū)是可以都訪問(wèn)到的,也可通過(guò)外設(shè)如普通文件等但效率太低咪鲜。進(jìn)程間通信的方法有管道狐赡、消息隊(duì)列、信號(hào)量疟丙、共享內(nèi)存和套接口等颖侄。消息隊(duì)列、信號(hào)量享郊、共享內(nèi)存通稱為系統(tǒng)(POSIX和System V系統(tǒng))IPC览祖。套接口用于遠(yuǎn)程通信,其余用于本地進(jìn)程間通信炊琉。管道可用于具有親緣關(guān)系進(jìn)程間的通信展蒂,命名管道還可用于無(wú)親緣關(guān)系進(jìn)程間的通信。消息隊(duì)列包括POSIX消息隊(duì)列System V消息隊(duì)列。有足夠權(quán)限的進(jìn)程可向隊(duì)列中添加消息玄货,被賦予讀權(quán)限的進(jìn)程則可讀走隊(duì)列中的消息,它克服了信號(hào)量承載信息量少悼泌,管道只能承載無(wú)格式字節(jié)流及緩沖區(qū)大小受限等缺點(diǎn)松捉。共享內(nèi)存是最快的IPC形式,是針對(duì)其他通信機(jī)制運(yùn)行效率較低而設(shè)計(jì)的馆里,往往與其他通信機(jī)制如信號(hào)量結(jié)合使用來(lái)達(dá)到同步及互斥隘世。信號(hào)量主要作為進(jìn)程或線程間同步的手段。進(jìn)程間通信包括程序運(yùn)行的實(shí)時(shí)數(shù)據(jù)也包括對(duì)方的代碼段鸠踪。
(10.1.1)丙者、 管道(Pipe)是在兩個(gè)進(jìn)程間實(shí)現(xiàn)一個(gè)數(shù)據(jù)流通的通道,簡(jiǎn)單易用营密,它實(shí)現(xiàn)數(shù)據(jù)以一種數(shù)據(jù)流的方式在進(jìn)程間流動(dòng)械媒。管道上的數(shù)據(jù)讀出后就沒(méi)有數(shù)據(jù)了,和文件不同评汰。匿名管道沒(méi)有實(shí)名并不在文件系統(tǒng)中可以看到纷捞,它只是進(jìn)程的一種資源,會(huì)隨進(jìn)程的結(jié)束而被系統(tǒng)清除被去。創(chuàng)建一個(gè)管道會(huì)生成兩個(gè)文件描述符主儡。管道一般是半雙工的,需要雙方通信時(shí)需要建立兩個(gè)管道惨缆,只能用于父子進(jìn)程或兄弟進(jìn)程之間(親緣關(guān)系的進(jìn)程)糜值。管道構(gòu)成一種文件文件系統(tǒng)只存在于內(nèi)存中。管道的緩沖區(qū)是有限的(只存在于內(nèi)存中坯墨,在創(chuàng)建時(shí)為緩沖區(qū)分配一個(gè)頁(yè)面大屑呕恪),且所傳送的是無(wú)格式字節(jié)流捣染,就需要雙方事先約定好如多少字節(jié)算一個(gè)消息健无。int pipe(int fd[2])。其中fd[0]是讀出端液斜,df[1]是寫入端的文件描述符累贤,關(guān)閉用close(fd[0]),close(fd[1])。一般的I/O函數(shù)都可以用于管道少漆,如close,read,write等臼膏。當(dāng)對(duì)于一個(gè)讀端已經(jīng)關(guān)閉的管道進(jìn)行寫操作時(shí)會(huì)產(chǎn)生信號(hào)SIGPIPE。管道的讀取規(guī)則是如果寫端不存在則認(rèn)為已經(jīng)讀到了數(shù)據(jù)的末尾示损,讀函數(shù)返回讀出字節(jié)數(shù)0渗磅,當(dāng)管道寫端存在時(shí),如果請(qǐng)求字節(jié)數(shù)目大于PIPE_BUF時(shí)管道中現(xiàn)在的字節(jié)數(shù),此值不同內(nèi)核不同可在/usr/include/linux/limits.h中查看始鱼。當(dāng)管道已經(jīng)滿時(shí)寫操作將阻塞仔掸。只有當(dāng)管道在讀端存在時(shí),向管道中寫入數(shù)據(jù)才有意義否則寫入數(shù)據(jù)的進(jìn)程將收到內(nèi)核傳來(lái)的SIGPIPE信號(hào)医清,可以處理該信號(hào)也可忽略(默認(rèn)動(dòng)作則是應(yīng)用程序終止)起暮。如要建立一個(gè)你進(jìn)程到子進(jìn)程的數(shù)據(jù)通道可先調(diào)用pipe緊接著調(diào)用fork,由于子進(jìn)程自動(dòng)繼承父進(jìn)程的數(shù)據(jù)段会烙,則父子進(jìn)程同時(shí)擁有管道的操作權(quán)负懦。
(10.1.2)命名管道:也稱FIFO是一種文件類型在程序中可通過(guò)查看文件stat結(jié)構(gòu)中st_mode的值來(lái)判斷該文件是否為FIFO。它不同于管道之處在于它提供一個(gè)路徑名與之關(guān)聯(lián)柏腻,以FIFO的文件形式存在于文件系統(tǒng)中纸厉,這樣只要能訪問(wèn)FIFO就要通信。它是一種特殊的文件存放于文件系統(tǒng)中而不像管道存放于內(nèi)存(使用完畢后消失)五嫂,除非對(duì)它刪除否則不會(huì)消息颗品。FIFO不支持lseek等文件定位操作,在shell中可用mkfifo建立其中參數(shù)-m mode指出八進(jìn)制模式沃缘,函數(shù):int mkfifo(const char* pathname, mode_t mode);由于FIFO文件的特性抛猫,它被隱性地規(guī)定不具有執(zhí)行權(quán)限。FIFO比管道多了個(gè)open孩灯,因?yàn)樗环N文件類型闺金,而管道不是。要?jiǎng)h除命名管道用unlink峰档。
(10.2)消息隊(duì)列:是一種以鏈表式結(jié)構(gòu)組織的一組數(shù)組败匹,存放在內(nèi)核中,由各進(jìn)程通過(guò)消息隊(duì)列標(biāo)識(shí)符來(lái)引用的一種數(shù)據(jù)傳送方式讥巡,也是由內(nèi)核來(lái)維護(hù)掀亩。消息隊(duì)列具有特定的格式及優(yōu)先級(jí),消息隊(duì)列是隨內(nèi)核持續(xù)(kernel-persistent)的欢顷,不是隨進(jìn)程(process-persisten)槽棍。隨進(jìn)程持續(xù):IPC一直存在直至打開IPC對(duì)象的最后一個(gè)進(jìn)程關(guān)閉該對(duì)象為止,如管道和命名管道抬驴,隨內(nèi)核持續(xù):IPC一直持續(xù)到內(nèi)核重新自舉或顯示刪除該對(duì)象為止炼七,如消息隊(duì)列、信號(hào)量布持、共享內(nèi)存豌拙,隨文件系統(tǒng)持續(xù):IPC一直持續(xù)到顯示刪除該對(duì)象為止。目前主要有兩大類型的消息隊(duì)列:POSIX及System V题暖。System V目前被大量使用按傅,考慮到移植性應(yīng)盡量使用POSIX捉超。系統(tǒng)中記錄消息隊(duì)列的數(shù)據(jù)結(jié)構(gòu)(struct ipc_ids msg_ids)位于內(nèi)核(因消息隊(duì)列隨內(nèi)核持續(xù))中,系統(tǒng)中所有消息隊(duì)列都可在結(jié)構(gòu)msg_ids中找到訪問(wèn)入口唯绍。每個(gè)消息隊(duì)列都有一個(gè)隊(duì)列頭拼岳,用struct msg_queue來(lái)描述。隊(duì)列頭中包含了該消息隊(duì)列的大量信息况芒,包括消息隊(duì)列的鍵值惜纸、用戶ID、組ID牛柒、消息隊(duì)列中的消息數(shù)目等,甚至記錄了最近對(duì)消息隊(duì)列讀寫進(jìn)程的ID痊乾。struct msqid_ds用來(lái)設(shè)置或返回消息隊(duì)列的消息皮壁,存在于用戶空間。全局?jǐn)?shù)組結(jié)構(gòu)struct ipc_ids msg_ids可訪問(wèn)每個(gè)消息隊(duì)列頭的第一個(gè)成員struct ipc_perm哪审,而每個(gè)struct ipc_perm能夠與具體消息隊(duì)列對(duì)應(yīng)起來(lái)是因?yàn)樵摻Y(jié)構(gòu)中蛾魄,有一個(gè)key_t類型成員key,而key則唯一確定一個(gè)消息隊(duì)列湿滓。
消息隊(duì)列的ID是由在系統(tǒng)范圍內(nèi)唯一的鍵值生成的滴须,而鍵值可以看做對(duì)應(yīng)系統(tǒng)內(nèi)的一條路徑。獲得特定文件名的鍵值的系統(tǒng)調(diào)用是key_t ftok(char* pathname, char proj);創(chuàng)建或打開一個(gè)消息隊(duì)列的系統(tǒng)調(diào)用為int msgget(key_t key, int msgflg);其中msgflg可為IPC_CREATE,IPC_EXCL,IPC_NOWAIT叽奥,key參數(shù)(上面創(chuàng)建key時(shí))設(shè)置成常數(shù)IPC_PRIVATE并不意味著其他進(jìn)程不能訪問(wèn)該消息隊(duì)列扔水,只意味關(guān)即將創(chuàng)建新的消息隊(duì)列,當(dāng)調(diào)用msgget創(chuàng)建一個(gè)消息隊(duì)列時(shí)朝氓,它相應(yīng)的msqid_ds結(jié)構(gòu)被初始化魔市。 消息隊(duì)列所傳遞的消息由兩部分組成,即消息的類型和所傳遞的數(shù)據(jù)赵哲,一般用數(shù)據(jù)結(jié)構(gòu)struct msgbuf來(lái)表示待德,通常消息類型是一個(gè)正的長(zhǎng)整數(shù),而數(shù)據(jù)則根據(jù)需要設(shè)定枫夺。發(fā)送:int msgsnd(int msqid, const void* ptr, size_t nbytes, int flags);其中flags指定消息隊(duì)列滿時(shí)的處理方法将宪,如設(shè)置IPC_NOWAIT當(dāng)消息隊(duì)列沒(méi)有足夠的空間容納要發(fā)送的消息時(shí),立即出錯(cuò)返回橡庞,否則發(fā)送消息的進(jìn)程被阻塞较坛,造成阻塞可能是消息內(nèi)容大或數(shù)量大(消息數(shù)目過(guò)多,但大小可能很邪亲睢)燎潮。接收int msgrcv(int msqid, void* ptr, size_t nbytes, long type, int flags);可獲取消息隊(duì)列的屬性,也可設(shè)置該數(shù)據(jù)結(jié)構(gòu):int msgctl(int msqid, int cmd, struct msqid_ds* buf);其中cmd:IPC_STAT,ICP_SET,IPC_RMID(表刪除)扼倘。
(10.3)共享內(nèi)存:它是存在于內(nèi)核級(jí)別的一種資源确封。分布機(jī)制可讓一個(gè)進(jìn)程的物理地址不連續(xù)除呵,也可讓一段內(nèi)存同時(shí)分配給不同的進(jìn)程,共享內(nèi)存機(jī)制就是通過(guò)該原理實(shí)現(xiàn)的爪喘。共享內(nèi)存并不是讀寫數(shù)據(jù)后就解除映射颜曾,而是保持共享區(qū)域直到通信完畢,這樣數(shù)據(jù)內(nèi)容一直保存在共享內(nèi)存中并沒(méi)有寫回文件秉剑,共享內(nèi)存中的內(nèi)容往往是在解除映射時(shí)才寫回文件的泛豪,因?yàn)樾矢摺?duì)每個(gè)共享存儲(chǔ)段內(nèi)核會(huì)為其維護(hù)一個(gè)shmid_ds結(jié)構(gòu)體侦鹏。int shmget(key_t key, int size, int flag); void* shmat(int shmd, const void* addr, int flag);失敗返回-1诡曙,成功返回指向共享內(nèi)存段的指針,且shmid_ds結(jié)構(gòu)的shm_nattch計(jì)數(shù)值加1略水,int shmdt(void* addr);失敗返回-1价卤,成功返回0,int shmctl(int shmid, int cmd, struct shmid_ds* buf);失敗返回-1成功返回0渊涝,其中cmd可IPC_STA,IPC_SET,IPC_RMID,SHM_LOCK,SHM_UNLOCK慎璧。
(10.4)信號(hào)量原理是一種數(shù)據(jù)操作鎖的概念本身不具備數(shù)據(jù)交換的功能,而是通過(guò)控制其他通信資源(如文件跨释、外部設(shè)備等)來(lái)實(shí)現(xiàn)進(jìn)程間通信胸私,它本身不具備數(shù)據(jù)傳輸?shù)墓δ埽皇且环N外部資源的標(biāo)識(shí)鳖谈。在信號(hào)量上定義兩種操作:Wait和Release岁疼。在信號(hào)量的實(shí)際應(yīng)用中,是不能單獨(dú)一個(gè)信號(hào)量的缆娃,只能定義一個(gè)信號(hào)量集五续,其中包含一組信號(hào)量,其中包含一組信號(hào)量同一信號(hào)量集中的信號(hào)量使用同一個(gè)引用ID龄恋,這樣設(shè)置是為了多個(gè)資源或同步操作的需要疙驾,每個(gè)信號(hào)量集都有一個(gè)與之對(duì)應(yīng)結(jié)構(gòu),記錄了信號(hào)量集的各個(gè)信息郭毕。 int semget(key_t key, int nsems, int flags); int semop(int semid, struct sembuf semoparray[], size_t nops);成功返回0出錯(cuò)-1它碎,此函數(shù)是原子操作∠匝海控制函數(shù):int semctl(int semid, int semnum, int cmd, ...);成功是當(dāng)操作為GET時(shí)返回相應(yīng)值否則返回0扳肛,失敗時(shí)返回-1,并設(shè)置errno乘碑。
十一挖息、網(wǎng)絡(luò)編程
物理層不是指物理設(shè)備或物理媒體而是有關(guān)物理設(shè)備通過(guò)物理媒體進(jìn)行互連的描述和規(guī)定,它定義了接口的機(jī)械特性兽肤、電氣特性套腹、功能特性恭取、規(guī)程特性4個(gè)基本特性肤舞,傳輸比特流哨苛。數(shù)據(jù)鏈路層確保幀間正確傳輸己莺。網(wǎng)絡(luò)層是包,負(fù)責(zé)網(wǎng)絡(luò)尋址路由選擇要考慮網(wǎng)絡(luò)擁塞程度尖飞、服務(wù)質(zhì)量症副、線路花費(fèi)和線路有效性等,對(duì)數(shù)據(jù)進(jìn)行分段和重組政基,以使得數(shù)據(jù)長(zhǎng)度能滿足數(shù)據(jù)鏈路層所支持的最大的數(shù)據(jù)幀(MTU)的長(zhǎng)度贞铣。傳輸層單位是段,進(jìn)行分段和重組(到網(wǎng)絡(luò)層)沮明,會(huì)話層進(jìn)行管理和控制保證會(huì)話數(shù)據(jù)可靠傳送辕坝,它與傳輸層連接之間有三種關(guān)系,一對(duì)一珊擂、一對(duì)多圣勒、多對(duì)一费变,會(huì)話過(guò)程它需要決定使用全雙工通信還是半雙工通信摧扇,如果全雙工會(huì)話層在對(duì)話管理中要做的工作就很少,如果半雙工會(huì)話層通過(guò)一個(gè)數(shù)據(jù)令牌協(xié)調(diào)會(huì)話保證每次只有一個(gè)用戶能夠傳輸數(shù)據(jù)挚歧。表示層專門負(fù)責(zé)有關(guān)網(wǎng)絡(luò)中計(jì)算機(jī)信息表示方式的問(wèn)題扛稽,負(fù)責(zé)在不同數(shù)據(jù)格式之間數(shù)據(jù)轉(zhuǎn)換操作、數(shù)據(jù)加密滑负、壓縮等在张。DNS屬于應(yīng)用層。
TCP負(fù)責(zé)和遠(yuǎn)程主機(jī)的連接矮慕,IP負(fù)責(zé)尋址帮匾。發(fā)送方將每個(gè)已發(fā)送的報(bào)文備份在自己的發(fā)送緩沖區(qū)里,且在收到相應(yīng)的確認(rèn)之前不會(huì)丟棄所保存的報(bào)文段的痴鳄∥列保滑動(dòng)窗口協(xié)議是為了在端到端之間的實(shí)現(xiàn)流量控制,即匹配發(fā)送與接收的速度痪寻,而數(shù)據(jù)鏈路層的滑動(dòng)窗口控制是為了相鄰節(jié)點(diǎn)間實(shí)現(xiàn)流量控制螺句。三次握手時(shí)源主機(jī)發(fā)送連接請(qǐng)求報(bào)文段其首部中SYN(同步)標(biāo)志位置為1,并發(fā)送同步序列號(hào)X(如seq=100)表后面?zhèn)魉蛿?shù)據(jù)時(shí)的第一個(gè)數(shù)據(jù)字節(jié)的序號(hào)是X+1橡类,目標(biāo)主機(jī)同意連接在確認(rèn)報(bào)文中應(yīng)將ACK和SYN標(biāo)志置為1蛇尚,確認(rèn)號(hào)應(yīng)為X+1同時(shí)也應(yīng)為自己選擇一個(gè)序號(hào)Y,源主機(jī)收到后向目標(biāo)主機(jī)給出確認(rèn)顾画,其ACK置為1取劫,確認(rèn)號(hào)為Y+1匆笤,而自己的序號(hào)為X+1。ACK是標(biāo)志位勇凭,ack是確認(rèn)序號(hào)(與發(fā)送信號(hào)對(duì)應(yīng))疚膊。TCP規(guī)定SYN置1的報(bào)文段要消息掉一個(gè)序號(hào),當(dāng)源主機(jī)向目標(biāo)主機(jī)發(fā)送第一數(shù)據(jù)報(bào)文段時(shí)其序號(hào)仍為X+1因?yàn)槠淝耙粋€(gè)確認(rèn)報(bào)文段并不消耗序號(hào)虾标。當(dāng)一端請(qǐng)求關(guān)閉時(shí)發(fā)送FIN信號(hào)這時(shí)它不再發(fā)送寓盗,但依然可接收到消息(收到另一主機(jī)發(fā)的消息時(shí)還是要回饋?zhàn)约菏盏搅耍?br>
套接口也就是網(wǎng)絡(luò)進(jìn)程的ID,網(wǎng)絡(luò)通信歸根到底還是進(jìn)程間的通信璧函。網(wǎng)絡(luò)地址和端口號(hào)信息放在一個(gè)結(jié)構(gòu)體中也就是套接口地址結(jié)構(gòu)傀蚌,大多數(shù)的套接口函數(shù)都需要一個(gè)指向套接口地址結(jié)構(gòu)的指針作為參數(shù),并以此來(lái)傳遞地址信息蘸吓,套接口地址都以sockaddr_開關(guān)并以每個(gè)協(xié)議族名中的兩個(gè)字母作為結(jié)尾善炫,如IPv4對(duì)應(yīng)的是sockaddr_in。端口號(hào)065535库继,其中01023為周知端口箩艺,在bind、connect等系統(tǒng)調(diào)用中特定于協(xié)議的套接口地址結(jié)構(gòu)指針都要強(qiáng)轉(zhuǎn)換成該通用的套接口地址結(jié)構(gòu)指針以實(shí)現(xiàn)協(xié)議的無(wú)關(guān)性宪萄。端口與和IP地址都是以網(wǎng)絡(luò)字節(jié)序存儲(chǔ)的(大端模式)艺谆,不一定同與主機(jī)字節(jié)序。htonl,htons返回網(wǎng)絡(luò)字節(jié)序拜英,ntohl,ntohs返回主機(jī)字節(jié)序静汤,在套接口編程中,經(jīng)常需要一起操作結(jié)構(gòu)體中的某幾個(gè)字節(jié)居凶,就是用到字節(jié)操縱函數(shù)虫给,有bzero,bcopy,bcmp,memset,memcpy,memcpy。其中以b開頭的函數(shù)由任何支持套接口函數(shù)的系統(tǒng)所提供侠碧,以mem開關(guān)的函數(shù)由ANSI C提供抹估。IP地址轉(zhuǎn)換函數(shù):int inet_aton(const char* straddr, struct in_addr* addrptr);成功返回1失敗返回0,用于點(diǎn)分十進(jìn)制轉(zhuǎn)換為32位網(wǎng)絡(luò)字節(jié)序,char* inet_ntoa(struct in_addr indaar);成功返回指向點(diǎn)分十進(jìn)制數(shù)串的指針失敗返回NULL弄兜,in_addr_t inet_addr(const char* straddr);成功返回32位二進(jìn)制字節(jié)序地址药蜻,出錯(cuò)返回INADDR_NONE,IP和域名的轉(zhuǎn)換struct hostent* gethostbyname是域名或主機(jī)名到IP地址的轉(zhuǎn)換,gethostbyaddr與上相反挨队。socket()系統(tǒng)調(diào)用為套接口在sockfs文件系統(tǒng)中分配一個(gè)新的文件和dentry對(duì)象谷暮,并通過(guò)文件描述符把它們與調(diào)用進(jìn)程聯(lián)系起來(lái),進(jìn)程可以像訪問(wèn)一個(gè)已打開的文件一樣訪問(wèn)套接口在sockfs中的對(duì)應(yīng)文件但不能調(diào)用open來(lái)訪問(wèn)(sockfs文件系統(tǒng)沒(méi)有可視安裝點(diǎn)盛垦,其中的文件永遠(yuǎn)不會(huì)出現(xiàn)在系統(tǒng)目錄樹上)湿弦,當(dāng)套接口被關(guān)閉時(shí),內(nèi)核會(huì)自動(dòng)刪除sockfs中的inodes腾夯。PF_INET與AF_INET是等同的颊埃,PF代表Protocol Family蔬充,AF代表Address Family。綁定時(shí)設(shè)置IP為INADDR_ANY表本地計(jì)算機(jī)地址班利。調(diào)用accept時(shí)參數(shù)sockfd表牌監(jiān)聽狀態(tài)的socket饥漫,addr是一個(gè)sockaddr結(jié)構(gòu)體類型的指針,系統(tǒng)會(huì)把遠(yuǎn)程主機(jī)的信息保存到這個(gè)指針?biāo)赶虻慕Y(jié)構(gòu)體中罗标,當(dāng)accept函數(shù)接受一個(gè)連接時(shí)會(huì)返回一個(gè)新的socket標(biāo)識(shí)符庸队,int connect(int sockfd, const struct sockaddr* serv_addr, int addrlen)中serv_addr存儲(chǔ)著遠(yuǎn)程服務(wù)器的IP與端口號(hào)信息,send成功返回已發(fā)送的字節(jié)數(shù)失敗返回-1闯割,成功時(shí)返回接收的字節(jié)數(shù)失敗返回-1彻消。
對(duì)于UDP服務(wù)端設(shè)置bind即可,就可發(fā)送接收了宙拉。當(dāng)數(shù)據(jù)發(fā)送完畢后UDP套接口中的客戶端調(diào)用close釋放通信鏈路但不再發(fā)送“斷開連接通知”來(lái)通知服務(wù)器端釋放通信鏈路宾尚。int sendto(int sockfd, const void* msg, int len, unsigned int flags, struct sockaddr* toaddr, int* addrlen);成功返回實(shí)際發(fā)送的字節(jié)數(shù),失敗返回-1谢澈,int recvfrom(int sockfd, void* buf, int len, unsigned int flags, struct sockaddr* fromaddr, int* addrlen);成功返回實(shí)際接收的字節(jié)數(shù)失敗返回-1煌贴。 原始套接口只能由有root權(quán)限的用戶創(chuàng)建,如ping就使用原始套接口發(fā)送ICMP應(yīng)答請(qǐng)求锥忿,創(chuàng)建sockfd牛郑,ping是應(yīng)用層直接使用網(wǎng)絡(luò)層ICMP的例子,它沒(méi)有通過(guò)TCP或UDP缎谷。ICMP是IP層的一個(gè)協(xié)議井濒,其報(bào)文需要通過(guò)IP協(xié)議來(lái)發(fā)送灶似,發(fā)送前需要兩級(jí)封裝列林,首先添加其頭,再添加IP頭酪惭。網(wǎng)際校驗(yàn)和算法:把被校驗(yàn)的數(shù)據(jù)進(jìn)行16位累加希痴,然后取反,若數(shù)據(jù)字節(jié)長(zhǎng)度為奇數(shù)則尾部補(bǔ)上一個(gè)字節(jié)的0以湊成偶數(shù)春感。
第12章 Linux圖形界面編程
第13章 設(shè)計(jì)Linux下的計(jì)算器
第14章 Linux平臺(tái)下聊天軟件的設(shè)計(jì)
第15章 Linux遠(yuǎn)程管理工具的設(shè)計(jì)
第16章 Linux下簡(jiǎn)易防火墻軟件的設(shè)計(jì)
第17章 基于Linux的嵌入式家庭網(wǎng)關(guān)遠(yuǎn)程文件交互操作
圖書位置:
鏈接:https://pan.baidu.com/s/11bpTqIi0Julpd-p_LCuWZQ
提取碼:a135