?? 11.html
字號:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312"> <META NAME="GENERATOR" CONTENT="《良友》v2.1, 作者:安富國,http://winking.126.com"> <TITLE>進程和線程編程</TITLE></HEAD><BODY style="font-family: 宋體; font-size: 9pt"> <CENTER><TABLE CELLSPACING=10 CELLPADDING=10 WIDTH="60%" BGCOLOR="#FFB693" ><TR><TD ALIGN=CENTER><FONT SIZE=+2><!--標題由此開始-->進程和線程編程</TD></TR></TABLE></CENTER><p><h3>目 錄</h3><!--目錄由此開始--><A NAME="Content" ID="Content"></A><OL><LI><A HREF="#I236">進程和線程編程</A></LI><OL><LI><A HREF="#I237">原始管道</A></LI><OL><LI><A HREF="#I238">pipe()</A></LI><LI><A HREF="#I239">dup()</A></LI><LI><A HREF="#I240">dup2()</A></LI><LI><A HREF="#I241">popen()和pclose()</A></LI></OL><LI><A HREF="#I242">命名管道</A></LI><OL><LI><A HREF="#I243">創建FIFO</A></LI><LI><A HREF="#I244">操作FIFO</A></LI><LI><A HREF="#I245">阻塞FIFO</A></LI></OL><LI><A HREF="#I246">消息隊列</A></LI><OL><LI><A HREF="#I247">msgget()</A></LI><LI><A HREF="#I248">msgsnd()</A></LI><LI><A HREF="#I249">msgrcv()</A></LI><LI><A HREF="#I250">msgctl()</A></LI></OL><LI><A HREF="#I251">信號量</A></LI><OL><LI><A HREF="#I252">semget()</A></LI><LI><A HREF="#I253">semop()</A></LI><LI><A HREF="#I254">semctl()</A></LI></OL><LI><A HREF="#I255">共享內存</A></LI><OL><LI><A HREF="#I256">shmget()</A></LI><LI><A HREF="#I257">shmat()</A></LI><LI><A HREF="#I258">shmctl()</A></LI><LI><A HREF="#I259">shmdt()</A></LI></OL><LI><A HREF="#I260">線程</A></LI><OL><LI><A HREF="#I261">線程同步</A></LI><LI><A HREF="#I262">使用信號量協調程序</A></LI><LI><A HREF="#I263">代碼例子</A></LI><OL><LI><A HREF="#I264">newthread</A></LI><LI><A HREF="#I265">exitthead</A></LI><LI><A HREF="#I266">getchannel</A></LI><LI><A HREF="#I267">def</A></LI><LI><A HREF="#I268">release</A></LI><LI><A HREF="#I269">redezvous</A></LI><LI><A HREF="#I270">unbouded</A></LI></OL></OL></OL></OL><hr><br><A NAME="I236" ID="I236"></A><center><b><font size=+2>進程和線程編程</font></b></center><br> 看一下UNIX系統中的進程和Mach的任務和線程之間的關系。在UNIX系統中,一個進程包括一個可執行的程序和一系列的資源,例如文件描述符表和地址空間。在Mach中,一個任務僅包括一系列的資源;線程處理所有的可執行代碼。一個Mach的任務可以有任意數目的線程和它相關,同時每個線程必須和某個任務相關。和某一個給定的任務相關的所有線程都共享任務的資源。這樣,一個線程就是一個程序計數器、一個堆棧和一系列的寄存器。所有需要使用的數據結構都屬于任務。一個UNIX系統中的進程在Mach中對應于一個任務和一個單獨的線程。<br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I237" ID="I237"></A><center><b><font size=+2>原始管道</font></b></center><br> 使用C語言創建管道要比在shell下使用管道復雜一些。如果要使用C語言創建一個簡單的管道,可以使用系統調用pipe()。它接受一個參數,也就是一個包括兩個整數的數組。如果系統調用成功,此數組將包括管道使用的兩個文件描述符。創建一個管道之后,一般情況下進程將產生一個新的進程。<p> 可以通過打開兩個管道來創建一個雙向的管道。但需要在子進程中正確地設置文件描述必須在系統調用fork()中調用pipe(),否則子進程將不會繼承文件描述符。當使用半雙工管道時,任何關聯的進程都必須共享一個相關的祖先進程。因為管道存在于系統內核之中,所以任何不在創建管道的進程的祖先進程之中的進程都將無法尋址它。而在命名管道中卻不是這樣。<br><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I238" ID="I238"></A><center><b><font size=+2>pipe()</font></b></center><br>系統調用:pipe();<br>原型:intpipe(intfd[2]);<br>返回值:如果系統調用成功,返回0<br>如果系統調用失敗返回-1:errno=EMFILE(沒有空閑的文件描述符)<br>EMFILE(系統文件表已滿)<br>EFAULT(fd數組無效)<br>注意fd[0]用于讀取管道,fd[1]用于寫入管道。<br>#include<stdio.h><br>#include<unistd.h><br>#include<sys/types.h><br>main()<br>{<br>intfd[2];<br>pipe(fd);<br>..<br>}<br>一旦創建了管道,我們就可以創建一個新的子進程:<br>#include<stdio.h><br>#include<unistd.h><br>#include<sys/types.h><br>main()<br>{<br>intfd[2];<br>pid_t childpid;<br>pipe(fd);<br>if((childpid=fork())==-1)<br>{<br>perror("fork");<br>exit(1);<br>}..<br>}<p> 如果父進程希望從子進程中讀取數據,那么它應該關閉fd1,同時子進程關閉fd0。反之,如果父進程希望向子進程中發送數據,那么它應該關閉fd0,同時子進程關閉fd1。因為文件描述符是在父進程和子進程之間共享,所以我們要及時地關閉不需要的管道的那一端。單從技術的角度來說,如果管道的一端沒有正確地關閉的話,你將無法得到一個EOF。<p>#include<stdio.h><br>#include<unistd.h><br>#include<sys/types.h><br>main()<br>{<br>intfd[2];<br>pid_t childpid;<br>pipe(fd);<br>if((childpid=fork())==-1)<br>{<br>perror("fork");<br>exit(1);<br>}<br>if(childpid==0)<br>{<br>/*Child process closes up in put side of pipe*/<br>close(fd[0]);<br>}<br>else<br>{<br>/*Parent process closes up out put side of pipe*/<br>close(fd[1]);<br>}..<br>}<p> 正如前面提到的,一但創建了管道之后,管道所使用的文件描述符就和正常文件的文件描述符一樣了。<p>#include<stdio.h><br>#include<unistd.h><br>#include<sys/types.h><br>intmain(void)<br>{<br>intfd[2],nbytes;<br>pid_tchildpid;<br>charstring[]="Hello,world!\n";<br>charreadbuffer[80];<br>pipe(fd);<br>if((childpid=fork())==-1)<br>{<br>perror("fork");<br>exit(1);<br>}<br>if(childpid==0)<br>{<br>/*Child process closes up in put side of pipe*/<br>close(fd[0]);<br>/*Send"string"through the out put side of pipe*/<br>write(fd[1],string,strlen(string));<br>exit(0);<br>}<br>else<br>{<br>/*Parent process closes up out put side of pipe*/<br>close(fd[1]);<br>/*Readinastringfromthepipe*/<br>nbytes=read(fd[0],readbuffer,sizeof(readbuffer));<br>printf("Receivedstring:%s",readbuffer);<br>}<br>return(0);<br>}<p> 一般情況下,子進程中的文件描述符將會復制到標準的輸入和輸出中。這樣子進程可以使用exec()執行另一個程序,此程序繼承了標準的數據流。<p><p><p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I239" ID="I239"></A><center><b><font size=+2>dup()</font></b></center><br>系統調用:dup();<br>原型:intdup(intoldfd);<br>返回:如果系統調用成功,返回新的文件描述符<br>如果系統調用失敗,返回-1:errno=EBADF(oldfd不是有效的文件描述符)<br>EBADF(newfd超出范圍)<br>EMFILE(進程的文件描述符太多)<p> 注意舊文件描述符oldfd沒有關閉。雖然舊文件描述符和新創建的文件描述符可以交換使用,但一般情況下需要首先關閉一個。系統調用dup()使用的是號碼最小的空閑的文件描述符。<p>再看下面的程序:<br>..<br>childpid=fork();<br>if(childpid==0)<br>{<br>/*Close up standard input of the child*/<br>close(0);<br>/*Dup licate the input side of pipe to stdin*/<br>dup(fd[0]);<br>execlp("sort","sort",NULL);<br>.<br>}<br> 因為文件描述符0(stdin)被關閉,所以dup()把管道的輸入描述符復制到它的標準輸入中。這樣我們可以調用execlp(),使用sort程序覆蓋子進程的正文段。因為新創建的程序從它的父進程中繼承了標準輸入/輸出流,所以它實際上繼承了管道的輸入端作為它的標準輸入端。現在,最初的父進程送往管道的任何數據都將會直接送往sort函數。<p><p><p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I240" ID="I240"></A><center><b><font size=+2>dup2()</font></b></center><br>系統調用:dup2();<br>原型:intdup2(intoldfd,intnewfd);<br>返回值:如果調用成功,返回新的文件描述符<br>如果調用失敗,返回-1:errno=EBADF(oldfd不是有效的文件描述符)<br>EBADF(newfd超出范圍)<br>EMFILE(進程的文件描述符太多)<br>注意dup2()將關閉舊文件描述符。<p> 使用此系統調用,可以將close操作和文件描述符復制操作集成到一個系統調用中。另外,此系統調用保證了操作的自動進行,也就是說操作不能被其他的信號中斷。這個操作將會在返回系統內核之前完成。如果使用前一個系統調用dup(),程序員不得不在此之前執行一個close()操作。請看下面的程序:<br>..<p>childpid=fork();<br>if(childpid==0)<br>{<br>/*Close stdin,dup licate the input side of pipe to stdin*/<br>dup2(0,fd[0]);<br>execlp("sort","sort",NULL);<br>..<br>}<p><p><p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I241" ID="I241"></A><center><b><font size=+2>popen()和pclose()</font></b></center><br>如果你認為上面創建和使用管道的方法過于繁瑣的話,你也可以使用下面的簡單的方法:<p>庫函數:popen()和pclose();<br>原型:FILE*popen(char*command,char*type);<br>返回值:如果成功,返回一個新的文件流。<br>如果無法創建進程或者管道,返回NULL。<br> 此標準的庫函數通過在系統內部調用pipe()來創建一個半雙工的管道,然后它創建一個子進程,啟動shell,最后在shell上執行command參數中的命令。管道中數據流的方向是由第二個參數type控制的。此參數可以是r或者w,分別代表讀或寫。但不能同時為讀和寫。在Linux系統下,管道將會以參數type中第一個字符代表的方式打開。所以,如果你在參數type中寫入rw,管道將會以讀的方式打開。<p> 雖然此庫函數的用法很簡單,但也有一些不利的地方。例如它失去了使用系統調用pipe()時可以有的對系統的控制。盡管這樣,因為可以直接地使用shell命令,所以shell中的一些通配符和其他的一些擴展符號都可以在command參數中使用。<br>使用popen()創建的管道必須使用pclose()關閉。其實,popen/pclose和標準文件輸入/輸出流中的fopen()/fclose()十分相似。<p><br>庫函數:pclose();<br>原型:intpclose(FILE*stream);<br>返回值:返回系統調用wait4()的狀態。<br>如果stream無效,或者系統調用wait4()失敗,則返回-1。<br> 注意此庫函數等待管道進程運行結束,然后關閉文件流。庫函數pclose()在使用popen()創建的進程上執行wait4()函數。當它返回時,它將破壞管道和文件系統。<br> 在下面的例子中,用sort命令打開了一個管道,然后對一個字符數組排序:<p>#include<stdio.h><br>#defineMAXSTRS5<br>intmain(void)<br>{<br>intcntr;<br>FILE*pipe_fp;<br>char*strings[MAXSTRS]={"echo","bravo","alpha",<br>"charlie","delta"};<br>/*Createonewaypipelinewithcalltopopen()*/<br>if((pipe_fp=popen("sort","w"))==NULL)<br>{<br>perror("popen");<br>exit(1);<br>}<br>/*Processingloop*/<br>for(cntr=0;cntr<MAXSTRS;cntr++){<br>fputs(strings[cntr],pipe_fp);<br>fputc('\n',pipe_fp);<br>}<br>/*Closethepipe*/<br>pclose(pipe_fp);<br>return(0);<br>}<br>因為popen()使用shell執行命令,所以所有的shell擴展符和通配符都可以使用。此外,它還可以和popen()一起使用重定向和輸出管道函數。再看下面的例子:<br>popen("ls~scottb","r");<br>popen("sort>/tmp/foo","w");<br>popen("sort|uniq|more","w");<br>下面的程序是另一個使用popen()的例子,它打開兩個管道(一個用于ls命令,另一個用于<br>sort命令):<br>#include<stdio.h><br>intmain(void)<br>{<br>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -