


版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、1-1本文是 linux 系統(tǒng)調(diào)用系列文章的第一篇,對(duì)linux 系統(tǒng)調(diào)用的定義、基本原理、使用方法和注意事項(xiàng)大概作了一個(gè)介紹,以便讀者對(duì)linux 系統(tǒng)調(diào)用建立一個(gè)大致的印象。什么是系統(tǒng)調(diào)用?linux 內(nèi)核中設(shè)置了一組用于實(shí)現(xiàn)各種系統(tǒng)功能的子程序,稱為系統(tǒng)調(diào)用。用戶可以通過系統(tǒng)調(diào)用命令在自己的應(yīng)用程序中調(diào)用它們。從某種角度來看, 系統(tǒng)調(diào)用和普通的函數(shù)調(diào)用非常相似。區(qū)別僅僅在于,系統(tǒng)調(diào)用由操作系統(tǒng)核心提供,運(yùn)行于核心態(tài);而普通的函數(shù)調(diào)用由函數(shù)庫或用戶自己提供,運(yùn)行于用戶態(tài)。 二者在使用方式上也有相似之處,在下面將會(huì)提到。隨 linux 核心還提供了一些c 語言函數(shù)庫,這些庫對(duì)系統(tǒng)調(diào)用進(jìn)行了一
2、些包裝和擴(kuò)展, 因?yàn)檫@些庫函數(shù)與系統(tǒng)調(diào)用的關(guān)系非常緊密,所以習(xí)慣上把這些函數(shù)也稱為系統(tǒng)調(diào)用。linux 中共有多少個(gè)系統(tǒng)調(diào)用?這個(gè)問題可不太好回答,就算讓linus torvaldz 本人也不見得一下子就能說清楚。在 2.4.4版內(nèi)核中 ,狹義上的系統(tǒng)調(diào)用共有221個(gè),你可以在/include/asm-i386/unistd.h中找到它們的原本,也可以通過命令man 2 syscalls 察看它們的目錄( man pages 的版本一般比較老,可能有很多最新的調(diào)用都沒有包含在內(nèi))。廣義上的系統(tǒng)調(diào)用, 也就是以庫函數(shù)的形式實(shí)現(xiàn)的那些,它們的個(gè)數(shù)從來沒有人統(tǒng)計(jì)過,這是一件吃力不討好的活, 新內(nèi)核不
3、斷地在推出, 每一個(gè)新內(nèi)核中函數(shù)數(shù)目的變化根本就沒有人在乎,至少連內(nèi)核的修改者本人都不在乎,因?yàn)樗麄儚膩頉]有發(fā)布過一個(gè)此類的聲明。隨本文一起有一份經(jīng)過整理的列表,它不可能非常全面, 但常見的系統(tǒng)調(diào)用基本都已經(jīng)包含在內(nèi), 那里面只有不多的一部分是你平時(shí)用得到的,本專欄將會(huì)有選擇的對(duì)它們進(jìn)行介紹。為什么要用系統(tǒng)調(diào)用?實(shí)際上,很多已經(jīng)被我們習(xí)以為常的c 語言標(biāo)準(zhǔn)函數(shù),在linux平臺(tái)上的實(shí)現(xiàn)都是靠系統(tǒng)調(diào)用完成的, 所以如果想對(duì)系統(tǒng)底層的原理作深入的了解,掌握各種系統(tǒng)調(diào)用是初步的要求。 進(jìn)一步,若想成為一名linux 下編程高手,也就是我們常說的hacker ,其標(biāo)志之一也是能對(duì)各種系統(tǒng)調(diào)用有透徹的了
4、解。即使除去上面的原因, 在平常的編程中你也會(huì)發(fā)現(xiàn), 在很多情況下, 系統(tǒng)調(diào)用是實(shí)現(xiàn)你的想法的簡(jiǎn)潔有效的途徑, 所以有可能的話應(yīng)該盡量多掌握一些系統(tǒng)調(diào)用, 這會(huì)對(duì)你的程序設(shè)計(jì)過程帶來意想不到的幫助。系統(tǒng)調(diào)用是怎么工作的?一般的,進(jìn)程是不能訪問內(nèi)核的。它不能訪問內(nèi)核所占內(nèi)存空間也不能調(diào)用內(nèi)核函數(shù)。cpu 硬件決定了這些(這就是為什么它被稱作 保護(hù)模式 )。系統(tǒng)調(diào)用是這些規(guī)則的一個(gè)例 外。其原理是進(jìn)程先用適當(dāng)?shù)闹堤畛浼拇嫫?,然后調(diào)用一個(gè)特殊的指令,這個(gè)指令會(huì)跳到一個(gè)事先定義的內(nèi)核中的一個(gè)位置(當(dāng)然,這個(gè)位置是用戶進(jìn)程可讀但是不可寫的)。在 intel cpu 中,這個(gè)由中斷 0x80 實(shí)現(xiàn)。硬件知
5、道一旦你跳到這個(gè)位置,你就不是在限制模式下運(yùn)行的用戶,而是作為操作系統(tǒng)的內(nèi)核-所以你就可以為所欲為。進(jìn)程可以跳轉(zhuǎn)到的內(nèi)核位置叫做sysem_call。這個(gè)過程檢查系統(tǒng)調(diào)用號(hào),這個(gè)號(hào)碼告訴 內(nèi)核進(jìn)程請(qǐng)求哪種服務(wù)。然后,它查看系統(tǒng)調(diào)用表(sys_call_table) 找到所調(diào)用的內(nèi)核函數(shù)入口地址。 接著, 就調(diào)用函數(shù), 等返回后, 做一些系統(tǒng)檢查, 最后返回到進(jìn)程 (或到其他進(jìn)程,如果這個(gè)進(jìn)程時(shí)間用盡)。如果你希望讀這段代碼,它在/kernel/entry.s ,entry(system_call) 的下一行。如何使用系統(tǒng)調(diào)用?先來看一個(gè)例子:#include/* 定義宏 _syscall1*/
6、 #include/* 定義類型 time_t*/_syscall1(time_t, time, time_t *, tloc) /*宏,展開后得到 time() 函數(shù)的原型 */main()time_tthe_time;the_time=time(time_t*)0); /* 調(diào)用 time 系統(tǒng)調(diào)用 */ printf(thetime is %ldn, the_time);系統(tǒng)調(diào)用 time 返回從格林尼治時(shí)間1970 年 1 月 1 日 0:00 開始到現(xiàn)在的秒數(shù)。這是最標(biāo)準(zhǔn)的系統(tǒng)調(diào)用的形式,宏_syscall1() 展開來得到一個(gè)函數(shù)原型,稍后我會(huì)作詳細(xì)解釋。但事實(shí)上,如果把程序改成下
7、面的樣子,程序也可以運(yùn)行得同樣的結(jié)果。#include main()time_tthe_time;the_time=time(time_t*)0); /* 調(diào)用 time 系統(tǒng)調(diào)用 */ printf(thetime is %ldn, the_time);1-2這是因?yàn)樵趖ime.h 中實(shí)際上已經(jīng)用庫函數(shù)的形式實(shí)現(xiàn)了time 這個(gè)系統(tǒng)調(diào)用,替我們省掉了調(diào)用 _syscall1 宏展開得到函數(shù)原型這一步。大多數(shù)系統(tǒng)調(diào)用都在各種c 語言函數(shù)庫中有所實(shí)現(xiàn),所以在一般情況下,我們都可以像調(diào)用普通的庫函數(shù)那樣調(diào)用系統(tǒng)調(diào)用,只在極個(gè)別的情況下, 我們才有機(jī)會(huì)用到 _syscall*()這幾個(gè)宏。_sysc
8、all*() 是什么?在 unistd.h 里定義了 7 個(gè)宏,分別是_syscall0(type,name)_syscall1(type,name, type1, arg1)_syscall2(type, name, type1, arg1, type2, arg2)_syscall3(type, name, type1, arg1, type2, arg2, type3, arg3)_syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4)_syscall5(type, name, type1, arg
9、1, type2, arg2, type3, arg3, type4, arg4, type5, arg5)_syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5, type6, arg6)它們看起來似乎不太像宏,但其實(shí)質(zhì)和#definemaxsize100里面的 maxsize沒有任何區(qū)別。它們的作用是形成相應(yīng)的系統(tǒng)調(diào)用函數(shù)原型,供我們?cè)诔绦蛑姓{(diào)用。我們很容易就能發(fā)現(xiàn)規(guī)律, _syscall 后面的數(shù)字和 typen, argn 的數(shù)目一樣多。事實(shí)上,_syscall 后面跟的
10、數(shù)字指明了展開后形成函數(shù)的參數(shù)的個(gè)數(shù),讓我們看一個(gè)實(shí)例,就是剛剛用過的time 系統(tǒng)調(diào)用:_syscall1(time_t,time, time_t *, tloc)展開后的情形是這樣:time_ttime(time_t *tloc)longres; asm do volatile(int $0x80 : =a (res) : 0 (13), b (long)(tloc);if(unsigned long)(res) = (unsigned long)(-125) errno = -(res); res= -1;return(time_t) (res); while (0) ;可以看出, _s
11、yscall1(time_t, time, time_t *, tloc)展開成一個(gè)名為time 的函數(shù),原參數(shù)time_t 就是函數(shù)的返回類型,原參數(shù)time_t * 和 tloc 分別構(gòu)成新函數(shù)的參數(shù)。事實(shí)上,程序中用到的 time 函數(shù)的原型就是它。errno 是什么?為防止和正常的返回值混淆,系統(tǒng)調(diào)用并不直接返回錯(cuò)誤碼,而是將錯(cuò)誤碼放入一個(gè)名為 errno 的全局變量中。如果一個(gè)系統(tǒng)調(diào)用失敗,你可以讀出errno 的值來確定問題所在。errno 不同數(shù)值所代表的錯(cuò)誤消息定義在errno.h 中,你也可以通過命令man 3 errno 來察看它們。需要注意的是, errno 的值只在函數(shù)
12、發(fā)生錯(cuò)誤時(shí)設(shè)置,如果函數(shù)不發(fā)生錯(cuò)誤,errno 的值就無定義,并不會(huì)被置為0。另外,在處理 errno 前最好先把它的值存入另一個(gè)變量,因?yàn)樵阱e(cuò)誤處理過程中,即使像printf() 這樣的函數(shù)出錯(cuò)時(shí)也會(huì)改變errno 的值。系統(tǒng)調(diào)用兼容性好嗎?很遺憾,答案是 -不好。但這決不意味著你的程序會(huì)三天兩頭的導(dǎo)致系統(tǒng)崩潰,因?yàn)橄到y(tǒng)調(diào)用是 linux 的內(nèi)核提供的,所以它們工作起來非常穩(wěn)定,對(duì)于此點(diǎn)無需絲毫懷疑,在絕大多數(shù)的情況下,系統(tǒng)調(diào)用要比你自己編寫的代碼可靠而高效的多。但是,在 linux 的各版本內(nèi)核之間,系統(tǒng)調(diào)用的兼容性表現(xiàn)得并不像想象那么好,這是由 linux 本身的性質(zhì)決定的。 linux
13、 是一群程序設(shè)計(jì)高手利用業(yè)余時(shí)間開發(fā)出來的,他們中間的大部分人沒有把linux 當(dāng)成一個(gè)嚴(yán)肅的商業(yè)軟件, (現(xiàn)在的情況有些不同了, 隨著 linux 商業(yè)公司和以 linux 為生的人的增長(zhǎng),不少人的腦筋發(fā)生了變化。)結(jié)果就是,如果新的方 案在效率和兼容性上發(fā)生了矛盾,他們往往舍棄兼容性而追求效率,就這樣, 如果他們認(rèn)為某個(gè)系統(tǒng)調(diào)用實(shí)現(xiàn)的比較糟糕,他們就會(huì)毫不猶豫的作出修改,有些時(shí)候甚至連接口也一起 改掉了,更可怕的是,很多時(shí)候,他們對(duì)自己的修改連個(gè)招呼也不打,在任何文檔里都找不到關(guān)于修改的提示。這樣,每當(dāng)新內(nèi)核推出的時(shí)候,很可能都會(huì)悄悄的更新一些系統(tǒng)調(diào)用, 用戶編制的應(yīng)用程序也會(huì)跟著出錯(cuò)。說
14、到這里,你是不是感覺前途一片昏暗呢?呵呵,不用太緊張,如前面所說,隨著越來越多的人把linux 當(dāng)成自己的飯碗,不兼容的情況也越來越罕見。從2.2 版本以后的linux 內(nèi)核已經(jīng)非常穩(wěn)定了, 不過盡管如此, 你還是有必要在每個(gè)新內(nèi)核推出之后,對(duì)自己的應(yīng)用程序進(jìn)行兼容性測(cè)試,以防止意外的發(fā)生。該如何學(xué)習(xí)使用 linux 系統(tǒng)調(diào)用呢?你可以用 man 2 系統(tǒng)調(diào)用名稱 的命令來查看各條系統(tǒng)調(diào)用的介紹,但這首先要求你要 有不錯(cuò)的英語基礎(chǔ),其次還得有一定的程序設(shè)計(jì)和系統(tǒng)編程的功底,man pages 不會(huì)涉及太多的應(yīng)用細(xì)節(jié),因?yàn)樗皇且粋€(gè)手冊(cè)而非教程。如果man pages 所提供的東西不能使你感到非
15、常滿意,那就跟我來吧,本專欄將向你展示linux 系統(tǒng)調(diào)用編程的無窮魅力。對(duì)讀者的兩點(diǎn)小小的要求:1) 讀者必須有一定的c 語言編程經(jīng)驗(yàn);2) 讀者必須有一定的 linux 使用經(jīng)驗(yàn)。如果你能完全看懂本文從開頭到這里所講的東西, 你就合格了。收拾好行囊,準(zhǔn)備出發(fā)吧!進(jìn)程管理 2-1本文介紹了 linux 下的進(jìn)程概念, 并著重講解了與 linux 進(jìn)程管理相關(guān)的 4 個(gè)重要系統(tǒng)調(diào)用 getpid, fork, exit和_exit,輔助一些例程說明了它們的特點(diǎn)和使用方法。關(guān)于進(jìn)程的一些必要知識(shí)先看一下進(jìn)程在大學(xué)課本里的標(biāo)準(zhǔn)定義:“進(jìn)程是可并發(fā)執(zhí)行的程序在一個(gè)數(shù)據(jù)集合上的運(yùn)行過程。 ”這個(gè)定義非
16、常嚴(yán)謹(jǐn),而且難懂,如果你沒有一下子理解這句話,就不妨看看筆者自己的并不嚴(yán)謹(jǐn)?shù)慕忉尅N覀兇蠹叶贾?,硬盤上的一個(gè)可執(zhí)行文件經(jīng)常被稱作程序,在 linux 系統(tǒng)中,當(dāng)一個(gè)程序開始執(zhí)行后,在開始執(zhí)行到執(zhí)行完畢退出這段時(shí)間里,它在內(nèi)存中的部分就被稱作一個(gè)進(jìn)程。當(dāng)然,這個(gè)解釋并不完善,但好處是容易理解,在以下的文章中,我們將會(huì)對(duì)進(jìn)程作一些更全面的認(rèn)識(shí)。linux 進(jìn)程簡(jiǎn)介linux 是一個(gè)多任務(wù)的操作系統(tǒng),也就是說,在同一個(gè)時(shí)間內(nèi),可以有多個(gè)進(jìn)程同時(shí)執(zhí)行。如果讀者對(duì)計(jì)算機(jī)硬件體系有一定了解的話,會(huì)知道我們大家常用的單cpu 計(jì)算機(jī)實(shí)際上在一個(gè)時(shí)間片斷內(nèi)只能執(zhí)行一條指令,那么 linux 是如何實(shí)現(xiàn)多進(jìn)程
17、同時(shí)執(zhí)行的呢?原來 linux 使用了一種稱為“進(jìn)程調(diào)度(process scheduling)”的手段,首先,為每個(gè)進(jìn)程指派一定的運(yùn)行時(shí)間,這個(gè)時(shí)間通常很短,短到以毫秒為單位,然后依照某種規(guī)則,從眾多進(jìn)程中挑選一個(gè)投入運(yùn)行, 其他的進(jìn)程暫時(shí)等待, 當(dāng)正在運(yùn)行的那個(gè)進(jìn)程時(shí)間耗盡,或執(zhí)行完畢退出,或因某種原因暫停,linux 就會(huì)重新進(jìn)行調(diào)度,挑選下一個(gè)進(jìn)程投入運(yùn)行。因?yàn)槊總€(gè)進(jìn)程占用的時(shí)間片都很短,在我們使用者的角度來看,就好像多個(gè)進(jìn)程同時(shí)運(yùn)行一樣了。在 linux中,每個(gè)進(jìn)程在創(chuàng)建時(shí)都會(huì)被分配一個(gè)數(shù)據(jù)結(jié)構(gòu),稱為進(jìn)程控制塊(process control block ,簡(jiǎn)稱 pcb)。 pcb
18、中包含了很多重要的信息,供系統(tǒng)調(diào)度和進(jìn)程本身執(zhí)行使用,其中最重要的莫過于進(jìn)程id ( process id )了,進(jìn)程 id 也被稱作進(jìn)程標(biāo)識(shí)符,是一個(gè)非負(fù)的整數(shù),在linux 操作系統(tǒng)中唯一地標(biāo)志一個(gè)進(jìn)程,在我們最常使用的i386 架構(gòu)(即pc 使用的架構(gòu))上,一個(gè)非負(fù)的整數(shù)的變化范圍是0-32767 ,這也是我們所有可能取到的進(jìn)程 id 。其實(shí)從進(jìn)程 id 的名字就可以看出,它就是進(jìn)程的身份證號(hào)碼,每個(gè)人的身份證號(hào)碼都不會(huì)相同,每個(gè)進(jìn)程的進(jìn)程id 也不會(huì)相同。一個(gè)或多個(gè)進(jìn)程可以合起來構(gòu)成一個(gè)進(jìn)程組(process group ),一個(gè)或多個(gè)進(jìn)程組可以合起來構(gòu)成一個(gè)會(huì)話(session)。這
19、樣我們就有了對(duì)進(jìn)程進(jìn)行批量操作的能力,比如通過向某個(gè)進(jìn)程組發(fā)送信號(hào)來實(shí)現(xiàn)向該組中的每個(gè)進(jìn)程發(fā)送信號(hào)。最后,讓我們通過ps 命令親眼看一看自己的系統(tǒng)中目前有多少進(jìn)程在運(yùn)行:$ps -aux(以下是在我的計(jì)算機(jī)上的運(yùn)行結(jié)果,你的結(jié)果很可能與這不同。)以上除標(biāo)題外,每一行都代表一個(gè)進(jìn)程。在各列中,pid 一列代表了各進(jìn)程的進(jìn)程id , command一列代表了進(jìn)程的名稱或在shell 中調(diào)用的命令行,對(duì)其他列的具體含義,我就不再作解釋,有興趣的讀者可以去參考相關(guān)書籍。進(jìn)程管理 2-2getpid在 2.4.4 版內(nèi)核中, getpid 是第 20 號(hào)系統(tǒng)調(diào)用,其在linux 函數(shù)庫中的原型是:#in
20、clude /*提供類型 pid_t 的定義 */ #include /*提供函數(shù)的定義 */pid_tgetpid(void);getpid的作用很簡(jiǎn)單,就是返回當(dāng)前進(jìn)程的進(jìn)程id ,請(qǐng)大家看以下的例子:/*getpid_test.c */ #includemain()printf(“ tchuerrent process id is %dn” , getpid();細(xì)心的讀者可能注意到了,這個(gè)程序的定義里并沒有包含頭文件sys/types.h,這是因?yàn)槲覀冊(cè)诔绦蛑袥]有用到pid_t 類型, pid_t 類型即為進(jìn)程id 的類型。事實(shí)上,在i386 架構(gòu)上(就是我們一般pc 計(jì)算機(jī)的架構(gòu))
21、, pid_t 類型是和 int 類型完全兼容的,我們可以用處理整形數(shù)的方法去處理pid_t 類型的數(shù)據(jù),比如,用 %d 把它打印出來。編譯并運(yùn)行程序 getpid_test.c:$gccgetpid_test.c -o getpid_test$./getpid_testthecurrent process id is 1980(你自己的運(yùn)行結(jié)果很可能與這個(gè)數(shù)字不一樣,這是很正常的。) 再運(yùn)行一遍:$./getpid_testthe current process id is 1981正如我們所見, 盡管是同一個(gè)應(yīng)用程序,每一次運(yùn)行的時(shí)候, 所分配的進(jìn)程標(biāo)識(shí)符都不相同。fork在 2.4.4
22、版內(nèi)核中, fork 是第 2 號(hào)系統(tǒng)調(diào)用,其在linux 函數(shù)庫中的原型是:#include /*提供類型 pid_t 的定義 */ #include /*提供函數(shù)的定義 */pid_tfork(void);只看 fork 的名字,可能難得有幾個(gè)人可以猜到它是做什么用的。 fork 系統(tǒng)調(diào)用的作用是復(fù)制一個(gè)進(jìn)程。 當(dāng)一個(gè)進(jìn)程調(diào)用它, 完成后就出現(xiàn)兩個(gè)幾乎一模一樣的進(jìn)程, 我們也由此得到了一個(gè)新進(jìn)程。據(jù)說 fork 的名字就是來源于這個(gè)與叉子的形狀頗有幾分相似的工作流程。在 linux 中,創(chuàng)造新進(jìn)程的方法只有一個(gè),就是我們正在介紹的fork 。其他一些庫函數(shù), 如 system(),看起來似
23、乎它們也能創(chuàng)建新的進(jìn)程,如果能看一下它們的源碼就會(huì)明白,它們實(shí)際上也在內(nèi)部調(diào)用了fork 。包括我們?cè)诿钚邢逻\(yùn)行應(yīng)用程序,新的進(jìn)程也是由shell 調(diào)用 fork 制造出來的。 fork 有一些很有意思的特征,下面就讓我們通過一個(gè)小程序來對(duì)它有更多的了解。/*fork_test.c */ #include #inlcudemain()pid_t pid;/* 此時(shí)僅有一個(gè)進(jìn)程*/ pid=fork();/* 此時(shí)已經(jīng)有兩個(gè)進(jìn)程在同時(shí)運(yùn)行*/ if(pid 0)printf(“ error in fork!” ); else if(pid=0)printf(“ i am the child p
24、rocess, my process id is %dn” , getpid();elseprintf(“ i am the parent process, my process id is %dn” , getpid();編譯并運(yùn)行:$gcc fork_test.c -o fork_test$./fork_testi am the parent process, my process id is 1991 i am the child process, my process id is 1992看這個(gè)程序的時(shí)候,頭腦中必須首先了解一個(gè)概念:在語句 pid=fork() 之前,只有一個(gè)進(jìn)程在執(zhí)
25、行這段代碼, 但在這條語句之后, 就變成兩個(gè)進(jìn)程在執(zhí)行了, 這兩個(gè)進(jìn)程的代碼部分完全相同,將要執(zhí)行的下一條語句都是 if(pid=0) 。兩個(gè)進(jìn)程中,原先就存在的那個(gè)被稱作“父進(jìn)程” ,新出現(xiàn)的那個(gè)被稱作“子進(jìn)程” 。父子進(jìn)程的區(qū)別除了進(jìn)程標(biāo)志符( process id)不同外,變量 pid 的值也不相同, pid 存放的是fork 的返回值。 fork 調(diào)用的一個(gè)奇妙之處就是它僅僅被調(diào)用一次,卻能夠返回兩次,它可能有三種不同的返回值:在父進(jìn)程中, fork 返回新創(chuàng)建子進(jìn)程的進(jìn)程id ; 在子進(jìn)程中, fork 返回 0;如果出現(xiàn)錯(cuò)誤, fork 返回一個(gè)負(fù)值; 進(jìn)程管理 2-3fork 出
26、錯(cuò)可能有兩種原因:( 1)當(dāng)前的進(jìn)程數(shù)已經(jīng)達(dá)到了系統(tǒng)規(guī)定的上限,這時(shí) errno 的值被設(shè)置為 eagain 。( 2) 系統(tǒng)內(nèi)存不足,這時(shí)errno 的值被設(shè)置為 enomem 。(關(guān)于 errno 的意義,請(qǐng)參考本系列的第一篇文章。)fork 系統(tǒng)調(diào)用出錯(cuò)的可能性很小,而且如果出錯(cuò),一般都為第一種錯(cuò)誤。如果出現(xiàn)第二種錯(cuò)誤,說明系統(tǒng)已經(jīng)沒有可分配的內(nèi)存,正處于崩潰的邊緣,這種情況對(duì)linux 來說是很罕見的。說到這里,聰明的讀者可能已經(jīng)完全看懂剩下的代碼了,如果pid 小于 0,說明出現(xiàn)了錯(cuò)誤; pid=0 ,就說明 fork 返回了 0,也就說明當(dāng)前進(jìn)程是子進(jìn)程,就去執(zhí)行 printf(i
27、 am the child!) ,否則( else),當(dāng)前進(jìn)程就是父進(jìn)程,執(zhí)行printf(i am the parent!)。完美主義者會(huì)覺得這很冗余, 因?yàn)閮蓚€(gè)進(jìn)程里都各有一條它們永遠(yuǎn)執(zhí)行不到的語句。不必過于為此耿耿于懷,畢竟很多年以前, unix 的鼻祖?zhèn)冊(cè)诋?dāng)時(shí)內(nèi)存小得無法想象的計(jì)算機(jī)上就是這樣寫程序的,以我們?nèi)缃竦摹昂A俊眱?nèi)存,完全可以把這幾個(gè)字節(jié)的顧慮拋到九霄云外。說到這里,可能有些讀者還有疑問:如果fork 后子進(jìn)程和父進(jìn)程幾乎完全一樣,而系統(tǒng)中產(chǎn)生新進(jìn)程唯一的方法就是fork ,那豈不是系統(tǒng)中所有的進(jìn)程都要一模一樣嗎?那我們要執(zhí)行新的應(yīng)用程序時(shí)候怎么辦呢?從對(duì)linux系統(tǒng)的經(jīng)驗(yàn)
28、中,我們知道這種問題并不存在。至于采用了什么方法,我們把這個(gè)問題留到后面具體討論。exit在 2.4.4 版內(nèi)核中, exit 是第 1 號(hào)調(diào)用,其在linux 函數(shù)庫中的原型是:#include voidexit(int status);不像 fork 那么難理解,從 exit 的名字就能看出,這個(gè)系統(tǒng)調(diào)用是用來終止一個(gè)進(jìn)程的。無論在程序中的什么位置,只要執(zhí)行到 exit 系統(tǒng)調(diào)用, 進(jìn)程就會(huì)停止剩下的所有操作,清除包括 pcb 在內(nèi)的各種數(shù)據(jù)結(jié)構(gòu),并終止本進(jìn)程的運(yùn)行。請(qǐng)看下面的程序:/*exit_test1.c */ #includemain()printf(“ tphrioscess w
29、ill exit!n” ); exit(0);printf(“ nebverdisplayed!n” );編譯后運(yùn)行:$gccexit_test1.c -o exit_test1$./exit_test1thisprocess will exit!我們可以看到,程序并沒有打印后面的“never be displayed!n”,因?yàn)樵诖酥?,在?zhí)行到 exit(0) 時(shí),進(jìn)程就已經(jīng)終止了。exit 系統(tǒng)調(diào)用帶有一個(gè)整數(shù)類型的參數(shù)status,我們可以利用這個(gè)參數(shù)傳遞進(jìn)程結(jié)束時(shí) 的狀態(tài),比如說,該進(jìn)程是正常結(jié)束的,還是出現(xiàn)某種意外而結(jié)束的,一般來說,0 表示沒有意外的正常結(jié)束;其他的數(shù)值表示出現(xiàn)了
30、錯(cuò)誤,進(jìn)程非正常結(jié)束。我們?cè)趯?shí)際編程時(shí),可以用 wait 系統(tǒng)調(diào)用接收子進(jìn)程的返回值,從而針對(duì)不同的情況進(jìn)行不同的處理。關(guān)于wait的詳細(xì)情況,我們將在以后的篇幅中進(jìn)行介紹。exit 和_exit作為系統(tǒng)調(diào)用而言, _exit 和 exit 是一對(duì)孿生兄弟,它們究竟相似到什么程度,我們可以從 linux 的源碼中找到答案:#definenrexitnr_exit /*摘自文件 include/asm-i386/unistd.h 第 334 行 */“nr_”是在 linux 的源碼中為每個(gè)系統(tǒng)調(diào)用加上的前綴,請(qǐng)注意第一個(gè)exit 前 有 2條下劃線,第二個(gè)exit 前只有 1 條下劃線。這時(shí)隨
31、便一個(gè)懂得c 語言并且頭腦清醒的人都會(huì)說,_exit 和 exit 沒有任何區(qū)別,但我們還要講一下這兩者之間的區(qū)別,這種區(qū)別主要體現(xiàn)在它們?cè)诤瘮?shù)庫中的定義。_exit在linux 函數(shù)庫中的原型是:#include void_exit(int status);和 exit 比較一下, exit() 函數(shù)定義在 stdlib.h 中,而_exit() 定義在 unistd.h 中,從名字上看,stdlib.h 似乎比 unistd.h 高級(jí)一點(diǎn), 那么,它們之間到底有什么區(qū)別呢?讓我們先來看流程圖, 通過下圖,我們會(huì)對(duì)這兩個(gè)系統(tǒng)調(diào)用的執(zhí)行過程產(chǎn)生一個(gè)較為直觀的認(rèn)識(shí)。從圖中可以看出, _exit(
32、) 函數(shù)的作用最為簡(jiǎn)單:直接使進(jìn)程停止運(yùn)行,清除其使用的內(nèi)存空間,并銷毀其在內(nèi)核中的各種數(shù)據(jù)結(jié)構(gòu);exit() 函數(shù)則在這些基礎(chǔ)上作了一些包裝,在執(zhí)行退出之前加了若干道工序,也是因?yàn)檫@個(gè)原因, 有些人認(rèn)為 exit 已經(jīng)不能算是純粹的系統(tǒng)調(diào)用。exit() 函數(shù)與 _exit() 函數(shù)最大的區(qū)別就在于exit() 函數(shù)在調(diào)用 exit 系統(tǒng)調(diào)用之前要檢查文件的打開情況,把文件緩沖區(qū)中的內(nèi)容寫回文件,就是圖中的“清理i/o 緩沖”一項(xiàng)。在 linux 的標(biāo)準(zhǔn)函數(shù)庫中, 有一套稱作“高級(jí) i/o ”的函數(shù), 我們熟知的 printf() 、fopen() 、fread()、 fwrite() 都在
33、此列,它們也被稱作“緩沖i/o (buffered i/o )”,其特征是對(duì)應(yīng)每一個(gè) 打開的文件,在內(nèi)存中都有一片緩沖區(qū),每次讀文件時(shí),會(huì)多讀出若干條記錄,這樣下次讀文件時(shí)就可以直接從內(nèi)存的緩沖區(qū)中讀取,每次寫文件的時(shí)候, 也僅僅是寫入內(nèi)存中的緩沖區(qū),等滿足了一定的條件 (達(dá)到一定數(shù)量, 或遇到特定字符, 如換行符 n 和文件結(jié)束符 eof ),再將緩沖區(qū)中的內(nèi)容一次性寫入文件,這樣就大大增加了文件讀寫的速度,但也為我們編程 帶來了一點(diǎn)點(diǎn)麻煩。如果有一些數(shù)據(jù), 我們認(rèn)為已經(jīng)寫入了文件,實(shí)際上因?yàn)闆]有滿足特定 的條件,它們還只是保存在緩沖區(qū)內(nèi),這時(shí)我們用_exit() 函數(shù)直接將進(jìn)程關(guān)閉,緩沖區(qū)
34、中的數(shù)據(jù)就會(huì)丟失,反之,如果想保證數(shù)據(jù)的完整性,就一定要使用exit() 函數(shù)。請(qǐng)看以下例程:/*exit2.c */ #includemain()printf(“ outpbuetginn” );printf(“ contiennbt uffer” ); exit(0);編譯并運(yùn)行:$gcc exit2.c -o exit2$./exit2outputbegin contentin buffer/*_exit1.c */#include main()printf(“ outpbuetginn” ); printf(“ contiennbt uffer” );_exit(0);編譯并運(yùn)行:$g
35、cc _exit1.c -o _exit1$./_exit1 outputbegin在 linux 中,標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出都是作為文件處理的,雖然是一類特殊的文件,但從程序員的角度來看, 它們和硬盤上存儲(chǔ)數(shù)據(jù)的普通文件并沒有任何區(qū)別。與所有其他文件一樣,它們?cè)诖蜷_后也有自己的緩沖區(qū)。請(qǐng)讀者結(jié)合前面的敘述, 思考一下為什么這兩個(gè)程序會(huì)得出不同的結(jié)果。相信如果您理解了我前面所講的內(nèi)容,會(huì)很容易的得出結(jié)論。在這篇文章中, 我們對(duì) linux 的進(jìn)程管理作了初步的了解,并在此基礎(chǔ)上學(xué)習(xí)了getpid 、fork 、exit 和 _exit 四個(gè)系統(tǒng)調(diào)用。在下一篇文章中,我們將學(xué)習(xí)與linux 進(jìn)程管理
36、相關(guān)的其他系統(tǒng)調(diào)用,并將作一些更深入的探討。僵尸進(jìn)程 3-1在前面的文章中,我們已經(jīng)了解了父進(jìn)程和子進(jìn)程的概念,并已經(jīng)掌握了系統(tǒng)調(diào)用exit 的用法,但可能很少有人意識(shí)到,在一個(gè)進(jìn)程調(diào)用了exit 之后,該進(jìn)程并非馬上就消失掉, 而是留下一個(gè)稱為僵尸進(jìn)程(zombie )的數(shù)據(jù)結(jié)構(gòu)。在linux 進(jìn)程的 5 種狀態(tài)中,僵尸進(jìn)程是非常特殊的一種, 它已經(jīng)放棄了幾乎所有內(nèi)存空間,沒有任何可執(zhí)行代碼, 也不能被調(diào)度, 僅僅在進(jìn)程列表中保留一個(gè)位置,記載該進(jìn)程的退出狀態(tài)等信息供其他進(jìn)程收集,除此之外, 僵尸進(jìn)程不再占有任何內(nèi)存空間。從這點(diǎn)來看, 僵尸進(jìn)程雖然有一個(gè)很酷的名字,但它的影響力遠(yuǎn)遠(yuǎn)抵不上那些
37、真正的僵尸兄弟,真正的僵尸總能令人感到恐怖,而僵尸進(jìn)程卻除了留下一些供人憑吊的信息,對(duì)系統(tǒng)毫無作用。也許讀者們還對(duì)這個(gè)新概念比較好奇,那就讓我們來看一眼linux 里的僵尸進(jìn)程究竟長(zhǎng)什么樣子。當(dāng)一個(gè)進(jìn)程已退出,但其父進(jìn)程還沒有調(diào)用系統(tǒng)調(diào)用wait(稍后介紹) 對(duì)其進(jìn)行收集之前的這段時(shí)間里,它會(huì)一直保持僵尸狀態(tài),利用這個(gè)特點(diǎn),我們來寫一個(gè)簡(jiǎn)單的小程序:/* zombie.c */#include #includemain()pid_t pid; pid=fork();if(pid0) /*如果出錯(cuò) */printf(erroroccurred!n);else if(pid=0)/*如果是子進(jìn)程
38、*/ exit(0);else/*如果是父進(jìn)程 */sleep(60);/*休眠 60 秒,這段時(shí)間里,父進(jìn)程什么也干不了*/ wait(null);/*收集僵尸進(jìn)程 */sleep 的作用是讓進(jìn)程休眠指定的秒數(shù),在這60 秒內(nèi),子進(jìn)程已經(jīng)退出,而父進(jìn)程正忙著睡覺,不可能對(duì)它進(jìn)行收集,這樣,我們就能保持子進(jìn)程60 秒的僵尸狀態(tài)。編譯這個(gè)程序:$ cc zombie.c -o zombie后臺(tái)運(yùn)行程序,以使我們能夠執(zhí)行下一條命令:$./zombie & 11577列一下系統(tǒng)內(nèi)的進(jìn)程:$ps -ax. .1177pts/0s0:00-bash1577pts/0s0:00./zombie1578pt
39、s/0z0:00zombie 1579pts/0r0:00ps -ax看到中間的“ z”了嗎?那就是僵尸進(jìn)程的標(biāo)志,它表示1578 號(hào)進(jìn)程現(xiàn)在就是一個(gè)僵尸進(jìn)程。我們已經(jīng)學(xué)習(xí)了系統(tǒng)調(diào)用exit ,它的作用是使進(jìn)程退出,但也僅僅限于將一個(gè)正常的進(jìn)程變成一個(gè)僵尸進(jìn)程,并不能將其完全銷毀。僵尸進(jìn)程雖然對(duì)其他進(jìn)程幾乎沒有什么影響, 不占用 cpu 時(shí)間,消耗的內(nèi)存也幾乎可以忽略不計(jì),但有它在那里呆著,還是讓人覺得心里很不舒服。而且linux 系統(tǒng)中進(jìn)程數(shù)目是有限制的,在一些特殊的情況下,如果存在太多的僵尸進(jìn)程,也會(huì)影響到新進(jìn)程的產(chǎn)生。那么,我們?cè)撊绾蝸硐麥邕@些僵尸進(jìn)程呢?先來了解一下僵尸進(jìn)程的來由,我們
40、知道,linux 和 unix 總有著剪不斷理還亂的親緣關(guān)系, 僵尸進(jìn)程的概念也是從unix 上繼承來的, 而 unix的先驅(qū)們?cè)O(shè)計(jì)這個(gè)東西并非是因?yàn)殚e來無聊想煩煩其他的程序員。僵尸進(jìn)程中保存著很多對(duì)程序員和系統(tǒng)管理員非常重要的 信息,首先,這個(gè)進(jìn)程是怎么死亡的?是正常退出呢,還是出現(xiàn)了錯(cuò)誤,還是被其它進(jìn)程強(qiáng)迫退出的?其次, 這個(gè)進(jìn)程占用的總系統(tǒng)cpu 時(shí)間和總用戶 cpu 時(shí)間分別是多少?發(fā)生頁錯(cuò)誤的數(shù)目和收到信號(hào)的數(shù)目。這些信息都被存儲(chǔ)在僵尸進(jìn)程中,試想如果沒有僵尸進(jìn)程,進(jìn)程一退出,所有與之相關(guān)的信息都立刻歸于無形,而此時(shí)程序員或系統(tǒng)管理員需要用到, 就只好干瞪眼了。僵尸進(jìn)程 3-2那么,
41、我們?nèi)绾问占@些信息,并終結(jié)這些僵尸進(jìn)程呢?就要靠我們下面要講到的waitpid 調(diào)用和 wait 調(diào)用。這兩者的作用都是收集僵尸進(jìn)程留下的信息,同時(shí)使這個(gè)進(jìn)程徹底消失。下面就對(duì)這兩個(gè)調(diào)用分別作詳細(xì)介紹。waitwait 的函數(shù)原型是:#include /*提供類型 pid_t 的定義 */ #includepid_t wait(int *status)進(jìn)程一旦調(diào)用了wait ,就立即阻塞自己,由wait 自動(dòng)分析是否當(dāng)前進(jìn)程的某個(gè)子進(jìn)程已經(jīng)退出, 如果讓它找到了這樣一個(gè)已經(jīng)變成僵尸的子進(jìn)程,wait 就會(huì)收集這個(gè)子進(jìn)程的信息,并把它徹底銷毀后返回;如果沒有找到這樣一個(gè)子進(jìn)程,wait 就會(huì)一
42、直阻塞在這里,直到有一個(gè)出現(xiàn)為止。參數(shù) status 用來保存被收集進(jìn)程退出時(shí)的一些狀態(tài),它是一個(gè)指向 int 類型的指針。但如果我們對(duì)這個(gè)子進(jìn)程是如何死掉的毫不在意,只想把這個(gè)僵尸進(jìn)程消滅掉, (事實(shí)上絕大多數(shù)情況下,我們都會(huì)這樣想) ,我們就可以設(shè)定這個(gè)參數(shù)為 null ,就象下面這樣:pid= wait(null);如果成功, wait 會(huì)返回被收集的子進(jìn)程的進(jìn)程id ,如果調(diào)用進(jìn)程沒有子進(jìn)程,調(diào)用就會(huì)失敗,此時(shí)wait 返回 -1,同時(shí) errno 被置為 echild 。實(shí)戰(zhàn)下面就讓我們用一個(gè)例子來實(shí)戰(zhàn)應(yīng)用一下wait 調(diào)用,程序中用到了系統(tǒng)調(diào)用fork ,如果你對(duì)此不大熟悉或已經(jīng)忘
43、記了,請(qǐng)參考上一篇文章進(jìn)程管理相關(guān)的系統(tǒng)調(diào)用(1)。/*wait1.c */#include #include #include #includemain()pid_t pc, pr; pc=fork();if(pc0)/*如果出錯(cuò) */printf(errorocurred!n);else if(pc=0)/*如果是子進(jìn)程 */printf(this is child process with pid of %dn, getpid(); sleep(10);/*睡眠 10 秒鐘 */else/*如果是父進(jìn)程 */pr=wait(null);/*在這里等待 */printf(i catched
44、 a child process with pid of %dn), pr);exit(0);編譯并運(yùn)行 :$ cc wait1.c -o wait1$ ./wait1this is child process with pid of 1508i catched a child process with pid of 1508可以明顯注意到,在第2 行結(jié)果打印出來前有10 秒鐘的等待時(shí)間,這就是我們?cè)O(shè)定的讓子進(jìn)程睡眠的時(shí)間, 只有子進(jìn)程從睡眠中蘇醒過來,它才能正常退出, 也就才能被父進(jìn)程捕捉到。 其實(shí)這里我們不管設(shè)定子進(jìn)程睡眠的時(shí)間有多長(zhǎng),父進(jìn)程都會(huì)一直等待下去,讀者如果有興趣的話,可以試著自
45、己修改一下這個(gè)數(shù)值,看看會(huì)出現(xiàn)怎樣的結(jié)果。參數(shù) status如果參數(shù) status 的值不是 null ,wait 就會(huì)把子進(jìn)程退出時(shí)的狀態(tài)取出并存入其中,這是一個(gè)整數(shù)值( int ),指出了子進(jìn)程是正常退出還是被非正常結(jié)束的(一個(gè)進(jìn)程也可以被其 他進(jìn)程用信號(hào)結(jié)束,我們將在以后的文章中介紹),以及正常結(jié)束時(shí)的返回值,或被哪一個(gè)信號(hào)結(jié)束的等信息。 由于這些信息被存放在一個(gè)整數(shù)的不同二進(jìn)制位中,所以用常規(guī)的方法讀取會(huì)非常麻煩,人們就設(shè)計(jì)了一套專門的宏(macro)來完成這項(xiàng)工作,下面我們來學(xué)習(xí)一下其中最常用的兩個(gè):僵尸進(jìn)程 3-3 wifexited(status)這個(gè)宏用來指出子進(jìn)程是否為正常退
46、出的,如果是,它會(huì)返回一個(gè)非零值。(請(qǐng)注意,雖然名字一樣,這里的參數(shù)status 并不同于 wait 唯一的參數(shù) - 指向整數(shù)的指針 status,而是那個(gè)指針?biāo)赶虻恼麛?shù),切記不要搞混了。) wexitstatus(status)當(dāng) wifexited返回非零值時(shí), 我們可以用這個(gè)宏來提取子進(jìn)程的返回值,如果子進(jìn)程調(diào) 用 exit(5) 退 出 , wexitstatus(status)就 會(huì) 返 回 5 ; 如 果 子 進(jìn) 程 調(diào) 用 exit(7) , wexitstatus(status)就會(huì)返回7 。請(qǐng)注意,如果進(jìn)程不是正常退出的,也就是說, wifexited返回 0,這個(gè)值就毫無
47、意義。下面通過例子來實(shí)戰(zhàn)一下我們剛剛學(xué)到的內(nèi)容:/*wait2.c */#include #include #includemain()int status; pid_t pc, pr; pc=fork();if(pc0)/*如果出錯(cuò) */printf(errorocurred!n); else if(pc=0)/*子進(jìn)程 */printf(this is child process with pid of %d.n, getpid();exit(3);/*子進(jìn)程返回 3 */else/*父 進(jìn) 程 */ pr=wait(&status);if(wifexited(status)/*如果 wi
48、fexited返回非零值 */ printf(the child process %d exit normally.n, pr);printf(thereturn code is %d.n, wexitstatus(status);else/*如果 wifexited返回零 */printf(the child process %d exit abnormally.n, pr);編譯并運(yùn)行 :$ cc wait2.c -o wait2$ ./wait2this is child process with pid of 1538. the child process 1538 exit norm
49、ally.thereturn code is 3.父進(jìn)程準(zhǔn)確捕捉到了子進(jìn)程的返回值3,并把它打印了出來。當(dāng)然, 處理進(jìn)程退出狀態(tài)的宏并不止這兩個(gè),但它們當(dāng)中的絕大部分在平時(shí)的編程中很少用到,就也不在這里浪費(fèi)篇幅介紹了,有興趣的讀者可以自己參閱linux man pages 去了解它們的用法。進(jìn)程同步有時(shí)候, 父進(jìn)程要求子進(jìn)程的運(yùn)算結(jié)果進(jìn)行下一步的運(yùn)算,或者子進(jìn)程的功能是為父進(jìn)程提供了下一步執(zhí)行的先決條件(如:子進(jìn)程建立文件,而父進(jìn)程寫入數(shù)據(jù)),此時(shí)父進(jìn)程就必須在某一個(gè)位置停下來,等待子進(jìn)程運(yùn)行結(jié)束,而如果父進(jìn)程不等待而直接執(zhí)行下去的 話,可以想見,會(huì)出現(xiàn)極大的混亂。這種情況稱為進(jìn)程之間的同步,
50、更準(zhǔn)確地說,這是進(jìn)程同步的一種特例。進(jìn)程同步就是要協(xié)調(diào)好2 個(gè)以上的進(jìn)程,使之以安排好地次序依次執(zhí)行。解決進(jìn)程同步問題有更通用的方法,我們將在以后介紹, 但對(duì)于我們假設(shè)的這種情況,則完全可以用 wait 系統(tǒng)調(diào)用簡(jiǎn)單的予以解決。請(qǐng)看下面這段程序:#include #includemain()pid_t pc, pr; int status; pc=fork(); if(pc0)printf(erroroccured on forking.n); else if(pc=0)/*子進(jìn)程的工作 */exit(0);else/*父進(jìn)程的工作 */ pr=wait(&status);/*利用子進(jìn)程的結(jié)果
51、*/這段程序只是個(gè)例子,不能真正拿來執(zhí)行,但它卻說明了一些問題,首先,當(dāng)fork調(diào)用成功后, 父子進(jìn)程各做各的事情, 但當(dāng)父進(jìn)程的工作告一段落, 需要用到子進(jìn)程的結(jié)果時(shí), 它就停下來調(diào)用 wait ,一直等到子進(jìn)程運(yùn)行結(jié)束,然后利用子進(jìn)程的結(jié)果繼續(xù)執(zhí)行,這樣就圓滿地解決了我們提出的進(jìn)程同步問題。waitpidwaitpid 系統(tǒng)調(diào)用在 linux 函數(shù)庫中的原型是:#include /*提供類型 pid_t 的定義 */ #includepid_t waitpid(pid_t pid, int *status, int options)從本質(zhì)上講,系統(tǒng)調(diào)用 waitpid 和 wait 的作用
52、是完全相同的,但 waitpid 多出了兩個(gè)可由用戶控制的參數(shù) pid 和 options ,從而為我們編程提供了另一種更靈活的方式。 下面我們就來詳細(xì)介紹一下這兩個(gè)參數(shù): pid從參數(shù)的名字 pid 和類型 pid_t 中就可以看出,這里需要的是一個(gè)進(jìn)程 id 。但當(dāng) pid 取不同的值時(shí),在這里有不同的意義。僵尸進(jìn)程 3-4pid0 時(shí),只等待進(jìn)程 id 等于 pid 的子進(jìn)程, 不管其它已經(jīng)有多少子進(jìn)程運(yùn)行結(jié)束退出了,只要指定的子進(jìn)程還沒有結(jié)束,waitpid 就會(huì)一直等下去。pid=-1 時(shí),等待任何一個(gè)子進(jìn)程退出,沒有任何限制,此時(shí) waitpid 和 wait 的作用一模一樣。pid=0時(shí),等待同一個(gè)進(jìn)程組中的任何子進(jìn)程,如果子進(jìn)程已經(jīng)加入了別的進(jìn)程組,waitpid 不會(huì)對(duì)它做任何理睬。pid-1 時(shí),等待一個(gè)指定進(jìn)程組中的任何子進(jìn)程,這個(gè)進(jìn)程組的id 等于 pid 的絕對(duì)值。 optionsoptions提供了一些額外的選項(xiàng)來控制waitpid ,目前在 linux 中只支持 wnohang和wuntraced兩個(gè)選項(xiàng),這是
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 谷雨變遷與氣候沖擊
- 2025至2030年中國(guó)間接氧化鋅市場(chǎng)現(xiàn)狀分析及前景預(yù)測(cè)報(bào)告
- 2025至2030年中國(guó)混凝土地面用水泥基耐磨材料市場(chǎng)現(xiàn)狀分析及前景預(yù)測(cè)報(bào)告
- 2025至2030年中國(guó)楓木枋數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 2025至2030年中國(guó)發(fā)條式洗滌定時(shí)器市場(chǎng)調(diào)查研究報(bào)告
- 2025━2030年經(jīng)營(yíng)管材行業(yè)深度研究報(bào)告
- 2025━2030年中國(guó)凝膠墊項(xiàng)目投資可行性研究報(bào)告
- 上海市交通大學(xué)附屬中學(xué)2024-2025學(xué)年高一下學(xué)期開學(xué)摸底考試數(shù)學(xué)試題(原卷版+解析版)
- 電工技術(shù)基礎(chǔ)(第5版)(微課版)(AR H5交互版)教學(xué)計(jì)劃
- 2025年專用級(jí)次磷酸鈉項(xiàng)目發(fā)展計(jì)劃
- 煤礦監(jiān)測(cè)監(jiān)控培訓(xùn)
- 柔性電路板自動(dòng)化制造-深度研究
- 2024年河南建筑職業(yè)技術(shù)學(xué)院高職單招職業(yè)技能測(cè)驗(yàn)歷年參考題庫(頻考版)含答案解析
- 電纜故障知識(shí)培訓(xùn)課件
- 國(guó)家開放大學(xué)本科《商務(wù)英語4》一平臺(tái)機(jī)考真題及答案(第四套)
- 交通運(yùn)輸考試題及答案
- 氣體滅火鋼瓶標(biāo)準(zhǔn)檢測(cè)流程
- 2025年上半年青島平度市人民檢察院招考編外書記員易考易錯(cuò)模擬試題(共500題)試卷后附參考答案-1
- 【??途W(wǎng)】2024秋季校園招聘白皮書
- 老年上消化道出血急診診療專家共識(shí)2024解讀
- 《電工技術(shù)》課件-戴維南定理
評(píng)論
0/150
提交評(píng)論