UNIX系統(tǒng)程序設計_第1頁
UNIX系統(tǒng)程序設計_第2頁
UNIX系統(tǒng)程序設計_第3頁
UNIX系統(tǒng)程序設計_第4頁
UNIX系統(tǒng)程序設計_第5頁
已閱讀5頁,還剩23頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第一章:生成一個Process(進程)進程是什么?簡單地說,進程就是在執(zhí)行狀態(tài)下的一個程序(包括CPU狀態(tài),所占內存的狀態(tài),等等)A進程生成了B進程,也就是說,A程序在執(zhí)行的時候,又生成了另一個進程B。這個時候,我們可以把A進程叫做父進程,把B進程叫做子進程。例程序://Usage:./a.out20#includeintmain(intargc,char*argv[]){intdep_time;dep_time=atoi(argv[1])*60;/將/參數中給出的20(分鐘)轉換成整型的秒數if(fork()==0)//生成子進程,然后父進程中止{sleep(dep_time);fprintf(stderr,!!!!!!!!\n);}return0;}上面的程序是一個鬧鐘程序。當你執(zhí)行之后。程序不會顯示什么,而是一下就回到UNIX的提示符下。但是你在命令行中指定了20分鐘后你有事,那么在你執(zhí)行這個程序之后20分鐘,他會提醒你到時間了。本程序只是做示例用,沒有檢查參數是否正確,等等。生成一個新的進程,可以使用fork()函數。以下說說fork()函數。頭文件:#include形式pid_tfork();參數無返回值成功時:父進程中:子進程的PID(ProcessID)子進程中:0失敗時:父進程中:-1由于失敗,沒有生成子進程;fork()剛執(zhí)行完的時候,子進程和父進程是完全一模一樣的兩份進程(當然,PID是不一樣的)。他們的各個變量的值都是一樣的,而且都認為自己已經執(zhí)行完fork()了。fork()后,區(qū)分父進程和子進程,只要看fork()的返回值就行了。if(fork()==0)printf這是子進程);elseprintf(這是父進程);同理:if(fork()==0){//接下來要子進程做的工作}else{//接下來要父進程做的工作}一般,我們會把fork()返回給父進程的值保存下來(其實就是子進程的PID),等到需要結束子進程的時候,我們關掉他,如下:pid_tchild_pid;child_pid=fork();if(child_pid==0){}else{}// 需要結束子進程的時候kill(child_pid,SIGKILL)//kil1(函數是用來發(fā)給另一個進程一個消息的。以后再講。先寫這些,試試手。喜歡就頂。要是沒人愛看我就不寫了。呵呵。省得大家說我亂貼垃圾。以后計劃貼的東西:在程序中執(zhí)行UNIX命令或者另一個程序取得環(huán)境變量并利用UNIX文件系統(tǒng)(在程序中取得分區(qū)信息,等等)使用管道操作達到在各進程互相交流數據信號(signal)進程間共享內存用message實現進程間共享信息第二章:在程序中執(zhí)行UNIX命令或者其它程序在UNIX下,像DOS的/r/那樣的程序,我們稱之為外殼(shell)。外殼就是一個命令解釋器,你在外殼的提示符下輸入命令(如同DOS的提示符一樣),系統(tǒng)便會執(zhí)行。DOS的提示符一般是C:\>,當然,你想改成什么樣就能改成什么樣,又當然,像BBS一樣貼張圖上去是不太現實的。UNIX的提示符根據外殼的不同是不同的。為了更好地說明本章想講解的內容,我們先做一個外殼試試(玩具級別的)。我們給他起名叫SSH(SohuShell)吧。想取名叫CSH,可惜CSH在沒生我之前就有了。呵呵。/*簡單的外殼程序*/#includeintmain(){staticcharprompt[64]=>;charcommand[256];intst;fprintf(stderr,%s,prompt);//在屏幕上輸出提示符while(gets(command)!=NULL)//取得命令{if(fork()==0)//生成子進程{//這里是子進程接下來要做的事if(execl(command,command,(char*)0)==(-1))//上一句是執(zhí)行命令exit(1);//當出錯時子進程異常中止}else{//父進程wait(&st);//等待子進程結束fprintf(stderr,%s,prompt);//輸出提示符,等待命令}}return0;}執(zhí)行方法:%./ssh>/bin/ls當前目錄下文件名一覽>Ctrl+D%普通的外殼在執(zhí)行exit命令后會關閉。也就是說,退出一層外殼。咱們這個程序現在還做不到。愿意的話加上這個功能試試好了。所以要關閉這個外殼就得來點狠的oCtrl+D,Ctrl+C什么的。再不你就再開一個外殼然后ps-ef再kilO再狠一些……拆硬盤,撥電源我們這里有了一個新的函數:execl()o其實他是一組函數中的一個。這組函數如下:intexecl(path,arg0,arg1,...,argn,(char*)0);intexecv(path,argv);intexecle(path,arg0,arg1,...,argn,(char*)0,envp);intexecve(path,argv,envp);intexeclp(file,arg0,arg1,...,argn,(char*)0);intexecvp(file,argv);其中的參數定義如下:char*path;char*file;char*arg0,*arg1,...,*argn;char*argv[];char*envp[];返回值:成功時:所執(zhí)行的命令將會覆蓋現有的進程,所以無返回值失敗時:-1用過TC的朋友應該知道,TC的Process.h里有一個system。函數。這組函數其實和system。的功能差不多。比方說:execl(/bin/ls,/bin/ls,-al,/home,(char*)0);或者char*argv[];strcpy(argv[0],/bin/ls);strcpy(argv[1],-al);strcop(argv[2],/home);execv(/bin/ls,argv);都相當于在命令行下敲入了/bin/ls-al/home并且回車。(引號不是命令里的。是我解釋時加上去的。別看混了)。execle()和execve(),函數名最后一個字母都是e。就是說,這兩個函數在調用其它程序的同時,還可以把環(huán)境變量一起傳給被調程序execlp()和execvp(),函數名最后一個字母都是p,就是說,這兩個函數在使用的時候,就算你不指定命令文件所在的路徑,它也會根據環(huán)境變量PATH去挨個地方找。找著就執(zhí)行。找不著拉倒。比方說:setenv$path=(/bin$path)這句話將環(huán)境變量PATH的第一個路徑設為/bin。這是在SHELL下執(zhí)行的。C里沒這東西吧。在程序中這樣用這個函數execlp(ls,ls,-al,/home,(char*)0);與上面的效果一樣。當然。如果你PATH變量沒設好的話。它就不一定找到哪兒去了。還有一個函數是wait(),說明如下:#includepid_twait(int*stat_loc);返回值就是一個PID了。忘了PID是什么意思的朋友光顧一下我上一篇貼子。它的參數有些意思。其實它與你的子進程用什么方式結束有關系。當你的子進程以exit()方式結束的話,stat_loc所指向的地址的前8位將會是exit()的參數的后8位,而stat_loc所指向的地址的后8位是0。比方說:你的子進程是exit(l)那stat_loc所指向的地址的內容應該是0000000100000000exit():#includevoidexit(intstatus);就算你在程序中沒寫exit()函數,編譯器也是認為你是在最后加入了這個函數。下一篇貼子,咱們再把這個Shell完善一下第三章:增強ssh的功能(使用環(huán)境變量)還記得上次做的那個ssh吧?這回咱們把它再改一改。大家知道。C語言的main函數是這樣的:intmain(intargc,char*argv[],char*envp);前兩個不用說了吧。其實知道前兩個的話應該也知道第三個:取得系統(tǒng)環(huán)境變量。UNIX和DOS—樣。有著各種各樣的環(huán)境變量(明確地說,應該是比DOS用得更廣泛)。比方說常用的$PATH,$HOME,$USER等等。如果用的是csh,可以改?/.cshrc或者干脆直接在命令行下設就行了。這些是UNIX的東西,不屬于我們在C/C++討論的東西了。有興趣的朋友可以到UNIX版去看一看。下面是一個取得系統(tǒng)環(huán)境變量并輸出的小程序/*getenv.c取得系統(tǒng)環(huán)境變量并輸出*/#includeintmain(intargc,char*argv[],char*envp[]){inti;for(i=0;envp[i]!=NULL;i++){printf(%s\n,envp[i]);}return0;}編譯執(zhí)行后應該是這樣:%./getenvDISPLAY=:0.0HOME=/home/syuuiUSER=syuui以及你的系統(tǒng)中其它的環(huán)境變量。想一想它是怎么運行的:當你在命令行下敲入getenv并回車,shell就fork出一個新的進程,然后讓這個進程去執(zhí)行getenv,并把現在系統(tǒng)中的各個環(huán)境變量存入到envp里去。這個時候,原來shell的進程就是getenv進程的父進程。envp的參數是從父進程中取得的?,F在你知道上一節(jié)中為什么有那兩個帶p的函數了?上一回做的ssh這個外殼的命令是帶不了參數的。因為咱們的程序不知道要去讀參數。這回不妨做一個能讀參數的試試#include#defineSP0#defineNOSP1voidgetarg(char*argv[],char*p);/取/得各個參數intmain(){staticcharprompt[64]=>;charcommand[256],*argv[256],*p;intst;fprintf(stderr,%s,prompt);while((p=gets(command))!=NULL){getarg(argv,p);if(fork()==0){if(execv(argv[0],argv)==(-1))exit(1);}else{wait(&st);fprintf(stderr,%s,prompt);}}return0;}voidgetarg(char*argv[],char*p){inti,sp_flag;sp_flag=SP;〃SP代表空格,NOSP代表非空格的意思for(i=0;*p!='\0';p++){if(sp_flag==SP&&*p!='')//如果現在狀態(tài)是讀過空格,但現在這個字母是非空格//那很顯然,讀到一個新的參數{argv[i]=p;i++;sp_flag=NOSP;}if(*p==''){*p='\0';sp_flag=SP;}}argv[i]=(char*)0;}這篇文章東西說得比較少。給大家出個問題吧:看到了吧。C能做的事情很多。誰說C不支持多進程?那是因為DOS。呵呵上回做的ssh,必須輸入絕對路徑才能執(zhí)行。現在要求,咱們做一個只輸入命令以及參數,程序自動4$path里定義的各路徑查找這個命令。找到就執(zhí)行,找不到就報錯的外殼出來試試?有做出來的,請貼程序。2003-7-27關于進程狀態(tài)的一些補足:眾所周知,UNIX是一個多用戶多任務的操作系統(tǒng)。所謂多任務,就是指在同一個時間內,看上去有許多任務在同時執(zhí)行。這一點是與DOS不同的。所以TurboC下找不到fork()函數。當然。大家知道,實際上CPU同一個時間只能處理一件事。它是在輪流執(zhí)行這些進程,由于速度很快。所以看起來像是所有進程在一起跑。同樣,一個進程,它有使用CPU的時候,也有等待使用CPU的時候。這就決定了進程的幾種狀態(tài)。進程大致可以分為“執(zhí)行中”,“準備執(zhí)行”,“睡眠中”三種狀態(tài)。執(zhí)行中:進程在占用CPU。準備執(zhí)行:想要使用CPU,但是CPU被別的進程占用中,所以等待CPU空閑。睡眠中:等待事件發(fā)生的進程。比方說,等待輸入輸出結束的進程。我們用fork()生成一個新的進程,或者用exec函數組覆蓋當前進程后。當這個子進程結束的時候要給父進程送去一個信號(ignal,后述),然后轉為zombie狀態(tài)。zombie狀態(tài)是指一個進程已經結束,所占內存空間等等已經返還給系統(tǒng),但是在系統(tǒng)的進程列表中仍然存在的這么一個狀態(tài)。當父進程執(zhí)行wait()后,子進程才真正完全結束。如果父進程先一步結束了,那么由init代替父進程。所以,wait()不只是等待子進程結束。它還有上面所說的這個任務。我們寫個程序來看一下:/*zombie*/#includeintmain(){intst;if(fork()==0){exit(1);//子進程生成后直接結束}else{sleep(300);//休眠300秒wait(&st);printf(Returncode=%d\n,i);}return0;}編譯后執(zhí)行%./zombie&這個時候,程序在后臺運行%ps-ef|grepzombie看一下,是不是有一個進程處在zombie狀態(tài)?第四章:文件系統(tǒng)UNIX所管理的機器一般是大型機而不是PC。所管理的硬盤一般也非常大。所以一般分成幾個區(qū),每個區(qū)都有其單獨的文件系統(tǒng)。比方說你能大概能找到這樣的一些文件/dev/sd/c0t0d0s0/dev/sd/c0t0d0s1當UNIX啟動的時候,分區(qū)被掛報mount并統(tǒng)一成一個樹狀的文件系統(tǒng)分區(qū)的物理構造咱們暫且放在一邊,先寫個程序,讀一下分區(qū)信息試試。/*ndf.c計算參數所指定的分區(qū)的剩余空間比率*/#include#include#includeintmain(intargc,char*argv[]){structstatvfsbuf[1];sync();if(statvfs(argv[1],buf)!=0){fprintf(stderr,Cannotreadsuperblock!\n);exit(1);}fprintf(stderr,%4.1f%%free\n,(float)buf[0].f_bfree/buf[0].f_blocks*100);return0;}編譯執(zhí)行:%./ndf/49.8%free這里用了一個statvfs函數。其具體如下:#include#includeintstatvfs(char*path,structstatvfs*buf);返回值:成功時:0失敗時:-1還有一個sync()函數。用來更新分區(qū)的superblock;voidsync();UNIX系統(tǒng)為了加快處理速度,將分區(qū)的superblock信息讀到內存中保存。sync()函數就是把在內存中保存的superblock信息再寫回到硬盤上去。UNIX系統(tǒng)使用好幾種文件系統(tǒng)。有S5,ufs,VxFS等等。雖然這些文件系統(tǒng)的構造非常不同,但一通百通,咱們在這幾篇貼子里只討論一下比較容易理解,而且經典的”S5文件系統(tǒng)。(別的我也不會。呵呵)S5:文件名最長14字節(jié),構造簡單ufs:文件名最長255字節(jié),BSD所用的文件系統(tǒng)。VxFS:VeritasSoftwave公司開發(fā)的文件系統(tǒng)。出現錯誤時可以快速恢復。由于這種文件系統(tǒng)保證文件在硬盤上連續(xù)存放,所以處理速度很快?,F在說一下S5分區(qū)的構造一個分區(qū)包含如下四部分(按順序):[bootblock][superblock][inodeblock][datablock]bootblock:這個部分在分區(qū)的最開始處,用來存放引導程序。就算是不能引導的分區(qū)一樣有bootblock,這個時候這部分就沒有用了。不過一般這部分也不大。大多數只有512或者1024字節(jié)。superblock:superblock在bootblock之后,用來存放這個分區(qū)全體的管理信息。上面那個ndf.c就是讀的這部分所存儲的信息。里邊存放了inodeblock的大小,freeblock數組等等。根據這些信息可以得知datablock的開始位置。inodeblock:inode是indexnode的縮寫。inodeblock就是存放inode的部分UNIX把一切都看成是個文件。包括目錄以及設備等等的所有的文件都有一個lnode號,作為這個文件的管理信息。文件本身存在于數據區(qū),但是node號存在inodeblock里。主要包含文件的模式,鏈接數,文件所有者,文件大小,在硬盤上的位置,最后讀寫時間,最后更新時間等信息。為了加快存儲速度,系統(tǒng)會把一定數量的inode存至內存。UNIX系統(tǒng)不一樣,存多少也就不一樣。datablock:這部分就是存放數據本身的了。這部分被分成一定大小的塊,如同DOS的扇區(qū)一樣。一般大小是1024字節(jié),分到4096的也有。解說到這里,我們再來寫個程序。打開一個目錄,然后把這個目錄下所有的文件的inode號及文件名輸出來。/*nls.c*/#include#defineDIRSIZ14intmain(intargc,char*argv[]){structdir{inti_no;charf_name;};structdirdir_data[1];FILE*fp;fp=fopen(argv[1],r);while(fscanf(fp,%i%s,&(dir_data[0].i_no),dir_data[0].f_name)!=EOF){printf(%i%s\n,dir_data[0].i_no,dir_data[0].f_name);}fclose(fp);return0;}%./nls/2048usr2049home別忘了,在UNIX下,目錄也當成文件。最近,為了使目錄的格式變得通用而不再依賴于操作系統(tǒng),程序中大多使用統(tǒng)一的格式這種情況下,我們最好就不直接用fopen()打開目錄,而使用opendir()readdir()等函數比較好。重寫一下上面的程序。/*nls2.c*/#include#includeintmain(intargc,char*argv[]){DIR*fp;structdirent*p;fp=opendir(argv[1]);while((p=readdir(fp))!=NULL){printf(%i%s\n,p->d_ino,p->d_name);}closedir(fp);return0;}執(zhí)行結果和上面一樣。函數說明如下:#includeDIR*opendir(char*dirname);打開一個目錄,成功時返回DIR結構體的地址,失敗返回NULLstructdirent*readdir(DIR*dirp);取得打開目錄中下一個文件的信息,成功時返回下一個文件信息的地址,失敗時,或者讀至末尾時返回NULLintcloseidr(DIR*dirp);關閉目錄,成功時返回0,失敗時返回-1注意:readdir在成功地讀出一項之后,會自動指向下一項。假設你有如下程序:structdirent*p;p=readdir(fp);p++;//千萬不要像這行這樣寫。你無法保證這目錄里的文件信息是連續(xù)存放的。你只要一遍一遍地用readirQ這個函數就行了。它幫你全搞定第五章:目錄及文件操作上一章我們說了一些UNIX的文件系統(tǒng)的物理構造。下面我們來看看具體怎么對文件進行操作。當然這里所說的文件及目錄操作不是fopen()。呵呵。我們要做一些fopen辦不到的事。/*newer.c比較參數所指定的兩個文件將其中新的文件的文件名輸出來*/#include#include#includeintmain(intargc,char*argvp]){structstatbuf[2],*p;if(argc!=3){fprintf(stderr,Usage:%sfile1file2\n,argv[0]);exit(1);}p=buf;if(stat(argv[1],p)!=0)//取得第一個文件的信息{fprintf(stderr,%snotfound!\n,argv[1]);exit(1);}p++;if(stat(argv[2],p)!=0)//取得第二個文件的信息{fprintf(stderr,%snotfound!\n,argv[2]);exit(1);}if(buf[0].st_mtime>buf[1].st_mtime)/比/較最終更新時間printf(%s\n,argv[1]);elseprintf(%s\n,argv[2]);return0;}執(zhí)行結果:%newerafilebfilebfile使用stat()函數,可以得到文件的信息。這些信息是在inode中保存的這個文件信息的一部分。得到的信息將會保存在stat.h中定義的stat型的結構體中。stat()函數解釋如下:#include#includeintstat(char*path,structstat*buf);返回值:成功時:0失敗時:-1我們再來寫一個玩玩#include#include#include#defineMASK0555//這個數字的意思等一下解釋,他代表“可讀”和“可執(zhí)行”intmain(intargc,char*argv[]){structstatbuf[1];mode_tmode;if(argc!=2){fprintf(stderr,Usage:%sfile\n,argv[0]);exit(1);}if(stat(argv[1],buf)!=0){fprintf(stderr,Cannotreadi-node\n);exit(1);}mode=(buf[0].st_mode&MASK);/計/算文件新的權限標志if(chmod(argv[1],mode)!=0)//改變文件的權限標志{fprintf(stderr,Cannotchangemode\n);exit(1);}return0;}現在來解釋一下0555這個數字的意思。眾所周知,UNIX是一個多用戶多任務的操作系統(tǒng)。每個用戶有自己的權限,這個權限限制了用戶可以做哪些事,不可以做哪些事。對于文件來說,用戶可以分成四類:root(根用戶,超級用戶)這個用戶是系統(tǒng)的管理吶,權限最大,可以隨意讀寫任何文件。owner(文件擁有者)實際擁有文件的用戶。文件屬主。group(組成員)用戶所在的用戶組的成員other以上三類之外的其它用戶UNIX中,每個文件信息中包括一組9位的權限標志。分別給文件屬主,用戶組和其他用戶指定對該文件的讀、寫和執(zhí)行權。請看下面的例子:%ls-l/bin/ls-rwxr-xr-x1rootbin27281Aug152002/bin/ls*重要是看-rwxr-xr-x,第一個-表示這是一個普通文件,這個位置也可以出現些別的字符,比方說目錄的話這里會是d。而1表示一個鏈接。余下的9位可以分成三段,每段三位。本例中從左至右rwx表示文件的屬主擁有文件的讀,寫,執(zhí)行權r-x表示同組的用戶擁有文件的讀,執(zhí)行權(注意,“寫”權限的位置是一個-)r-x表示其它的用戶擁有文件的讀,執(zhí)行權文件的訪問權限還可以用三位8進制來表示。如上例rwxr-xr-x可以換成111101101(有該權限,則該位為1,否則為0)換成8進制,二進制的111是八進制的7,二進制的101是八進制的5?,F在看看0555是什么意思?就是說,可以讀,可以寫的意思。把0555和原來文件的權限標志做與運算,得到的新的權限標志就是把原來的文件權限標志中所有的寫權限全取消了。其余權限變。然后在程序中用chmod()把這個新的權限標志賦給文件即可。chomd()函數用法如下:#include#includeintchmod(char*path,mode_tmode);返回值:成功時:0失敗時:-1關于目錄,還有另一個比較有用的函數,即chdir()。用這個函數可以在程序中轉換當前目錄。#include#includeintchdir(char*path);返回值:成功時:0失敗時:-1以上兩章,簡單地敘述了一下UNIX的文件系統(tǒng)以及在UNIXC中對文件的操作方法。并列舉了常用的一些函數。下一章,我們將簡單地敘述一下UNIXC的輸入輸出,以及用管道pipe)實現兩個進程互換數據。第六章:標準輸入輸出以及管道操作標準輸入輸出大概所有的操作系統(tǒng)都差不多吧。從鍵盤輸入。從屏幕輸出。除非你用的還是打紙帶的老家伙。呵呵。主要說一下管道操作。注意:此處所說的管道操作不是%cat-ntest.c|more這是在提示符狀態(tài)下使用管道,把第一個程序(cat)的輸出作為輸入傳給第二個程序(more)。我們現在要在程序中使用的管道原理與此相同。將某一個程序的輸出變?yōu)榱硪粋€程序的輸入。做一個石頭剪子布的程序。其中包括一個父程序和一個子程序。注意是兩個程序,不是兩個函數。也不是兩個進程。不過因為父程序運行的時候要通過exec()函數來執(zhí)行子程序。所以我們也可以把它看成是兩個進程。父進程取得用戶的輸入(石頭S,剪子C,布P中的某一個,P=0,S=l,C=2)并通過管道傳給子進程子進程取得父進程傳來的數字,加上自己的PID后做為種子數,生成一個隨機數然后被3除,得出來一個余數是0、1或者2。再通過管道傳回給父進程。父進程根據兩個數字(用戶輸入的,以及子進程傳回來的)判定勝負,輸出/*parent.*/#include#includeintmain(){inti,j,st,fd[2];pid_tpid;staticintresult[3][3]={1,0,2,2,1,0,0,1,2};charargv1[3],argv2[3],ch;ch=getchar();switch(ch){case'P':i=0;break;case'S':i=1;break;case'C':i=2;break;default:fprintf(stderr,EnterP,SorC,Please!\n);exit(1);}if(pipe(fd)!=0)//建立管道{fprintf(stderr,PIPEError!\n);exit(1);}sprintf(argv1,%d,fd[0]);sprintf(argv1,%d,fd[1]);switch(pid=fork())//fork出一個新的進程,執(zhí)行子程序{case0:if(execl(child,child,argv1,argv2,(char*)0)==(-1))exit(1);//執(zhí)行了子程序break;case-1:fprintf(stderr,forkError!\n);exit(1)}write(fd[1],&i,sizeof(i));//向管道寫數據wait(&st);//等待子程序結束read(fd[0],&j,sizeof(j));//從管道讀數據switch(result[i][j]){case0:printf(Youwon\n);break;case1:printf(Same\n);break;case2:printf(Youlost\n);}close(fd[0]);close(fd[1]);return0;}/*child.c*/#includeintmain(intargc,char*argv[]){inti,j,read_fd,write_fd;read_fd=atoi(argv[1]);//設定輸入用管道write_fd=atoi(argv[2]);//設定輸出用管道read(read_fd,&i,sizeof(i));/從/管道中取得數據srand(i+getpid());//設定隨機數的種子數j=rand()%3;//生成隨機數write(write_fd,&j,sizeof(j));/寫/向管道close(read_fd);close(write_fd);return0;}編譯執(zhí)行父程序即可。這種管道是用來在父子進程間傳遞數據的。如同大家在程序中所見:父進程開辟管道然后生成子進程執(zhí)行子程序,并將管道參數作為main()函數的參數傳給子程序。通過一個相同的管道實現讀寫。開辟管道時,我們用到了這個函數:intpipe(intfd[2]);開辟一個管道參數fd⑵是一個有兩個元素的數組??梢钥闯墒莾蓚€管道的記述子。fd[O]用來讀,fd[1]用來寫。返回值:成功時:0失敗時:-1讀取/寫入管道時,我們用到了下面函數讀取管道中的數據intread(intfd,char*buf,unsignedintnbyte);向管道中寫入數據intwrite(intfd,char*buf,unsignedintnbyte);其中,fd是管道記述子,也就是我們前面說的fd[0]或者fd[l],buf裝數據,nbyte指定讀/寫數據的數量,單位是字節(jié)。成功時返回0,失敗時返回-1。由于準備考研。這篇文章耽誤了一些時日。最近還有些事,也許下一篇也得幾天后才能再貼。另外,在此向諸位致歉。我的程序是在學校的UNIX下寫的,一般是用軟盤帶回來,寫上一篇貼子程序的時候沒有帶軟盤,只好打印出來回來再敲。在輸入的時候有一個錯誤(現已改正)intmain(intargc,char*argvp]);應為intmain(intargc,char*argv[]);下一章準備說說UNIX的進程(Process)和信號(signal、另外,感謝版主將我前幾篇貼子選進了精華區(qū)。愚作不堪,如有錯誤及不到之處請諸位高人指正為盼。對本貼內容如有不明,請給我留言。我會盡快回答(如果我明白的話)。謝謝遠方的潔白的哈達,霧中雪,初學無罪,hellosamxiaoyu等朋友。有你們的支持,我才有信心繼續(xù)寫下去。愿拙文能對諸位學習C語言有所幫助。下章見。第七章:UNIX進程(Process)及信號(signal)在進程執(zhí)行過程中,如果出現什么事件(event),系統(tǒng)將會給進程一個信號。進程可以在得到這個信號后做些適當的處理。還是與以前一樣,咱們先來個小程序吧。/*slot.c*/#include#includeintx[3];longkin;intkekka();intmain(intargc,char*argv[]){srand(getpid());/*設定隨機數的種子數*/signal(SIGKILL,kekka);/*當Ctrl+C按下時,執(zhí)行kekka函數*/kin=atoi(argv[l]);/*取得賭注數目*/printf(pressCtrl+Ctostopmaching!\n);while(l){x[0]=rand()%10;x[1]=rand()%10;x[2]=rand()%10;printf(%d%d%d\n,x[0],x[1],x[2]);fflush(stdout);}return0;}intkekka(){if(x[0]==x[1]&&x[1]==x[2])printf(Great,youwon%d\n,kin*3000);elseprintf(Youlost%d\n,kin);exit(0);/*游戲結束。退出程序*/}執(zhí)行方法:%./slot100執(zhí)行后。畫面上會出現一排排的數字。當你按下Ctrl+C的時候,程序得到信號,跳轉至kekka()函數入,判斷隨機數。然后退出程序。這里有這樣一個新函數。是我們在UNIX程序設計中時常用得到的。#includeint(*signal(intsig,void(*func())))();返回值:成功時:進程中斷地址錯誤時:SIG_ERR這個函數挺不好懂呵。解釋解釋。它的意思是,當收到SIG這個信號時,跳轉至func()這個函數處繼續(xù)執(zhí)行。intsig這個參數其實好辦。下面我們會給出一個表。將其所有的參數一并列出。這是在頭文件中#define的,代表各種不同的信號。void(*func())這個參數是一個函數指針。意思是在SIG信號被傳給進程后,所要執(zhí)行的部分。此外,func()這個參數還可以為如下值:SIG_DEL系統(tǒng)的默認動作SIG_IGN忽略這個信號。signal作用只有一次,當一個信號發(fā)生,觸發(fā)signal動作的之后,signal就不起作用了。比方說,在上例中,如果你把所有的exit(O)全去掉(不退出程序),程序會進入無限循環(huán)中。你按一次Ctrl+C,signal執(zhí)行一次,顯示出結果。然后繼續(xù)循環(huán),生成隨機數,輸出。你再按Ctrl+C,signal就不好使了。如果需要重復使用signal()函數,可以考慮在signa1調用的動作函數中再加一個signal比方說在上例的程序中再加一個signal(SIGKILL,kekka);加了signal這個函數后,整個程序如果沒收到信號,則正常執(zhí)行。否則跳轉至igna1中指定的func()函數處繼續(xù)。當然,我們不可能,也沒有必要去為每一個信號指定一個動作。當未指定動作的信號發(fā)生的時候,系統(tǒng)會執(zhí)行默認的動作。比方說我們知道:當Ctrl+C按下的時候,正常默認的動作是結束程序。但是,Ctrl+C按下時,發(fā)生的信號是SIGKILL,如果你為SIGKILL這個信號指定了一個動作的話,系統(tǒng)將去執(zhí)行你指定的這個動作,而不管原來默認的了。打個比方讓大家更容易懂一些:比方說:你在睡午覺。突然來了一個美女(帥哥)(Ctrl+C)讓你陪她/他去逛街購物共進晚餐……(此處刪去若干字),你正常的默認的動作是馬上起來陪她出去玩(程序中止)。但是你媽說不許去,你媽說你得在家擦窗戶,你只好留在家擦窗戶signal()指定的動作)。擦完窗戶之后你可以選擇陪美女去逛街(默認動作,Ctrl+C的埸合是退出程序,即上例程序中的exit(O)),可以選擇繼續(xù)睡覺(回到原來中斷的地方繼續(xù)執(zhí)行,上例程序中如果沒有exit(O),便會回到中斷處繼續(xù)執(zhí)行)。(大哥。打個比方嘛。干嘛拿臭雞蛋砸我??…)signal種類非常多,在此不一一列出。使用這個函數,你可以防止用戶按下Ctrl+C結束程序。還可以做很多其它的事——只有你想不到的,沒有C做不到的。注意:根據UNIX系統(tǒng)的不同,signal的定義是不一樣的。比方說,有的老式UNIX工作站上SIGINT是按下del鍵后發(fā)生的信號,而有些機型上剛是按下Ctrl+Z發(fā)生的。在使用時要注意。再一次感謝SOHUC/C++論壇的朋友們的支持。我考上研究生了。下一次,準備說說UNIX下C程序中,如何向別的進程發(fā)送信號。第八章:向其它進程傳遞信號上一章,我們簡單地說了一下信號。信號不僅僅可以在本進程內使用,還可以向其它的進程傳遞。確切地說,一般情況下,一個進程收到的信號絕大部分是從系統(tǒng)的NIT傳來的。而INIT也是一個進程。用過UNIX或者LINUX的朋友大概都知道,UNIX系的操作系統(tǒng)里,有一個kill命令。如果你的一個進程無法中止,可以使用kill命令強行干掉它。%killmypro我們自己也可以做個簡單的kill命令。/*nkill.c*/#include#include#includeintmain(intargc,char*argv[]){pid_tpid;if(argc!=2)//檢查命令行參數個數{fprintf(stderr,Usage:%sPID\n,argv[0]);exit(1);}pid=atol(argv[1]);if(kill(pid,SIGKILL)!=0)//向pid這個進程傳送一個SIGKILL信號{fprintf(stderr,killfailed\n);}return0;}執(zhí)行例:%sleep300&(后臺睡眠300秒)[1]520%nkill520[1]+Killedsleep300你用自己寫的程序殺死了一個sleep進程。沒事。殺了就殺了吧。留著也是垃圾??…說明一下,眾所周知,UNIX是非常注意用戶的權限的。所以,如果你向你自己生成的進程發(fā)送SIGKILL的話,這個進程會中止。如果你是向別的(你沒有寫權限的)進程發(fā)送SIGKILL的話,除非那個進程允許你動它,否則你發(fā)了也是白發(fā)。打個不恰當的比較下流一些的比方:如果你要求親一下你自己老婆的話(你有“親”的權限),你會如愿以償。但是如果你想親一下別人的老婆的話(你沒有“親”的權限),除非他及她同意,否則……呵呵。你死定了。所以,執(zhí)行上面的程序的時候,你必須保證你有權限關掉那個進程??刹皇钦f侮s-ef然后隨便挑一個進程就能關的。要不那黑客也太好當了。幾乎是咱們的老規(guī)矩了:先寫個程序,然后解釋這個程序,說說其中新出現的函數。這回還照舊吧。大家看到了。這里新出來一個kill()函數。解釋如下:#include#includeintkill(pid_tpid,intsig);返回值:成功時:0失敗時:-1這個函數是向進程ID(PID)為pid的進程傳送一個sig信號。這個函數有什么用呢?用處可大了。在上一章中,我們用到了SIGKILL信號。這一節(jié)我們用的還是這個信號。實際上,信號(signal)有許多。而且根據UNIX系統(tǒng)的不同,這些信號也不太一樣。我們簡單列舉一些信號例子。其用途請恕小生才疏,解釋不清。有興趣的朋友可以自己查閱一下資料。SIGHUP,SIGINT,SIGQUIT,SIGILL,SIGTRAP,SIGABRT,SIGEMT,SEGFPE(注意,這里是SEG),SIGKILL,SIGBUS,SIGSEGV,SIGSYS,SIGPIPE,SIGALRM,SIGTERM,SIGUSR1,SIGUSR2,SIGCLD,SIGPWR,SIGWINCH,SIGSTOP,SIGTSTP,SIGCONT,SIGTTIN,SIGTTOU使用kill()函數,可以將這些信號中的一個或者多個送至另一個進程。剛才的程序中,我們就是將一個SIGKILL信號送至sleep那個進程,該進程收到信號,以為是用戶按下了Ctrl+C,就中止了。知道了這些,我們再來做一個比較有意思的程序。我們做一個cat程序(類似于DOS下的type,查看文本文件的內容)。不過這個cat程序與UNIX的cat不太一樣,我們把由參數指定的文件的內容輸出到屏幕上。如果你沒指定文件,那么這個程序會等你五秒,在這五秒鐘里,你可以從標準輸入輸入一個文件。然后顯示。萬一這五秒鐘里你也沒輸入什么文件名。程序會把當前目錄下的所有文件名打印出來(ls),以督促你輸入一個文件名。/*show.c*/#include#include#includejmp_bufenv;intfunc();intmain(intargc,char*argv[]){charfilename[256];signal(SIGALRM,func);//如果收到一個SIGALRM信號,則轉至func處執(zhí)行if(argc==1){fprintf(stderr,&);alarm(5);//5秒鐘后向自己發(fā)送一個SIGALRM信號setjpm(env);//設置記號scanf(%s,filename);execl(/usr/bin/more,more,filename,(char*)0);}else{execl(/usr/bin/more,more,argv[1],(char*)0);}return0;}intfunc(){intst;fprintf(stderr,Whichfiledoyouwanttoread?\n\n);if(fork()==0){execl(/bin/ls,ls,-FCa,(char*)0);}wait(&st);longjmp(env,0);//跳轉至先前的setjmp()處}這個程序看明白了嗎?可能有些不太好懂。我們從頭跑一遍試試。首先是裝入頭文件等等。沒關系了。然后signal(SIGALRM,func),即,當收到一個SIGALRM信號的時候,中斷現在所做的工作。轉而執(zhí)行func()函數。不過可惜得很,現在暫時還沒人給咱們發(fā)送SIGALRM信號,我們繼續(xù)往下看。下面是判斷命令行參數了。如果只有這個程序的可執(zhí)行文件一個參數的話(也就是說,沒有指明要顯示哪個文件的內容),那就打印出一個&號來。然后設置alarm(5),也就是說,在5秒后給自己發(fā)一個SIGALRM信號。下一句setjpm(env)是設置一個記號,以后當執(zhí)行至longjmp()函數的時候,就會跳轉到這里來。然后等待你輸入文件名。如果在這五秒種里,你輸入了一個文件名的話,那么就向下執(zhí)行一一通過execl()函數調用系統(tǒng)的more命令,顯示該文件的內容。然后退出。這只需要要很少的一段時間。絕對用不上一秒(看你當前目錄下有多少文件了。沒人會在一個目錄下裝上1G吧?)。最后結束程序。alarm()在5秒后發(fā)出的信號樂意咋地咋地去吧。咱不用管了。問題是:如果在這五秒鐘里,你沒有輸入文件名的話,事情就變得非常微妙了。這種情況下,程序將停留在scanf(%s,filename);這一行。等待你輸入文件名。然后五秒種的期限到了°alarm()函數向這個進程發(fā)了一個SIGALRM信號。signal。函數檢測到這個信號,轉向func()函數處執(zhí)行。func()函數先是輸出了一個提示信息Whichfiledoyouwanttoread?。然后fork()出一個子進程。這個子進程通過execl()函數調用/bin/ls命令,顯示出當前目錄下的所有文

溫馨提示

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

評論

0/150

提交評論