




版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、寫(xiě)在前面的話(huà)。這是去年夏天剛來(lái)SOHU的時(shí)候?qū)懙摹T诒景孀畛鹾孟癜l(fā)過(guò)一遍?,F(xiàn)在整理個(gè)人文集,重發(fā)一遍。大家見(jiàn)諒。說(shuō)明:本文所有程序,應(yīng)在UNIX類(lèi)操作系統(tǒng)下編譯執(zhí)行。第一章:生成一個(gè)Process(進(jìn)程)進(jìn)程是什么?簡(jiǎn)單地說(shuō),進(jìn)程就是在執(zhí)行狀態(tài)下的一個(gè)程序(包括CPU狀態(tài),所占內(nèi)存的狀態(tài),等等)A進(jìn)程生成了B進(jìn)程,也就是說(shuō),A程序在執(zhí)行的時(shí)候,又生成了另一個(gè)進(jìn)程B。這個(gè)時(shí)候,我們可以把A進(jìn)程叫做父進(jìn)程,把B進(jìn)程叫做子進(jìn)程。例程序:/ Usage : ./a.out 20#includeint main( int argc , char *argv)int dep_time;dep_time =
2、 atoi( argv1 )*60 ; /將參數(shù)中給出的20(分鐘)轉(zhuǎn)換成整型的秒數(shù)if( fork()=0 ) /生成子進(jìn)程,然后父進(jìn)程中止sleep( dep_time );fprintf( stderr , !n);return 0;上面的程序是一個(gè)鬧鐘程序。當(dāng)你執(zhí)行之后。程序不會(huì)顯示什么,而是一下就回到UNIX的提示符下。但是你在命令行中指定了20分鐘后你有事,那么在你執(zhí)行這個(gè)程序之后20分鐘,他會(huì)提醒你到時(shí)間了。本程序只是做示例用,沒(méi)有檢查參數(shù)是否正確,等等。生成一個(gè)新的進(jìn)程,可以使用 fork() 函數(shù) 。以下說(shuō)說(shuō)fork()函數(shù)。頭文件: #include 形式 pid_t fo
3、rk();參數(shù) 無(wú)返回值 成功時(shí): 父進(jìn)程中:子進(jìn)程的PID (Process ID)子進(jìn)程中:0失敗時(shí): 父進(jìn)程中:-1由于失敗,沒(méi)有生成子進(jìn)程;fork()剛執(zhí)行完的時(shí)候,子進(jìn)程和父進(jìn)程是完全一模一樣的兩份進(jìn)程(當(dāng)然,PID是不一樣的)。他們的各個(gè)變量的值都是一樣的,而且都認(rèn)為自己已經(jīng)執(zhí)行完fork()了。fork()后,區(qū)分父進(jìn)程和子進(jìn)程,只要看fork()的返回值就行了。if( fork()=0 ) printf(這是子進(jìn)程);else printf(這是父進(jìn)程);同理:if( fork()=0 )/接下來(lái)要子進(jìn)程做的工作else/接下來(lái)要父進(jìn)程做的工作一般,我們會(huì)把fork()返回給
4、父進(jìn)程的值保存下來(lái)(其實(shí)就是子進(jìn)程的PID),等到需要結(jié)束子進(jìn)程的時(shí)候,我們關(guān)掉他,如下:pid_t child_pid ;child_pid=fork();if( child_pid=0 )/ . .else/ . ./ . .需要結(jié)束子進(jìn)程的時(shí)候kill( child_pid , SIGKILL ) / kill()函數(shù)是用來(lái)發(fā)給另一個(gè)進(jìn)程一個(gè)消息的。以后再講。先寫(xiě)這些,試試手。喜歡就頂。要是沒(méi)人愛(ài)看我就不寫(xiě)了。呵呵。省得大家說(shuō)我亂貼垃圾。以后計(jì)劃貼的東西:在程序中執(zhí)行UNIX命令或者另一個(gè)程序取得環(huán)境變量并利用UNIX文件系統(tǒng)(在程序中取得分區(qū)信息,等等)使用管道操作達(dá)到在各進(jìn)程互相交流
5、數(shù)據(jù)信號(hào)(signal)進(jìn)程間共享內(nèi)存用message實(shí)現(xiàn)進(jìn)程間共享信息本文所有程序均應(yīng)在UNIX系操作系統(tǒng)下編譯執(zhí)行。第二章:在程序中執(zhí)行UNIX命令或者其它程序在UNIX下,像DOS的那樣的程序,我們稱(chēng)之為外殼(shell)。外殼就是一個(gè)命令解釋器,你在外殼的提示符下輸入命令(如同DOS的提示符一樣),系統(tǒng)便會(huì)執(zhí)行。DOS的提示符一般是C:,當(dāng)然,你想改成什么樣就能改成什么樣,又當(dāng)然,像BBS一樣貼張圖上去是不太現(xiàn)實(shí)的。UNIX的提示符根據(jù)外殼的不同是不同的。為了更好地說(shuō)明本章想講解的內(nèi)容,我們先做一個(gè)外殼試試(玩具級(jí)別的)。我們給他起名叫SSH(Sohu Shell
6、)吧。想取名叫CSH,可惜CSH在沒(méi)生我之前就有了。呵呵。/* 簡(jiǎn)單的外殼程序 */#includeint main()static char prompt64= ;char command256;int st;fprintf(stderr,%s,prompt); / 在屏幕上輸出提示符while(gets(command)!=NULL) / 取得命令if(fork()=0) / 生成子進(jìn)程 / 這里是子進(jìn)程接下來(lái)要做的事if( execl(command,command,(char *)0)=(-1) )/ 上一句是執(zhí)行命令 exit(1); / 當(dāng)出錯(cuò)時(shí)子進(jìn)程異常中止else / 父進(jìn)程w
7、ait(&st); / 等待子進(jìn)程結(jié)束fprintf(stderr,%s,prompt);/ 輸出提示符,等待命令return 0;執(zhí)行方法:%./ssh/bin/ls當(dāng)前目錄下文件名一覽Ctrl+D%普通的外殼在執(zhí)行exit命令后會(huì)關(guān)閉。也就是說(shuō),退出一層外殼。咱們這個(gè)程序現(xiàn)在還做不到。愿意的話(huà)加上這個(gè)功能試試好了。所以要關(guān)閉這個(gè)外殼就得來(lái)點(diǎn)狠的。Ctrl+D,Ctrl+C什么的。再不你就再開(kāi)一個(gè)外殼然后ps -ef再kill。再狠一些拆硬盤(pán),撥電源我們這里有了一個(gè)新的函數(shù):execl()。其實(shí)他是一組函數(shù)中的一個(gè)。這組函數(shù)如下:int execl( path , arg0 , arg1 ,
8、 . , argn , (char *)0 );int execv( path , argv );int execle( path , arg0 , arg1 , . , argn , (char *)0 , envp );int execve( path , argv , envp );int execlp( file , arg0 , arg1 , . , argn , (char *)0 );int execvp( file , argv );其中的參數(shù)定義如下:char *path;char *file;char *arg0 , *arg1 , . , *argn;char *argv
9、;char *envp;返回值: 成功時(shí):所執(zhí)行的命令將會(huì)覆蓋現(xiàn)有的進(jìn)程,所以無(wú)返回值失敗時(shí):-1用過(guò)TC的朋友應(yīng)該知道,TC的Process.h里有一個(gè)system()函數(shù)。這組函數(shù)其實(shí)和system()的功能差不多。比方說(shuō):execl( /bin/ls , /bin/ls , -al , /home , (char *)0 );或者char *argv;strcpy( argv0 , /bin/ls );strcpy( argv1 , -al );strcop( argv2 , /home );execv( /bin/ls , argv );都相當(dāng)于在命令行下敲入了/bin/ls -al
10、/home并且回車(chē)。(引號(hào)不是命令里的。是我解釋時(shí)加上去的。別看混了)。execle()和execve(),函數(shù)名最后一個(gè)字母都是e。就是說(shuō),這兩個(gè)函數(shù)在調(diào)用其它程序的同時(shí),還可以把環(huán)境變量一起傳給被調(diào)程序execlp()和execvp(),函數(shù)名最后一個(gè)字母都是p,就是說(shuō),這兩個(gè)函數(shù)在使用的時(shí)候,就算你不指定命令文件所在的路徑,它也會(huì)根據(jù)環(huán)境變量PATH去挨個(gè)地方找。找著就執(zhí)行。找不著拉倒。比方說(shuō):setenv $path = ( /bin $path ) 這句話(huà)將環(huán)境變量PATH的第一個(gè)路徑設(shè)為/bin。這是在SHELL下執(zhí)行的。C里沒(méi)這東西吧。在程序中這樣用這個(gè)函數(shù)execlp( ls
11、, ls , -al , /home , (char *)0 );與上面的效果一樣。當(dāng)然。如果你PATH變量沒(méi)設(shè)好的話(huà)。它就不一定找到哪兒去了。還有一個(gè)函數(shù)是wait(),說(shuō)明如下:#include pid_t wait(int *stat_loc);返回值就是一個(gè)PID了。忘了PID是什么意思的朋友光顧一下我上一篇貼子。它的參數(shù)有些意思。其實(shí)它與你的子進(jìn)程用什么方式結(jié)束有關(guān)系。當(dāng)你的子進(jìn)程以exit()方式結(jié)束的話(huà),stat_loc所指向的地址的前8位將會(huì)是exit()的參數(shù)的后8位,而stat_loc所指向的地址的后8位是0。比方說(shuō):你的子進(jìn)程是exit(1);那stat_loc所指向的地
12、址的內(nèi)容應(yīng)該是0000 0001 0000 0000。exit():#include void exit(int status);就算你在程序中沒(méi)寫(xiě)exit()函數(shù),編譯器也是認(rèn)為你是在最后加入了這個(gè)函數(shù)。下一篇貼子,咱們?cè)侔堰@個(gè)Shell完善一下本文所有程序均應(yīng)在UNIX系操作系統(tǒng)下編譯執(zhí)行。第三章:增強(qiáng)ssh的功能(使用環(huán)境變量)還記得上次做的那個(gè)ssh吧?這回咱們把它再改一改。大家知道。C語(yǔ)言的main函數(shù)是這樣的:int main( int argc , char *argv , char *envp );前兩個(gè)不用說(shuō)了吧。其實(shí)知道前兩個(gè)的話(huà)應(yīng)該也知道第三個(gè):取得系統(tǒng)環(huán)境變量。UNIX
13、和DOS一樣。有著各種各樣的環(huán)境變量(明確地說(shuō),應(yīng)該是比DOS用得更廣泛)。比方說(shuō)常用的$PATH,$HOME,$USER等等。如果用的是csh,可以改 /.cshrc或者干脆直接在命令行下設(shè)就行了。這些是UNIX的東西,不屬于我們?cè)贑/C+討論的東西了。有興趣的朋友可以到UNIX版去看一看。下面是一個(gè)取得系統(tǒng)環(huán)境變量并輸出的小程序/* getenv.c 取得系統(tǒng)環(huán)境變量并輸出 */#includeint main ( int argc , char *argv , char *envp )int i;for( i=0 ; envpi!=NULL ; i+ )printf( %sn , env
14、pi );return 0;編譯執(zhí)行后應(yīng)該是這樣:%./getenvDISPLAY=:0.0HOME=/home/syuuiUSER=syuui以及你的系統(tǒng)中其它的環(huán)境變量。想一想它是怎么運(yùn)行的:當(dāng)你在命令行下敲入getenv并回車(chē),shell就fork出一個(gè)新的進(jìn)程,然后讓這個(gè)進(jìn)程去執(zhí)行g(shù)etenv,并把現(xiàn)在系統(tǒng)中的各個(gè)環(huán)境變量存入到envp里去。這個(gè)時(shí)候,原來(lái)shell的進(jìn)程就是getenv進(jìn)程的父進(jìn)程。envp的參數(shù)是從父進(jìn)程中取得的?,F(xiàn)在你知道上一節(jié)中為什么有那兩個(gè)帶p的函數(shù)了?上一回做的ssh這個(gè)外殼的命令是帶不了參數(shù)的。因?yàn)樵蹅兊某绦虿恢酪プx參數(shù)。這回不妨做一個(gè)能讀參數(shù)的試試#
15、include #define SP 0#define NOSP 1void getarg( char *argv , char *p ); /取得各個(gè)參數(shù)int main()static char prompt64= ;char command256, *argv256, *p;int st;fprintf( stderr , %s , prompt );while( (p=gets(command)!=NULL )getarg( argv , p );if( fork()=0 )if( execv(argv0,argv)=(-1) )exit(1);elsewait( &st );fpri
16、ntf( stderr , %s , prompt);return 0;void getarg( char *argv , char *p )int i , sp_flag ;sp_flag=SP; /SP代表空格,NOSP代表非空格的意思for( i=0 ; *p!=0 ; p+ )if( sp_flag=SP & *p!= )/如果現(xiàn)在狀態(tài)是讀過(guò)空格,但現(xiàn)在這個(gè)字母是非空格/那很顯然,讀到一個(gè)新的參數(shù)argvi=p;i+;sp_flag=NOSP;if( *p= )*p=0;sp_flag=SP;argvi=(char *)0;這篇文章東西說(shuō)得比較少。給大家出個(gè)問(wèn)題吧:看到了吧。C能做的事
17、情很多。誰(shuí)說(shuō)C不支持多進(jìn)程?那是因?yàn)镈OS。呵呵上回做的ssh,必須輸入絕對(duì)路徑才能執(zhí)行?,F(xiàn)在要求,咱們做一個(gè)只輸入命令以及參數(shù),程序自動(dòng)在$PATH里定義的各路徑查找這個(gè)命令。找到就執(zhí)行,找不到就報(bào)錯(cuò)的外殼出來(lái)試試?有做出來(lái)的,請(qǐng)貼程序。2003-7-27 關(guān)于進(jìn)程狀態(tài)的一些補(bǔ)足:眾所周知,UNIX是一個(gè)多用戶(hù)多任務(wù)的操作系統(tǒng)。所謂多任務(wù),就是指在同一個(gè)時(shí)間內(nèi),看上去有許多任務(wù)在同時(shí)執(zhí)行。這一點(diǎn)是與DOS不同的。所以Turbo C下找不到fork()函數(shù)。當(dāng)然。大家知道,實(shí)際上CPU同一個(gè)時(shí)間只能處理一件事。它是在輪流執(zhí)行這些進(jìn)程,由于速度很快。所以看起來(lái)像是所有進(jìn)程在一起跑。同樣,一個(gè)進(jìn)程
18、,它有使用CPU的時(shí)候,也有等待使用CPU的時(shí)候。這就決定了進(jìn)程的幾種狀態(tài)。進(jìn)程大致可以分為“執(zhí)行中”,“準(zhǔn)備執(zhí)行”,“睡眠中”三種狀態(tài)。執(zhí)行中:進(jìn)程在占用CPU。準(zhǔn)備執(zhí)行:想要使用CPU,但是CPU被別的進(jìn)程占用中,所以等待CPU空閑。睡眠中:等待事件發(fā)生的進(jìn)程。比方說(shuō),等待輸入輸出結(jié)束的進(jìn)程。我們用fork()生成一個(gè)新的進(jìn)程,或者用exec函數(shù)組覆蓋當(dāng)前進(jìn)程后。當(dāng)這個(gè)子進(jìn)程結(jié)束的時(shí)候要給父進(jìn)程送去一個(gè)信號(hào)(signal,后述),然后轉(zhuǎn)為zombie狀態(tài)。zombie狀態(tài)是指一個(gè)進(jìn)程已經(jīng)結(jié)束,所占內(nèi)存空間等等已經(jīng)返還給系統(tǒng),但是在系統(tǒng)的進(jìn)程列表中仍然存在的這么一個(gè)狀態(tài)。當(dāng)父進(jìn)程執(zhí)行wait
19、()后,子進(jìn)程才真正完全結(jié)束。如果父進(jìn)程先一步結(jié)束了,那么由init代替父進(jìn)程。所以,wait()不只是等待子進(jìn)程結(jié)束。它還有上面所說(shuō)的這個(gè)任務(wù)。我們寫(xiě)個(gè)程序來(lái)看一下:/* zombie */#includeint main()int st;if( fork()=0 )exit(1); /子進(jìn)程生成后直接結(jié)束elsesleep( 300 ); /休眠300秒wait( &st );printf(Return code=%dn,i);return 0;編譯后執(zhí)行%./zombie &這個(gè)時(shí)候,程序在后臺(tái)運(yùn)行%ps -ef | grep zombie看一下,是不是有一個(gè)進(jìn)程處在zombie狀態(tài)?本
20、文所有程序均應(yīng)在UNIX系操作系統(tǒng)下編譯執(zhí)行。第四章:文件系統(tǒng)UNIX所管理的機(jī)器一般是大型機(jī)而不是PC。所管理的硬盤(pán)一般也非常大。所以一般分成幾個(gè)區(qū),每個(gè)區(qū)都有其單獨(dú)的文件系統(tǒng)。比方說(shuō)你能大概能找到這樣的一些文件/dev/sd/c0t0d0s0/dev/sd/c0t0d0s1. .當(dāng)UNIX啟動(dòng)的時(shí)候,分區(qū)被掛裝(mount)并統(tǒng)一成一個(gè)樹(shù)狀的文件系統(tǒng)分區(qū)的物理構(gòu)造咱們暫且放在一邊,先寫(xiě)個(gè)程序,讀一下分區(qū)信息試試。/* ndf.c 計(jì)算參數(shù)所指定的分區(qū)的剩余空間比率 */#include #include #include int main(int argc , char *argv)str
21、uct statvfs buf1;sync();if( statvfs(argv1,buf)!=0 )fprintf(stderr , Cannot read super block !n);exit(1);fprintf(stderr , %4.1f % freen,(float)buf0.f_bfree / buf0.f_blocks*100 );return 0;編譯執(zhí)行:%./ndf /49.8 % free這里用了一個(gè)statvfs函數(shù)。其具體如下:#include#includeint statvfs( char *path , struct statvfs *buf );返回值:
22、 成功時(shí): 0失敗時(shí): -1還有一個(gè)sync()函數(shù)。用來(lái)更新分區(qū)的super block;void sync();UNIX系統(tǒng)為了加快處理速度,將分區(qū)的super block信息讀到內(nèi)存中保存。sync()函數(shù)就是把在內(nèi)存中保存的super block信息再寫(xiě)回到硬盤(pán)上去。UNIX系統(tǒng)使用好幾種文件系統(tǒng)。有S5,ufs,VxFS等等。雖然這些文件系統(tǒng)的構(gòu)造非常不同,但一通百通,咱們?cè)谶@幾篇貼子里只討論一下比較容易理解,而且“經(jīng)典的”S5文件系統(tǒng)。(別的我也不會(huì)。呵呵)S5: 文件名最長(zhǎng)14字節(jié),構(gòu)造簡(jiǎn)單ufs: 文件名最長(zhǎng)255字節(jié),BSD所用的文件系統(tǒng)。VxFS: Veritas Soft
23、wave公司開(kāi)發(fā)的文件系統(tǒng)。出現(xiàn)錯(cuò)誤時(shí)可以快速恢復(fù)。由于這種文件系統(tǒng)保證文件在硬盤(pán)上連續(xù)存放,所以處理速度很快?,F(xiàn)在說(shuō)一下S5分區(qū)的構(gòu)造一個(gè)分區(qū)包含如下四部分(按順序): boot block super block i node block data block boot block :這個(gè)部分在分區(qū)的最開(kāi)始處,用來(lái)存放引導(dǎo)程序。就算是不能引導(dǎo)的分區(qū)一樣有boot block,這個(gè)時(shí)候這部分就沒(méi)有用了。不過(guò)一般這部分也不大。大多數(shù)只有512或者1024字節(jié)。super block :super block在boot block之后,用來(lái)存放這個(gè)分區(qū)全體的管理信息。上面那個(gè)ndf.c就是讀的這
24、部分所存儲(chǔ)的信息。里邊存放了i node block的大小,free block數(shù)組等等。根據(jù)這些信息可以得知data block的開(kāi)始位置。i node block :i node是index node的縮寫(xiě)。i node block就是存放i node的部分UNIX把一切都看成是個(gè)文件。包括目錄以及設(shè)備等等的所有的文件都有一個(gè)i node號(hào),作為這個(gè)文件的管理信息。文件本身存在于數(shù)據(jù)區(qū),但是i node號(hào)存在i node block里。主要包含文件的模式,鏈接數(shù),文件所有者,文件大小,在硬盤(pán)上的位置,最后讀寫(xiě)時(shí)間,最后更新時(shí)間等信息。為了加快存儲(chǔ)速度,系統(tǒng)會(huì)把一定數(shù)量的i node存至內(nèi)存
25、。UNIX系統(tǒng)不一樣,存多少也就不一樣。data block :這部分就是存放數(shù)據(jù)本身的了。這部分被分成一定大小的塊,如同DOS的扇區(qū)一樣。一般大小是1024字節(jié),分到4096的也有。解說(shuō)到這里,我們?cè)賮?lái)寫(xiě)個(gè)程序。打開(kāi)一個(gè)目錄,然后把這個(gè)目錄下所有的文件的i node號(hào)及文件名輸出來(lái)。/* nls.c */#include #define DIRSIZ 14int main( int argc , char *argv )struct dirint i_no;char f_name;struct dir dir_data1;FILE *fp;fp=fopen(argv1,r);while( f
26、scanf(fp,%i%s,&(dir_data0.i_no),dir_data0.f_name)!=EOF )printf(%i %sn , dir_data0.i_no , dir_data0.f_name );fclose(fp);return 0;%./nls /. .2048 usr2049 home. .別忘了,在UNIX下,目錄也當(dāng)成文件。最近,為了使目錄的格式變得通用而不再依賴(lài)于操作系統(tǒng),程序中大多使用統(tǒng)一的格式。這種情況下,我們最好就不直接用fopen()打開(kāi)目錄,而使用opendir(),readdir()等函數(shù)比較好。重寫(xiě)一下上面的程序。/* nls2.c */#incl
27、ude #include int main( int argc , char *argv )DIR *fp;struct dirent *p;fp=opendir(argv1);while( (p=readdir(fp)!=NULL )printf(%i %sn, p-d_ino , p-d_name );closedir(fp);return 0;執(zhí)行結(jié)果和上面一樣。函數(shù)說(shuō)明如下:#include DIR *opendir( char *dirname ); 打開(kāi)一個(gè)目錄,成功時(shí)返回DIR結(jié)構(gòu)體的地址,失敗返回NULLstruct dirent *readdir( DIR *dirp );
28、取得打開(kāi)目錄中下一個(gè)文件的信息,成功時(shí)返回下一個(gè)文件信息的地址,失敗時(shí),或者讀至末尾時(shí)返回NULLint closeidr( DIR *dirp ); 關(guān)閉目錄,成功時(shí)返回0,失敗時(shí)返回-1注意:readdir在成功地讀出一項(xiàng)之后,會(huì)自動(dòng)指向下一項(xiàng)。假設(shè)你有如下程序:struct dirent *p;p=readdir(fp);p+; /千萬(wàn)不要像這行這樣寫(xiě)。你無(wú)法保證這目錄里的文件信息是連續(xù)存放的。你只要一遍一遍地用readir()這個(gè)函數(shù)就行了。它幫你全搞定本文所有程序均應(yīng)在UNIX系操作系統(tǒng)下編譯執(zhí)行。第五章:目錄及文件操作上一章我們說(shuō)了一些UNIX的文件系統(tǒng)的物理構(gòu)造。下面我們來(lái)看看具
29、體怎么對(duì)文件進(jìn)行操作。當(dāng)然這里所說(shuō)的文件及目錄操作不是fopen()。呵呵。我們要做一些fopen辦不到的事。/* newer.c 比較參數(shù)所指定的兩個(gè)文件將其中新的文件的文件名輸出來(lái) */#include #include #include int main( int argc , char *argvp )struct stat buf2 , *p ;if( argc!=3 )fprintf( stderr , Usage : %s file1 file2n , argv0 );exit(1);p=buf;if( stat(argv1,p)!=0 ) /取得第一個(gè)文件的信息fprintf(
30、 stderr , %s not found !n , argv1 );exit(1);p+;if( stat(argv2,p)!=0 ) /取得第二個(gè)文件的信息fprintf( stderr , %s not found !n , argv2 );exit(1);if( buf0.st_mtime buf1.st_mtime ) /比較最終更新時(shí)間printf( %sn , argv1 );elseprintf( %sn , argv2 );return 0;執(zhí)行結(jié)果:%newer afile bfilebfile使用stat()函數(shù),可以得到文件的信息。這些信息是在i node中保存的這個(gè)
31、文件信息的一部分。得到的信息將會(huì)保存在stat.h中定義的stat型的結(jié)構(gòu)體中。stat()函數(shù)解釋如下:#include #include int stat( char *path , struct stat *buf );返回值: 成功時(shí):0失敗時(shí):-1我們?cè)賮?lái)寫(xiě)一個(gè)玩玩 #include #include #include #define MASK 0555 /這個(gè)數(shù)字的意思等一下解釋?zhuān)怼翱勺x”和“可執(zhí)行”int main( int argc , char *argv )struct stat buf1;mode_t mode;if( argc!=2 )fprintf( stder
32、r , Usage : %s filen , argv0 );exit(1);if( stat(argv1,buf)!=0 )fprintf( stderr , Cannot read i-noden );exit(1);mode = ( buf0.st_mode & MASK ); /計(jì)算文件新的權(quán)限標(biāo)志if ( chmod(argv1,mode)!=0 ) /改變文件的權(quán)限標(biāo)志fprintf( stderr , Cannot change moden );exit(1);return 0;現(xiàn)在來(lái)解釋一下0555這個(gè)數(shù)字的意思。眾所周知,UNIX是一個(gè)多用戶(hù)多任務(wù)的操作系統(tǒng)。每個(gè)用戶(hù)有自己的
33、權(quán)限,這個(gè)權(quán)限限制了用戶(hù)可以做哪些事,不可以做哪些事。對(duì)于文件來(lái)說(shuō),用戶(hù)可以分成四類(lèi):root(根用戶(hù),超級(jí)用戶(hù))這個(gè)用戶(hù)是系統(tǒng)的管理吶,權(quán)限最大,可以隨意讀寫(xiě)任何文件。owner(文件擁有者)實(shí)際擁有文件的用戶(hù)。文件屬主。group(組成員)用戶(hù)所在的用戶(hù)組的成員other以上三類(lèi)之外的其它用戶(hù)UNIX中,每個(gè)文件信息中包括一組9位的權(quán)限標(biāo)志。分別給文件屬主,用戶(hù)組和其他用戶(hù)指定對(duì)該文件的讀、寫(xiě)和執(zhí)行權(quán)。請(qǐng)看下面的例子:%ls -l /bin/ls-rwxr-xr-x 1 root bin 27281 Aug 15 2002 /bin/ls*重要是看-rwxr-xr-x,第一個(gè) - 表示這是
34、一個(gè)普通文件,這個(gè)位置也可以出現(xiàn)些別的字符,比方說(shuō)目錄的話(huà)這里會(huì)是 d 。而 l 表示一個(gè)鏈接。余下的9位可以分成三段,每段三位。本例中從左至右rwx 表示文件的屬主擁有文件的讀,寫(xiě),執(zhí)行權(quán)r-x 表示同組的用戶(hù)擁有文件的讀,執(zhí)行權(quán)(注意 ,“寫(xiě)”權(quán)限的位置是一個(gè) - )r-x 表示其它的用戶(hù)擁有文件的讀,執(zhí)行權(quán)文件的訪(fǎng)問(wèn)權(quán)限還可以用三位8進(jìn)制來(lái)表示。如上例rwx r-x r-x 可以換成111 101 101 (有該權(quán)限,則該位為1,否則為0)換成8進(jìn)制, 二進(jìn)制的111是八進(jìn)制的7 , 二進(jìn)制的101是八進(jìn)制的5?,F(xiàn)在看看0555是什么意思?就是說(shuō),可以讀,可以寫(xiě)的意思。把0555和原來(lái)文
35、件的權(quán)限標(biāo)志做與運(yùn)算,得到的新的權(quán)限標(biāo)志就是把原來(lái)的文件權(quán)限標(biāo)志中所有的寫(xiě)權(quán)限全取消了。其余權(quán)限變。然后在程序中用chmod()把這個(gè)新的權(quán)限標(biāo)志賦給文件即可。chomd()函數(shù)用法如下:#include #include int chmod( char *path , mode_t mode );返回值: 成功時(shí):0失敗時(shí):-1關(guān)于目錄,還有另一個(gè)比較有用的函數(shù),即chdir()。用這個(gè)函數(shù)可以在程序中轉(zhuǎn)換當(dāng)前目錄。#include #include int chdir( char *path );返回值: 成功時(shí):0失敗時(shí):-1以上兩章,簡(jiǎn)單地?cái)⑹隽艘幌耈NIX的文件系統(tǒng)以及在UNIX C
36、中對(duì)文件的操作方法。并列舉了常用的一些函數(shù)。下一章,我們將簡(jiǎn)單地?cái)⑹鲆幌耈NIX C的輸入輸出,以及用管道(pipe)實(shí)現(xiàn)兩個(gè)進(jìn)程互換數(shù)據(jù)。本文所有程序均應(yīng)在UNIX系操作系統(tǒng)下編譯執(zhí)行。第六章:標(biāo)準(zhǔn)輸入輸出以及管道操作標(biāo)準(zhǔn)輸入輸出大概所有的操作系統(tǒng)都差不多吧。從鍵盤(pán)輸入。從屏幕輸出。除非你用的還是打紙帶的老家伙。呵呵。主要說(shuō)一下管道操作。注意:此處所說(shuō)的管道操作不是% cat -n test.c | more 這是在提示符狀態(tài)下使用管道,把第一個(gè)程序(cat)的輸出作為輸入傳給第二個(gè)程序(more)。我們現(xiàn)在要在程序中使用的管道原理與此相同。將某一個(gè)程序的輸出變?yōu)榱硪粋€(gè)程序的輸入。做一個(gè)石頭
37、剪子布的程序。其中包括一個(gè)父程序和一個(gè)子程序。注意是兩個(gè)程序,不是兩個(gè)函數(shù)。也不是兩個(gè)進(jìn)程。不過(guò)因?yàn)楦赋绦蜻\(yùn)行的時(shí)候要通過(guò)exec()函數(shù)來(lái)執(zhí)行子程序。所以我們也可以把它看成是兩個(gè)進(jìn)程。父進(jìn)程取得用戶(hù)的輸入(石頭S,剪子C,布P中的某一個(gè),P=0,S=1,C=2)并通過(guò)管道傳給子進(jìn)程子進(jìn)程取得父進(jìn)程傳來(lái)的數(shù)字,加上自己的PID后做為種子數(shù),生成一個(gè)隨機(jī)數(shù)然后被3除,得出來(lái)一個(gè)余數(shù)是0、1或者2。再通過(guò)管道傳回給父進(jìn)程。父進(jìn)程根據(jù)兩個(gè)數(shù)字(用戶(hù)輸入的,以及子進(jìn)程傳回來(lái)的)判定勝負(fù),輸出/* parent. */#include #include int main()int i , j ,st ,
38、 fd2 ;pid_t pid ;static int result33=1,0,2,2,1,0,0,1,2;char argv13 , argv23 ,ch ;ch=getchar();switch(ch)case P: i=0;break;case S: i=1;break;case C: i=2;break;default:fprintf( stderr , Enter P , S or C , Please!n );exit(1);if( pipe(fd)!=0 ) /建立管道fprintf( stderr , PIPE Error !n );exit(1);sprintf( argv
39、1 , %d , fd0 );sprintf( argv1 , %d , fd1 );switch( pid=fork() ) / fork出一個(gè)新的進(jìn)程,執(zhí)行子程序case 0:if( execl(child,child,argv1,argv2,(char *)0) = (-1) exit(1);/執(zhí)行了子程序break;case -1:fprintf( stderr , fork Error !n );exit(1)write( fd1, &i , sizeof(i) ); /向管道寫(xiě)數(shù)據(jù)wait( &st ); /等待子程序結(jié)束read( fd0 , &j , sizeof(j) );
40、/從管道讀數(shù)據(jù)switch(resultij)case 0:printf( You wonn );break;case 1:printf( Samen );break;case 2:printf( You lostn );close(fd0);close(fd1);return 0;/* child.c */#include int main(int argc , char *argv)int i,j,read_fd,write_fd;read_fd=atoi( argv1 ); /設(shè)定輸入用管道write_fd=atoi( argv2 ); /設(shè)定輸出用管道read( read_fd , &
41、i , sizeof(i) ); /從管道中取得數(shù)據(jù)srand( i+getpid() ); /設(shè)定隨機(jī)數(shù)的種子數(shù)j=rand()%3; /生成隨機(jī)數(shù)write( write_fd , &j , sizeof(j) ); /寫(xiě)向管道close( read_fd );close( write_fd );return 0;編譯執(zhí)行父程序即可。這種管道是用來(lái)在父子進(jìn)程間傳遞數(shù)據(jù)的。如同大家在程序中所見(jiàn):父進(jìn)程開(kāi)辟管道,然后生成子進(jìn)程執(zhí)行子程序,并將管道參數(shù)作為main()函數(shù)的參數(shù)傳給子程序。通過(guò)一個(gè)相同的管道實(shí)現(xiàn)讀寫(xiě)。開(kāi)辟管道時(shí),我們用到了這個(gè)函數(shù):int pipe( int fd2 ); 開(kāi)辟一
42、個(gè)管道參數(shù)fd2是一個(gè)有兩個(gè)元素的數(shù)組。可以看成是兩個(gè)管道的記述子。fd0用來(lái)讀,fd1用來(lái)寫(xiě)。返回值: 成功時(shí):0失敗時(shí):-1讀取/寫(xiě)入管道時(shí),我們用到了下面函數(shù)讀取管道中的數(shù)據(jù)int read( int fd , char *buf , unsigned int nbyte );向管道中寫(xiě)入數(shù)據(jù)int write( int fd , char *buf , unsigned int nbyte );其中,fd是管道記述子,也就是我們前面說(shuō)的fd0或者fd1,buf裝數(shù)據(jù),nbyte指定讀/寫(xiě)數(shù)據(jù)的數(shù)量,單位是字節(jié)。成功時(shí)返回0,失敗時(shí)返回-1。由于準(zhǔn)備考研。這篇文章耽誤了一些時(shí)日。最近還有
43、些事,也許下一篇也得幾天后才能再貼。另外,在此向諸位致歉。我的程序是在學(xué)校的UNIX下寫(xiě)的,一般是用軟盤(pán)帶回來(lái),寫(xiě)上一篇貼子程序的時(shí)候沒(méi)有帶軟盤(pán),只好打印出來(lái)回來(lái)再敲。在輸入的時(shí)候有一個(gè)錯(cuò)誤(現(xiàn)已改正)int main( int argc , char *argvp);應(yīng)為int main( int argc , char *argv);下一章準(zhǔn)備說(shuō)說(shuō)UNIX的進(jìn)程(Process)和信號(hào)(signal)。另外,感謝版主將我前幾篇貼子選進(jìn)了精華區(qū)。愚作不堪,如有錯(cuò)誤及不到之處請(qǐng)諸位高人指正為盼。對(duì)本貼內(nèi)容如有不明,請(qǐng)給我留言。我會(huì)盡快回答(如果我明白的話(huà))。謝謝遠(yuǎn)方的潔白的哈達(dá),霧中雪,初學(xué)無(wú)
44、罪,hellosam,xiaoyu等朋友。有你們的支持,我才有信心繼續(xù)寫(xiě)下去。愿拙文能對(duì)諸位學(xué)習(xí)C語(yǔ)言有所幫助。下章見(jiàn)。本文所有程序均應(yīng)在UNIX系操作系統(tǒng)下編譯執(zhí)行。第七章:UNIX進(jìn)程(Process)及信號(hào)(signal)在進(jìn)程執(zhí)行過(guò)程中,如果出現(xiàn)什么事件(event),系統(tǒng)將會(huì)給進(jìn)程一個(gè)信號(hào)。進(jìn)程可以在得到這個(gè)信號(hào)后做些適當(dāng)?shù)奶幚?。還是與以前一樣,咱們先來(lái)個(gè)小程序吧。/* slot.c */#include #include int x3;long kin;int kekka();int main( int argc , char *argv )srand( getpid() ); /
45、* 設(shè)定隨機(jī)數(shù)的種子數(shù) */signal( SIGKILL , kekka ); /* 當(dāng)Ctrl+C按下時(shí),執(zhí)行kekka函數(shù) */kin = atoi( argv1 ); /* 取得賭注數(shù)目 */printf(press Ctrl+C to stop maching !n);while(1)x0=rand()%10;x1=rand()%10;x2=rand()%10;printf(%d %d %dn,x0,x1,x2);fflush( stdout );return 0;int kekka() if( x0=x1 & x1=x2 ) printf(Great , you won %d n
46、, kin*3000);elseprintf(You lost %dn,kin);exit(0); /* 游戲結(jié)束。退出程序 */執(zhí)行方法:%./slot 100執(zhí)行后。畫(huà)面上會(huì)出現(xiàn)一排排的數(shù)字。當(dāng)你按下Ctrl+C的時(shí)候,程序得到信號(hào),跳轉(zhuǎn)至kekka()函數(shù)入,判斷隨機(jī)數(shù)。然后退出程序。這里有這樣一個(gè)新函數(shù)。是我們?cè)赨NIX程序設(shè)計(jì)中時(shí)常用得到的。#include int ( *signal( int sig , void (*func() )();返回值: 成功時(shí):進(jìn)程中斷地址錯(cuò)誤時(shí):SIG_ERR這個(gè)函數(shù)挺不好懂呵。解釋解釋。它的意思是,當(dāng)收到SIG這個(gè)信號(hào)時(shí),跳轉(zhuǎn)至func()這個(gè)函
47、數(shù)處繼續(xù)執(zhí)行。int sig這個(gè)參數(shù)其實(shí)好辦。下面我們會(huì)給出一個(gè)表。將其所有的參數(shù)一并列出。這是在頭文件中#define的,代表各種不同的信號(hào)。void ( * func() )這個(gè)參數(shù)是一個(gè)函數(shù)指針。意思是在SIG信號(hào)被傳給進(jìn)程后,所要執(zhí)行的部分。此外,func()這個(gè)參數(shù)還可以為如下值:SIG_DEL 系統(tǒng)的默認(rèn)動(dòng)作SIG_IGN 忽略這個(gè)信號(hào)。signal作用只有一次,當(dāng)一個(gè)信號(hào)發(fā)生,觸發(fā)signal動(dòng)作的之后,signal就不起作用了。比方說(shuō),在上例中,如果你把所有的exit(0)全去掉(不退出程序),程序會(huì)進(jìn)入無(wú)限循環(huán)中。你按一次Ctrl+C,signal執(zhí)行一次,顯示出結(jié)果。然后繼
48、續(xù)循環(huán),生成隨機(jī)數(shù),輸出。你再按Ctrl+C,signal就不好使了。如果需要重復(fù)使用signal()函數(shù),可以考慮在signal調(diào)用的動(dòng)作函數(shù)中再加一個(gè)signal,比方說(shuō)在上例的程序中再加一個(gè) signal( SIGKILL , kekka );加了signal這個(gè)函數(shù)后,整個(gè)程序如果沒(méi)收到信號(hào),則正常執(zhí)行。否則跳轉(zhuǎn)至signal中指定的func()函數(shù)處繼續(xù)。當(dāng)然,我們不可能,也沒(méi)有必要去為每一個(gè)信號(hào)指定一個(gè)動(dòng)作。當(dāng)未指定動(dòng)作的信號(hào)發(fā)生的時(shí)候,系統(tǒng)會(huì)執(zhí)行默認(rèn)的動(dòng)作。比方說(shuō)我們知道:當(dāng)Ctrl+C按下的時(shí)候,正常默認(rèn)的動(dòng)作是結(jié)束程序。但是,Ctrl+C按下時(shí),發(fā)生的信號(hào)是SIGKILL,如
49、果你為SIGKILL這個(gè)信號(hào)指定了一個(gè)動(dòng)作的話(huà),系統(tǒng)將去執(zhí)行你指定的這個(gè)動(dòng)作,而不管原來(lái)默認(rèn)的了。打個(gè)比方讓大家更容易懂一些:比方說(shuō):你在睡午覺(jué)。突然來(lái)了一個(gè)美女(帥哥)(Ctrl+C)讓你陪她/他去逛街購(gòu)物共進(jìn)晚餐(此處刪去若干字),你正常的默認(rèn)的動(dòng)作是馬上起來(lái)陪她出去玩(程序中止)。但是你媽說(shuō)不許去,你媽說(shuō)你得在家擦窗戶(hù),你只好留在家擦窗戶(hù)(signal()指定的動(dòng)作)。擦完窗戶(hù)之后你可以選擇陪美女去逛街(默認(rèn)動(dòng)作,Ctrl+C的埸合是退出程序,即上例程序中的exit(0)),可以選擇繼續(xù)睡覺(jué)(回到原來(lái)中斷的地方繼續(xù)執(zhí)行,上例程序中如果沒(méi)有exit(0),便會(huì)回到中斷處繼續(xù)執(zhí)行)。(大哥。
50、打個(gè)比方嘛。干嘛拿臭雞蛋砸我)signal種類(lèi)非常多,在此不一一列出。使用這個(gè)函數(shù),你可以防止用戶(hù)按下Ctrl+C結(jié)束程序。還可以做很多其它的事只有你想不到的,沒(méi)有C做不到的。注意:根據(jù)UNIX系統(tǒng)的不同,signal的定義是不一樣的。比方說(shuō),有的老式UNIX工作站上SIGINT是按下del鍵后發(fā)生的信號(hào),而有些機(jī)型上剛是按下Ctrl+Z發(fā)生的。在使用時(shí)要注意。再一次感謝SOHU C/C+論壇的朋友們的支持。我考上研究生了。下一次,準(zhǔn)備說(shuō)說(shuō)UNIX下C程序中,如何向別的進(jìn)程發(fā)送信號(hào)。本文所有程序均應(yīng)在UNIX系操作系統(tǒng)下編譯執(zhí)行。第八章:向其它進(jìn)程傳遞信號(hào)上一章,我們簡(jiǎn)單地說(shuō)了一下信號(hào)。信號(hào)不
51、僅僅可以在本進(jìn)程內(nèi)使用,還可以向其它的進(jìn)程傳遞。確切地說(shuō),一般情況下,一個(gè)進(jìn)程收到的信號(hào)絕大部分是從系統(tǒng)的INIT傳來(lái)的。而INIT也是一個(gè)進(jìn)程。用過(guò)UNIX或者LINUX的朋友大概都知道,UNIX系的操作系統(tǒng)里,有一個(gè)kill命令。如果你的一個(gè)進(jìn)程無(wú)法中止,可以使用kill命令強(qiáng)行干掉它。%kill mypro我們自己也可以做個(gè)簡(jiǎn)單的kill命令。/* nkill.c */#include #include #include int main( int argc , char *argv )pid_t pid;if( argc != 2 ) / 檢查命令行參數(shù)個(gè)數(shù)fprintf( stder
52、r , Usage : %s PID n , argv0 );exit(1);pid = atol( argv1 );if( kill( pid , SIGKILL )!=0 ) / 向pid這個(gè)進(jìn)程傳送一個(gè)SIGKILL信號(hào)fprintf( stderr , kill failedn );return 0;執(zhí)行例:%sleep 300 & (后臺(tái)睡眠300秒)1 520%nkill 5201 + Killed sleep 300你用自己寫(xiě)的程序殺死了一個(gè)sleep進(jìn)程。沒(méi)事。殺了就殺了吧。留著也是垃圾說(shuō)明一下,眾所周知,UNIX是非常注意用戶(hù)的權(quán)限的。所以,如果你向你自己生成的進(jìn)程發(fā)送SIG
53、KILL的話(huà),這個(gè)進(jìn)程會(huì)中止。如果你是向別的(你沒(méi)有寫(xiě)權(quán)限的)進(jìn)程發(fā)送SIGKILL的話(huà),除非那個(gè)進(jìn)程允許你動(dòng)它,否則你發(fā)了也是白發(fā)。打個(gè)不恰當(dāng)?shù)谋容^下流一些的比方:如果你要求親一下你自己老婆的話(huà)(你有“親”的權(quán)限),你會(huì)如愿以?xún)敗5侨绻阆胗H一下別人的老婆的話(huà)(你沒(méi)有“親”的權(quán)限),除非他及她同意,否則呵呵。你死定了。所以,執(zhí)行上面的程序的時(shí)候,你必須保證你有權(quán)限關(guān)掉那個(gè)進(jìn)程??刹皇钦f(shuō)你ps -ef然后隨便挑一個(gè)進(jìn)程就能關(guān)的。要不那黑客也太好當(dāng)了。幾乎是咱們的老規(guī)矩了:先寫(xiě)個(gè)程序,然后解釋這個(gè)程序,說(shuō)說(shuō)其中新出現(xiàn)的函數(shù)。這回還照舊吧。大家看到了。這里新出來(lái)一個(gè)kill()函數(shù)。解釋如下:#include #include int kill( pid_t pid , int sig );返回值: 成功時(shí):0失敗時(shí):-1這個(gè)函數(shù)是向進(jìn)程ID(PID)為pid的進(jìn)程傳送一個(gè)sig信號(hào)。這個(gè)函數(shù)有什么用呢?用處可大了。在上一章中,我們用到了SIGKILL信號(hào)。這一節(jié)我們用的還是這個(gè)信號(hào)。實(shí)際上,信號(hào)(sign
溫馨提示
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年中國(guó)米花巧克力行業(yè)市場(chǎng)發(fā)展前景及發(fā)展趨勢(shì)與投資戰(zhàn)略研究報(bào)告
- 2025年中國(guó)物料提升機(jī)市場(chǎng)競(jìng)爭(zhēng)態(tài)勢(shì)及行業(yè)投資潛力預(yù)測(cè)報(bào)告
- 2020-2025年中國(guó)縫制機(jī)械市場(chǎng)運(yùn)行態(tài)勢(shì)及行業(yè)發(fā)展前景預(yù)測(cè)報(bào)告
- 2025年中國(guó)云南省休閑農(nóng)業(yè)行業(yè)市場(chǎng)調(diào)研分析及投資前景預(yù)測(cè)報(bào)告
- 2025年新學(xué)期班主任團(tuán)隊(duì)協(xié)作工作計(jì)劃
- 2023-2029年中國(guó)特級(jí)面粉市場(chǎng)競(jìng)爭(zhēng)格局及投資前景展望報(bào)告
- 中國(guó)物流設(shè)備行業(yè)市場(chǎng)調(diào)查研究及投資戰(zhàn)略研究報(bào)告
- 句容壁紙項(xiàng)目可行性研究報(bào)告
- 護(hù)理部新生兒護(hù)理專(zhuān)項(xiàng)計(jì)劃
- 2024年中國(guó)灰硅沙行業(yè)調(diào)查報(bào)告
- 施工現(xiàn)場(chǎng)信息化管理方案
- 2023-2024年6月廣東省普通高中學(xué)業(yè)水平考試化學(xué)試題及答案
- DB11∕512-2017 建筑裝飾工程石材應(yīng)用技術(shù)規(guī)程
- TSG ZF001-2006《安全閥安全技術(shù)監(jiān)察規(guī)程》
- 滬科版(2024新版)八年級(jí)全冊(cè)物理第一學(xué)期期末學(xué)情評(píng)估測(cè)試卷(含答案)
- 高中數(shù)學(xué)課堂情景引入經(jīng)典案例
- 招標(biāo)代理過(guò)程中與各方的溝通
- 護(hù)理質(zhì)量改進(jìn)計(jì)劃書(shū)
- 2014電氣裝置安裝工程低壓電器施工及驗(yàn)收規(guī)范
- (正式版)JBT 11270-2024 立體倉(cāng)庫(kù)組合式鋼結(jié)構(gòu)貨架技術(shù)規(guī)范
- 中醫(yī)治療失眠課件
評(píng)論
0/150
提交評(píng)論