《網(wǎng)絡(luò)應(yīng)用程序設(shè)計》課件第5章 進(jìn)程間通信及實(shí)現(xiàn)方法_第1頁
《網(wǎng)絡(luò)應(yīng)用程序設(shè)計》課件第5章 進(jìn)程間通信及實(shí)現(xiàn)方法_第2頁
《網(wǎng)絡(luò)應(yīng)用程序設(shè)計》課件第5章 進(jìn)程間通信及實(shí)現(xiàn)方法_第3頁
《網(wǎng)絡(luò)應(yīng)用程序設(shè)計》課件第5章 進(jìn)程間通信及實(shí)現(xiàn)方法_第4頁
《網(wǎng)絡(luò)應(yīng)用程序設(shè)計》課件第5章 進(jìn)程間通信及實(shí)現(xiàn)方法_第5頁
已閱讀5頁,還剩90頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

第5章進(jìn)程間通信及實(shí)現(xiàn)方法

5.1管道

5.2命名管道5.3消息通信5.4共享內(nèi)存5.5信號燈5.6UNIX域套接字習(xí)題5.1管道 1.管道的定義

Linux系統(tǒng)提供了幾種通信方式,如消息隊(duì)列、信號量及共享內(nèi)存等。它們有的需要使用較多的存儲資源才能進(jìn)行信息傳遞,有的不適合在進(jìn)程間進(jìn)行大量的信息傳遞。而管道機(jī)構(gòu)能夠?yàn)檫M(jìn)程之間大量信息的傳輸提供通道。管道是Linux系統(tǒng)利用文件系統(tǒng)為基礎(chǔ)構(gòu)成的一種進(jìn)程間通信的機(jī)構(gòu)。它有以下幾個特點(diǎn):

(1)管道是一個單向的通信信道,發(fā)送進(jìn)程能以比較簡單的方式,把要發(fā)送的信息以信息流的方式寫入信道,不需要考慮對每次傳送信息長度的限制。 (2)接收進(jìn)程從信道按需取用信息,不必考慮長度限制。

(3)發(fā)送和接收進(jìn)程的實(shí)施相互協(xié)調(diào),其中發(fā)送和接收進(jìn)程相互協(xié)調(diào)指的是: ①

發(fā)送和接收進(jìn)程對通信信道的使用是互斥的,一個進(jìn)程利用信道進(jìn)行讀或?qū)懖僮鲿r,其他進(jìn)程就不能用,當(dāng)這個進(jìn)程不用信道時,其他的進(jìn)程才能使用。 ②

發(fā)送和接收進(jìn)程都能了解對方是否存在。 ③

發(fā)送和接收進(jìn)程同步。即接收進(jìn)程在信道中已有信息時,才接收信息;發(fā)送進(jìn)程要在信道空閑時,才能發(fā)送信息。

管道是由系統(tǒng)調(diào)用pipe建立的,其調(diào)用格式為:

#include<unistd.h> intpipe(intfd[2]);

參數(shù)fd指向兩個文件描述符;fd[0]返回管道讀通道打開的文件號,用于讀;fd[1]返回管道的寫通道打開的文件號,用于寫,如圖5-1所示。圖5-1管道工作示意圖 2.管道的共享使用

進(jìn)程利用pipe系統(tǒng)調(diào)用生成管道后,通常接著就要創(chuàng)建一個或幾個子進(jìn)程,管道被父子進(jìn)程共享。每個進(jìn)程可以用類似文件讀寫操作方式對管道進(jìn)行存取操作。一般情況下,一個管道最好為兩個進(jìn)程專用,一個進(jìn)程只用其發(fā)送端,另一個進(jìn)程只用其接收端。

當(dāng)需要進(jìn)行雙向數(shù)據(jù)傳輸時,必須建立兩個管道。每個管道負(fù)責(zé)一個方向的數(shù)據(jù)傳輸。它的操作過程如下:

●創(chuàng)建兩個管道:管道1和管道2;

●調(diào)用fork()產(chǎn)生子進(jìn)程;

●父進(jìn)程關(guān)閉管道1的讀端; ●父進(jìn)程關(guān)閉管道2的寫端;

●子進(jìn)程關(guān)閉管道1的寫端; ●子進(jìn)程關(guān)閉管道2的讀端。 下面的例子說明了管道機(jī)構(gòu)的建立和使用。

