版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度出口企業(yè)出口貨物報關(guān)單據(jù)與憑證管理合同3篇
- 二零二五年餐飲項(xiàng)目合伙經(jīng)營合同范本3篇
- 2025年度智能化工廠租賃合同涉及土地使用權(quán)及配套設(shè)施4篇
- 二零二四年臨時工勞動保障與勞動法實(shí)施合同3篇
- 專屬2024版企業(yè)人力外包協(xié)議樣本版B版
- 2024鋁合金門窗生產(chǎn)與安裝一體化工程合同3篇
- 2025年度企業(yè)級“師帶徒”人才孵化項(xiàng)目合同3篇
- 專業(yè)勞務(wù)派遣協(xié)議樣本2024版B版
- 街道黨工委知識培訓(xùn)課件
- 2025年度商務(wù)辦公空間租賃安全合同文本4篇
- GB/T 4167-2024砝碼
- 老年人視覺障礙護(hù)理
- 《腦梗塞的健康教育》課件
- 《請柬及邀請函》課件
- 中小銀行上云趨勢研究分析報告
- 遼寧省普通高中2024-2025學(xué)年高一上學(xué)期12月聯(lián)合考試語文試題(含答案)
- 青海原子城的課程設(shè)計
- 常州大學(xué)《新媒體文案創(chuàng)作與傳播》2023-2024學(xué)年第一學(xué)期期末試卷
- 麻醉蘇醒期躁動患者護(hù)理
- 英語雅思8000詞匯表
- 小學(xué)好詞好句好段摘抄(8篇)
評論
0/150
提交評論