main() { charparstr[]={"thedatasfromparent.\n"};

charchistr[]={"thedatasfromchild.\n"};

intchpipe1[2],

chpipe2[2];

charbuff[100]; if(pipe(chpipe1)<0)err_sys("pipe1createrror");

if(pipe(chpipe2)<0)err_sys("pipe2createrror");

if(fork()){/*在父進(jìn)程中

*/close(chpipe1[0]);

close(chpipe2[1]); if(write(chpipe1[1],parstr,

sizeof(parstr))!=sizeof(parstr))err_sys("senderror");

close(chpipe1[1]);

if(read(chpipe2[0],buff,100)<=0)err_sys("receiveerror");

elseprintf("parentprocess:%s\n",buff);

}/*fork*/

else{close(chpipe1[1]);

/*

在子進(jìn)程中

*/close(chpipe2[0]);

if(read(chpipe1[0],buff,100)<=0)err_sys("receiveerror");

elseprintf("childprocess:%s\n",buff);

if(write(chpipe2[1],chistr,sizeof(chistr))!=sizeof(chistr))err_sys("senderror");

close(chpipe2[1]);

}}

運(yùn)行結(jié)果

childprocess:

thedatasfromparent. parentprocess:

thedatasfromchild.

程序中信息的傳輸過程如圖5-2所示。圖5-2利用管道進(jìn)行通信

首先,進(jìn)程創(chuàng)建兩個管道chpipe1和chpipe2,然后利用

fork產(chǎn)生一子進(jìn)程。如果子進(jìn)程創(chuàng)建成功,父進(jìn)程利用管道chpipe1[1]作寫入端,用管道chpipe2[0]作讀出端,關(guān)閉管道端chpipe1[0]和chpipe2[1]。子進(jìn)程用管道chpipe1[0]作為讀出端,用管道chpipe2[1]作為寫入端,關(guān)閉管道端chpipe1[1]和chpipe2[0],兩者進(jìn)行通信。就每個管道而言,均是單向管道,其數(shù)據(jù)信息只按一個方向流動。就每個進(jìn)程而言,信息的傳送是雙向的,即能發(fā)送又能接收。 如果將上面程序改為在顧客—服務(wù)員模式上運(yùn)行,上例程序可改寫為:由main()產(chǎn)生一管道,然后產(chǎn)生一個子進(jìn)程。原進(jìn)程運(yùn)行顧客程序,而子進(jìn)程運(yùn)行服務(wù)員程序。

main(){intchpipe1[2],

chpipe2[2];

intpid;

if(pipe(chpipe1)<0)err_sys("pipe1createrror");

if(pipe(chpipe2)<0)err_sys("pipe2createrror"); if((pid=fork())<0){err_sys("can'tfork");} else if(pid){/*

在父進(jìn)程中

*/close(chpipe1[0]);

close(chpipe2[1]);

pro_client(chpipe2[0],chpipe1[1]); while(wait()!=pid);

/*

等待原子進(jìn)程運(yùn)行返回

*/close(chpipe1[1]);

close(chpipe2[0]);

}else{/*

在子進(jìn)程中

*/close(chpipe1[1]);

close(chpipe2[0]); pro_server(chpipe1[0],chpipe2[1]);

close(chpipe1[0]);

close(chpipe2[1]);

}exit(0);

}

其中顧客子程序?yàn)椋?/p>

#include<stdlio.h> pro_client(rfd,

wfd) intrfd;

intwfd;

{charparstr[]={"parfile.dat"};

intn;

charbuff[100];

n=strlen(parstr);

if(write(wfd,parstr,n)!=n)err_sys("client:

senderror");

if(read(rfd,buff,100)<=0)err_sys("client:

receiveerror");

elseprintf("parentprocess:%s\n",buff);

}

服務(wù)員子程序?yàn)椋?/p>

#include<stdlio.h> pro_server(rfd,

wfd) intrfd;

intwfd;

{ intn;

charbuff[1024],

errbuff[64];

intn,

fd;

/*

IPC描述符中讀文件名

*/ if((n=read(rfd,buff,1024))<=0) err_sys("server:filereadhaserror");

buff[n]='\0';

if((fd=open(buff,0))<0) { strcpy(errbuf,"filecan'topen.\n");

strcat(buff,errbuff);

n=strlen(buff);

if(write(wfd,buff,n)!=n)err_sys("server:messagesenderror") } else{while((n=read(fd,buff,1024))>0)if(wirte(wfd,buff,n)!=n)err_sys("server:messagesenderror");

if(n<0)err_sys("server:readerror");

}}

程序pro_client中的父進(jìn)程通過管道發(fā)送文件名給子進(jìn)程,pro_server中的子進(jìn)程通過管道讀文件名,如果接收的文件名有錯,文件打不開,則將錯誤信息回傳給client方(父進(jìn)程);否則,讀文件中的信息并將文件信息回傳給client。 利用管道的只能在父子進(jìn)程間的通信,對于兩個完全不相關(guān)的進(jìn)程不能采用管道進(jìn)行通信,這是管道的一個特點(diǎn)。5.2命名管道

命名管道與管道不同之處在于,它與一個路徑名關(guān)聯(lián),以FIFO的文件形式存在于文件系統(tǒng)中。這樣,即使與FIFO的創(chuàng)建進(jìn)程無親緣關(guān)系的進(jìn)程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信。 系統(tǒng)調(diào)用mkfifo()創(chuàng)建命名管道:

intmkfifo(constchar*pathname,

mode_tmode);

該函數(shù)的pathname是一個路徑名,也就是創(chuàng)建后命名管道的名字,mode打開文件的模式,與打開普通文件的open()函數(shù)中文件操作模式參數(shù)相似。如果mkfifo的第一個參數(shù)是一個已經(jīng)存在的路徑名時,則返回類型為EEXIST的錯誤;如果返回該錯誤,那么只要調(diào)用打開FIFO的函數(shù)就可以了。生成了命名管道后,就可以使用一般的文件I/O函數(shù)如open、close、read、write等來對它進(jìn)行操作。

1.通過命名管道發(fā)信息

#include<sys/types.h>

#include<sys/stat.h>

#include<errno.h>

#include<fcntl.h>

#defineFIFO_SERVER"/tmp/fifos_server" main(intargc,char**argv) /*

參數(shù)為即將寫入的字節(jié)數(shù)

*/ { intfd,n; charw_buf[512]; intwrite_num; n=100; memset(w_buf,0,512); strncpy(w_buf,argv[1],n); if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) printf("can’tcreatefifos_server\n"); /*

設(shè)置非阻塞標(biāo)志

*/ fd=open(FIFO_SERVER, O_WRONLY|O_NONBLOCK,0); write_num=write(fd,w_buf,n);if(write_wnum==-1) { if(errno==EAGAIN) printf("writetofifoerror;trylater\n"); }else printf("realwritenumis%d\n",

write_num);}

首先,調(diào)用函數(shù)mkfifo創(chuàng)建命名管道FIFO_SERVER,如果創(chuàng)建成功,則將該管道設(shè)置為非阻塞狀態(tài)。向FIFO_SERVER中寫消息,如果成功,則輸出這些消息,否則,輸出出錯信息。

2.通過命名管道收信息

#include<sys/types.h>

#include<sys/stat.h>

#include<errno.h>

#include<fcntl.h>

#defineFIFO_SERVER"/tmp/fifos_server" main(intargc,char**argv){ charr_buf[4096*2]; intfd; intr_size; intret_size; r_size=atoi(argv[1]); printf("inputrealreadbytes%d\n",r_size); memset(r_buf,0,sizeof(r_buf)); fd=open(FIFO_SERVER, O_RDONLY|O_NONBLOCK,0); /*fd=open(FIFO_SERVER,O_RDONLY,0);在此 處可以把讀程序編譯成兩個

不同版本:阻塞版本及非阻塞版本

*/ if(fd==-1) { printf("open%sforreaderror\n"); exit(); } while(1) { memset(r_buf,0,sizeof(r_buf)); ret_size=read(fd,r_buf,r_size); if(ret_size==-1) if(errno==EAGAIN) printf("nodataavlaible\n");printf("realreadbytes%d\n",ret_size); sleep(1); } pause(); unlink(FIFO_SERVER);}

首先,調(diào)用mkfifo創(chuàng)建命名管道FIFO_SERVER,如果創(chuàng)建成功,則以讀方式打開該管道[wei2],并將該管道設(shè)置為非阻塞狀態(tài)。通過鍵盤輸入希望讀取的字節(jié)數(shù),從FIFO_SERVER中讀取消息,如果成功,則輸出實(shí)際讀到的字節(jié)數(shù),否則,輸出出錯信息。 下面介紹UNIX系統(tǒng)V提供的三種進(jìn)程間通信方式。5.3消息通信

利用消息通信,進(jìn)程可以將具有一定格式的消息發(fā)送給任意進(jìn)程。UNIX系統(tǒng)V為消息通信提供了四個系統(tǒng)調(diào)用。

1.生成一個消息隊(duì)列

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> intmsgget(key_tkey,intflags) /*

獲取消息隊(duì)列標(biāo)識數(shù)

*/ /*key:消息隊(duì)列關(guān)鍵字,長整型

*/*flags:操作標(biāo)志

*/

其中,參數(shù)key是通信雙方約定的消息隊(duì)列關(guān)鍵字,它是一個非負(fù)長整型。UNIXIPC通信機(jī)構(gòu)將根據(jù)它生成一個消息隊(duì)列,并返回一個隊(duì)列標(biāo)識數(shù)ID。隊(duì)列ID與文件描述字相似,但是進(jìn)程只要知道該值就可適應(yīng)它,不必像文件ID那樣只有通過繼承才能對同一個文件操作。當(dāng)指定關(guān)鍵字的消息隊(duì)列存在時,msgget就簡單地返回該隊(duì)列的ID。

參數(shù)flags類似于打開和創(chuàng)建文件時的第二個參數(shù)o_flags和mode的組合。 2.向消息隊(duì)列發(fā)送一個消息

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> intmsgsnd(intqid,structmsgbuf*buf,intnbytes,intflags)

其中,參數(shù)qid是消息隊(duì)列ID,nbytes是消息正文的長度,flags是發(fā)送標(biāo)志。如果flags為零,則當(dāng)消息隊(duì)列滿時進(jìn)程阻塞自己;如果flags中IPC以NOWAIT(04000)置位,則消息隊(duì)列滿時msgsnd返回-1,不阻塞進(jìn)程。

參數(shù)buf指定一個由用戶定義的消息結(jié)構(gòu),其基本格式如下:

structmsgtype{ longmtype;

chartext[NBYTES];

}; 其中,mtype是正整數(shù),text[]是長度為NBYTES的正文,其長度是有限制的。

3.從消息隊(duì)列中接收一個消息

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> intmsgrcv(intqid,structmsgbuf*buf,intnbytes,mtype,intflags)

其中,msgrcv中的參數(shù)與msgsnd類似,如果flags中的MSG_NOERROR置位,則允許所接收的長度nbytes小于消息正文長度。buf所指的空間大小為不包括mbyte的最大消息正文長度,實(shí)際接收的消息長度由msgrcv返回值指出。當(dāng)mtype=0時,接收消息隊(duì)列中最早的消息,而不管消息的類型是什么。 4.消息隊(duì)列的控制

#include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> intmsgctl(intqid,intcmd,structmsqid_ds*sbuf)

其中,msgctl詢問隊(duì)列ID為qid的消息隊(duì)列的各種特性或者對其進(jìn)行相應(yīng)的控制。msqid_ds是消息隊(duì)列定義的控制結(jié)構(gòu),包括存取權(quán)限結(jié)構(gòu)、隊(duì)列容量、進(jìn)程標(biāo)識和時間等信息。當(dāng)cmd的取值為IPC_RMID(值為0)時,刪除指定的消息隊(duì)列,釋放消息隊(duì)列標(biāo)識符;為IPC_SET(值為1)時,將sbuf中的控制信息寫到消息隊(duì)列控制結(jié)構(gòu)中;為IPC_STAT(值為2)時,將消息隊(duì)列控制結(jié)構(gòu)中的消息寫到sbuf中。5.4共享內(nèi)存

在UNIX中,進(jìn)程間傳遞數(shù)據(jù)的最快方法是讓一些相關(guān)進(jìn)程直接共享某些內(nèi)存區(qū)域,而系統(tǒng)V支持任意數(shù)據(jù)進(jìn)程對內(nèi)存的共享。這是一種最快捷高效的方式,系統(tǒng)在內(nèi)存中指定一個區(qū)域作為共享存儲區(qū),建立一張段表進(jìn)行管理,各進(jìn)程可以申請其中的一個存儲段,并在申請時提供關(guān)鍵字。若申請的存儲區(qū)已經(jīng)被其他進(jìn)程所有,則系統(tǒng)向申請進(jìn)程返回關(guān)鍵字,該存儲區(qū)就連接到了進(jìn)程的邏輯地址空間,此后進(jìn)程就可以直接存取共享存儲區(qū)中的數(shù)據(jù)了;若申請的存儲段尚未分配,則系統(tǒng)會按照申請者的要求分配存儲段,并在段表中加入該進(jìn)程的信息。一個進(jìn)程可以申請多個存儲段,使用共享存儲區(qū)進(jìn)行通信時進(jìn)程間的互斥或同步要靠其他的機(jī)構(gòu)來解決。

圖5-3給出了兩個進(jìn)程P1、P2共享一個關(guān)鍵字為key1的共享存儲區(qū)的情況。

圖5-3進(jìn)程P1、P2共享一個存儲區(qū)

共享內(nèi)存設(shè)計的頭文件和系統(tǒng)調(diào)用是:

#include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> 1.創(chuàng)建一個共享內(nèi)存段

intshmget(key_tkey,intnbytes,intflags)

三個參數(shù)key、nbytes和flags的含義與消息通信中的系統(tǒng)調(diào)用msgget類似。key取值IPC_PRIVATE時,新創(chuàng)建的共享內(nèi)存段的關(guān)鍵字由系統(tǒng)分配。shmget創(chuàng)建共享內(nèi)存段成功時,初始化相應(yīng)的控制信息,返回該共享段的描述字ID。 2.將共享內(nèi)存段映射到進(jìn)程的虛擬地址空間

char*shmat(intsegid,char*addr,intflags) shmat將標(biāo)識字為segid的共享內(nèi)存段映射到由addr參數(shù)指定的進(jìn)程虛擬地址空間。該地址空間可通過brk或者sbrk系統(tǒng)調(diào)用動態(tài)分配而得到。如果不關(guān)心映射內(nèi)存的地址,則可以置addr為0,讓系統(tǒng)選擇一個可用地址。Shmat調(diào)用成功后返回共享內(nèi)存段在進(jìn)程虛擬地址空間的首地址。

3.解除共享內(nèi)存段的映射

intshmdt(char*addr)/*

共享內(nèi)存段虛擬地址

*/

參數(shù)addr是相應(yīng)shmdt調(diào)用的返回值。Shmdt調(diào)用成功時,內(nèi)存段的訪問計數(shù)減1,返回值為0。

4.共享內(nèi)存段控制

intshmct(intsegid,intcmd,structshmid_ds*sbuf)

其中,segid為標(biāo)識符,cmd為控制字,sbuf為指向共享內(nèi)存段控制結(jié)構(gòu)指針。

參數(shù)cmd取值為SHM_LOCK時,將共享段鎖定在內(nèi)存,禁止換出(超級用戶才具有此權(quán)限);為SHM_UNLOCK時,與LOCK相反(超級用戶才具有此權(quán)限);為IPC_RMID、ICP_STAT和IPC_SET時,類似于msgctl中的定義,其中IPC_RMID標(biāo)志所對應(yīng)的存儲段為“可釋放”。

下面介紹關(guān)鍵字的創(chuàng)建函數(shù)ftok。

key_t

ftok(char

*pathname,char

proj);

函數(shù)根據(jù)pathname和proj來創(chuàng)建一個關(guān)鍵字,該鍵值與pathname相對應(yīng)。例如:

#defineMSG_FILE"server.c" key_tkey; if((key=ftok(MSG_FILE,'a'))==-1) { fprintf(stderr,"CreatKeyError:%s\a\n",strerror(errno)); exit(1); }

關(guān)鍵字除了可以用ftok創(chuàng)建,也可以使用IPC_PRIVATE表明由系統(tǒng)選擇一個關(guān)鍵字。例如:

/*

使用IPC_PRIVATE表示由系統(tǒng)選擇一個關(guān)鍵字來創(chuàng)建

*/ /*

創(chuàng)建以后信號燈的初始值為0*/ if((semid=semget(IPC_PRIVATE,1,PERMS))==-1) { fprintf(stderr,"[%d]:AcessSemaphoreError:%s\n\a",

getpid(),strerror(errno)); exit(1); }

父子進(jìn)程利用共享內(nèi)存進(jìn)行通信的程序示例如下:#include

<stdio.h>#include

<string.h>#include

<errno.h>#include

<unistd.h>#include

<sys/stat.h>#include

<sys/types.h>#include

<sys/ipc.h>#include

<sys/shm.h>#define

PERM

S_IRUSR|S_IWUSRint

main(int

argc,char

**argv){int

shmid;char

*p_addr,*c_addr;char*name="/dev/shm/myshm1";

if(argc!=2){fprintf(stderr,"Usage:%s\n",argv[0]);exit(1);}key=ftok(name,0);if(key==-1)perror("ftokerror");

shmid=shmget(key,

1024,0777); if(shmid==-1){perror("Create

Share

Memory

Error:");exit(1);}if(fork()){/*

父進(jìn)程將參數(shù)寫入到共享內(nèi)存

*/p_addr=shmat(shmid,0,0);memset(p_addr,'\0',1024);strncpy(p_addr,argv[1],1024);}else{/*

子進(jìn)程從共享內(nèi)存讀信息

*/c_addr=shmat(shmid,0,0);printf("Client

get

%s",c_addr);exit(0);}}

在使用一個共享內(nèi)存之前,應(yīng)調(diào)用shmat得到共享內(nèi)存的起始地址,結(jié)束后再用shmdt斷開這個內(nèi)存。在這個程序中,首先創(chuàng)建共享內(nèi)存,然后產(chǎn)生一個子進(jìn)程。父進(jìn)程將參數(shù)寫入共享內(nèi)存,之后子進(jìn)程從共享內(nèi)存讀出寫入的內(nèi)容。5.5信號燈

進(jìn)程間的互斥和同步可以利用P、V操作實(shí)現(xiàn),但是UNIX系統(tǒng)并沒有直接向用戶提供這兩個操作,而是提供了一組有關(guān)信號燈的系統(tǒng)調(diào)用。在系統(tǒng)V中的信號燈機(jī)制的功能比一般信號燈要強(qiáng),管理和使用也比較復(fù)雜。用戶可以一次對一組信號燈進(jìn)行相同或者不同的操作。

系統(tǒng)V中有關(guān)信號燈的頭文件和系統(tǒng)調(diào)用。

#include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> 1.創(chuàng)建一個信號燈組

intsemget(key_tkey,intnsems,intflags)

其中,key為信號燈組關(guān)鍵字;nsems為信號燈個數(shù);flags為操作標(biāo)志。 當(dāng)key為IPC_PRIVATE時,信號燈組關(guān)鍵字由系統(tǒng)選擇。flags決定信號燈組的創(chuàng)建方式和權(quán)限,其取值和含義與msgget中的flags類似。semget調(diào)用成功時,初始化相應(yīng)的控制塊信息,返回信號燈組標(biāo)識數(shù)。

下面是一個打開和創(chuàng)建信號量集的程序段:

intcreat_semaphore_set(key_tkeyval,

intnumsems) { intsid; if(!numsems) return(-1); if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1) { return(-1); } return(sid); };

其中,keyval為鍵值,用系統(tǒng)調(diào)用ftok()返回,參數(shù)numsems指出了一個新的信號量集中應(yīng)該創(chuàng)建的信號量的個數(shù)。信號量集中最多的信號量的個數(shù)是在linux/sem.h中定義的:

#defineSEMMSL32 /*<=512maxnumofsemaphoresperid*/ 2.對信號燈組的控制

intsemop(intsid,structsembuf**ops,unsignednops)

其中,intsid為信號燈組標(biāo)識符;ops是對信號燈組進(jìn)行操作的數(shù)據(jù)結(jié)構(gòu);nops為操作個數(shù)。semop根據(jù)sembuf型的結(jié)構(gòu)數(shù)組對標(biāo)識數(shù)為sid的信號燈組中的信號燈進(jìn)行塊操作。在sembuf結(jié)構(gòu)中定義了對編號為sem_num的信號燈要進(jìn)行的操作。 structsembuf{ shortsem_num;

/*

信號燈編號,從0開始

*/ shortsem_op;

/*

要執(zhí)行的操作

*/ shortsem_flg;

/*

操作標(biāo)志

*/ }; 如果sem_op為正數(shù),則信號量將加上它的值。這對應(yīng)進(jìn)程釋放信號量控制的資源。 如果sem_op為負(fù)數(shù),則信號量將減去它的值。IPC_NOWAIT是否被置位決定對信號燈進(jìn)行的操作,如果沒有使用IPC_NOWAIT,那么調(diào)用進(jìn)程將進(jìn)入睡眠狀態(tài),直到信號量控制的資源可以使用為止。

如果sem_op為0,那么對信號燈進(jìn)行測試。這在一個進(jìn)程等待完全空閑的資源時使用。

3.信號燈控制

intsemctl(intsid,intsnum,intcmd,unionsemunarg); 其中,sid、snum和cmd分別為信號燈組標(biāo)識、信號燈編號和控制信令。 聯(lián)合semun的格式為:

unionsemun{ intval;

structsemid_ds*buf;

/*

指針信號燈集控制塊的指針

*/ ushort*array;

};

在semctl調(diào)用中,系統(tǒng)根據(jù)cmd的主要取值及相關(guān)的arg含義為:

GETVAL:將信號燈(sid,snum)的值,存入arg.val。

SETVAL:將信號燈(sid,snum)的值置為arg.val,用于對信號燈初始化。

GETALL:將信號燈組(sid)中所有信號燈的值取到arg.array[]中。

SETALL:將信號燈組(sid)中所有信號燈的值設(shè)置為arg.array[]中的值。

IPC_STAT:將信號燈組(sid)的狀態(tài)信息取到buf結(jié)構(gòu)中。

IPC_SET:將信號燈組(sid)的狀態(tài)信息設(shè)置為buf結(jié)構(gòu)中的信息。

IPC_RMID:刪除信號燈組的標(biāo)識數(shù)。

下面的程序段介紹了semctl使用方法:

/*

使用getval命令返回信號量的值,調(diào)用中的最后一個參數(shù)被忽略

*/ intget_sem_val(intsid,intsemnum)

{ return(semctl(sid,semnum,GETVAL,0)); } /*

下面的程序可以用來初始化一個新的信號量值

*/ voidinit_semaphore(intsid,intsemnum,intinitval) { unionsemunsemopts; semopts.val=initval; semctl(sid,semnum,SETVAL,semopts); } #defineMAX_PRINTERS5 voidprinter_usage(intsid) { inti; for(i=0;i<MAX_PRINTERS;i++) printf("Printer%d:%d",i,get_sem_val(sid,i)); }

其中,sid為信號燈組標(biāo)識,semnum為信號燈編號。函數(shù)get_sem_val返回信號量的值,函數(shù)init_semaphore設(shè)置信號量的值為initval。函數(shù)printer_usage打印出信號燈組sid的信號量的值。5.6UNIX域套接字 UNIX域協(xié)議不是一個真正的網(wǎng)絡(luò)通信協(xié)議,它提供同一臺計算機(jī)上的進(jìn)程之間進(jìn)行通信的手段。UNIX域套接字與同一臺機(jī)器上的域套接字相連,每一個與套接字的新連接都產(chǎn)生一個新的通信管道。同一臺機(jī)器上的客戶機(jī)和服務(wù)器進(jìn)程可以通過UNIX域套接字進(jìn)行通信。與TCP套接字相比,使用UNIX域套接字通信的效率更高。UNIX域套接字經(jīng)常被用來代替命名管道實(shí)現(xiàn)很多重要服務(wù)中的IPC,也可以用socketpair()來得到非命名Unix域套接字,與非命名管道類似。 1.命名UNIX域套接字 這種域套接字需要有自己的套接字地址,即UNIX域協(xié)議地址結(jié)構(gòu)sockaddr_un:

#include<sys/un.h> structsockaddr_un { shortintsun_family; charsun_path[104]; }

其中,sun_family應(yīng)設(shè)置為AF_UNIX,sun_path成員變量用來指定Linux文件系統(tǒng)的一個文件路徑名。也就是說,UNIX域協(xié)議使用路徑名標(biāo)識客戶機(jī)和服務(wù)器。UNIX域套接字的定義方式為:

intsocket(AF_UNIX,SOCK_STREAM,0) intsocket(AF_UNIX,SOCK_DGRAM,0)

當(dāng)服務(wù)器或客戶機(jī)綁定一個UNIX域套接字地址時,按照該路徑名創(chuàng)建一個文件。該文件是一個特殊文件,不同于普通的Linux文件,無法用open等系統(tǒng)調(diào)用打開或讀、寫該文件。如果在操作系統(tǒng)提示符下,鍵入命令“l(fā)s–F”查看該文件,可以發(fā)現(xiàn)該文件是一個管道類型的文件,其文件名的末尾顯示一條“|”線。

函數(shù)socket用來創(chuàng)建UNIX域套接字有以下幾點(diǎn)要注意:

(1)創(chuàng)建的UNIX域套接字是有名的。創(chuàng)建的域套接字需要綁定未占用的UNIX域套接字相連,服務(wù)器需要綁定這個地址,客戶機(jī)以該地址呼叫連接實(shí)現(xiàn)與服務(wù)器的連接。UNIX域套接字使用的文件名必須是絕對路徑名。

(2)服務(wù)器使用UNIX域套接字接收多個客戶端的連接請求。同TCP的套接字類似,服務(wù)器在綁定地址后,利用listen偵聽連接請求,利用accept函數(shù)取得一個請求,并返回建立的連接。 (3)客戶機(jī)調(diào)用函數(shù)connect呼叫同服務(wù)器得到連接??蛻魴C(jī)的connect所使用的套接字地址應(yīng)當(dāng)是一個已打開的UNIX域套接字,即是服務(wù)器綁定過的地址??蛻魴C(jī)進(jìn)程應(yīng)當(dāng)擁有打開套接字地址對應(yīng)文件的權(quán)限。若客戶機(jī)發(fā)出連接請求時,偵聽隊(duì)列已滿,則connect函數(shù)的返回值為錯誤ECONNREFUSED。

#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/un.h> #include<sys/socket.h>main(intargc,char**argv){intsock_fd,new_fd;structsockaddr_unmy_addr1,my_addr2;if(argc!=2){fprintf(stderr,"usage:UNIXbindpathname\n");exit(1);} if(sock_fd=socket(AF_UNIX,

SOCK_STREAM,

0))==-1) { /*

輸出錯誤提示并退出*/ perror("socket"); exit(1); }/*將要綁定的套接字地址對應(yīng)的文件刪除*/unlike(argv[1]);bzero(&(my_addr1),

sizeof(my_addr1));my_addr1.sun_family=AF_UNIX;strncpy(my_addr1.sun_path,argv[1],sizeof(my_addr1.sun_path)-1);if(bind(sock_fd,

(structsockaddr*)&my_addr1,sizeof(structsockaddr))<0{/*

如果調(diào)用bind()失敗,則給出錯誤提示,退出*/perror(“binderror”);exit(1);}getsockname(sock_fd,(structsockaddr*)&my_addr2,&sizeof(my_addr2));printf("bindingtopathname:%s\n",

my_addr2.sun_path,

&sizeof(my_addr2));}

程序中首先創(chuàng)建一個UNIX域套接字,將要綁定的套接字地址對應(yīng)的文件刪除,如果這個文件已存在,函數(shù)bind的運(yùn)行將出錯。利用函數(shù)strncpy從命令行參數(shù)中取得套接字地址,調(diào)用bind函數(shù)以套接字地址創(chuàng)建一個文件,利用函數(shù)getsockname獲得綁定地址,并加以顯示。 下面給出利用UNIX域套接字進(jìn)行客戶機(jī)、服務(wù)器間通信的程序示例。

(1)服務(wù)器端示例:

#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h>#include<sys/type.h>#include<sys/un.h>#include<sys/socket.h>#defineBACKLOG5#defineSOCK_PATH"/tmp/exmp_sock"main(){intlen,

sin_size,n;intsock_fd,

new_fd; //s2;structsockaddr_unmy_addr,

remote_addr;charbuf[512];if(sock_fd=socket(AF_UNIX,

SOCK_STREAM,

0))==-1){/*

輸出錯誤提示并退出*/perror("socketerror"); exit(1); } my_addr.sun_family=AF_UNIX; strcpy(my_addr.sun_path,SOCK_PATH); /*

將要綁定的套接字地址對應(yīng)的文件刪除

*/ unlike(my_addr.sun_path); len=strlen(my_addr.sun_path)+sizeof(my_addr.sun_family); if(bind(sock_fd,

(structsockaddr*)&my_addr,len)<0) { /*

如果調(diào)用bind()失敗,則給出錯誤提示,退出*/

perror("binderror"); exit(1); }if(listen(sockf_d,BACKLOG)==-1){perror("listen");exit(1);}while(1){ sin_size=sizeof(structsockaddr_un);

if((new_fd=accept(sock_fd,(structsockaddr*)&remote_addr,

&sin_size))==-1){ perror("accept");continue;}do{n=recv(new_fd,buf,512,0);if(n<0){perror("recverror");done=1;}if(n==0)done=1;if(!done)if(send(new_fd,buf,n,0)<0){perror("recverror");done=1;}}while(!done);close(new_fd);}//whilereturn0;}(2)客戶機(jī)示例:#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<string.h>#include<netdb.h>#include<sys/types.h>#include<netinet/in.h>#include<sys/socket.h>#defineSOCK_PATH"exmp_sock"/*

一次所能夠接收的最大字節(jié)數(shù)

*/#defineMAXDATASIZE512intmain(){intsockfd,

n;charbuf[MAXDATASIZE];structsockaddr_untheir_addr;if((sockfd=socket(AF_UNIX,SOCK_STREAM,0))==-1)/ *

如果socket()調(diào)用出現(xiàn)錯誤,則顯示錯誤信息并退出*/

perror("socket");

exit(1); } their_addr.sin_family=AF_INET; /*

網(wǎng)絡(luò)字節(jié)順序,短整型*/ their_addr.sun_family=AF_UNIX; strcpy(their_addr.sun_path,SOCK_PATH); len=strlen(their_addr.sun_path)+sizeof(their_addr.sun_family); if(connect(so

溫馨提示

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

評論

0/150

提交評論