Linux程序間通訊(一)——管道、訊號量
一、Linux程序間通訊方式 :有六種方式在兩個程式間傳遞資訊
1、訊號( Singal )
2、管道 ( Pipe ) 及有名管道
3、訊號量 ( Semaphore )
4、共享記憶體 ( SharedMessage)
5、訊息佇列 ( MessageQueue )
6、套接字 ( Socket )
其中,共享記憶體是效率最高的。
二、具體介紹
2、管道 ( Pipe ) 及有名管道
管道分為有名和無名管道。
無名管道用於父子程序間的通訊。
a、用pipe( )建立無名管道
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<memory.h> #include<signal.h> void my_func(int); int main(){ pid_t pid; int pfd[2]; char *msg="pipe test program"; char buf[100]; int r,w; memset(buf,0,sizeof(buf)); //建立一個無名管道。一定要在fork之前建立。這樣子程序才能有同樣的一個管道 if(pipe(pfd)<0){ printf("pipe create error!\n"); exit(1); } if((pid = fork())<0){ printf("fork error!\n"); exit(1); }else if(pid == 0){ //close(pfd[0]); //此句是測試異常的時候使用 close(pfd[1]); sleep(3); if(r=read(pfd[0],buf,100)<0){ printf("read error!\n"); waitpid(pid,NULL,0); //等待子程序退出後再退出 exit(1); }else printf("child read from pipe: %s\n",buf); close(pfd[0]); }else{ signal(SIGPIPE,my_func); //當讀端不存在時系統發出SIGPIPE訊號 close(pfd[0]); sleep(1); if(w=write(pfd[1],msg,strlen(msg))<0){ printf("wirte error!\n"); exit(1); }else printf("parent send msg to child!\n"); close(pfd[1]); waitpid(pid,NULL,0); } return 0; } void my_func(int sig){ if(sig == SIGPIPE){ printf("read not exist\n"); } }
b、標準流管道: popen()
這個函式會完成:建立管道、fork()、關閉相應的檔案描述符、執行exec、執行函式中指定的命令 這一系列的操作。優點當然是省事啦,缺點必然是不靈活。popen( )返回的是檔案指標所以要用標準IO操作。其中,第二個引數:" r " 表示命令的輸出作為程式的輸入;" w " 表示程式的輸出作為命令的輸入。另外要注意用pclose( ) 關閉檔案流。兩個函數出錯
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<memory.h> #include<signal.h> int main(){ FILE *fp; int pfd[2]; char *cmd="ls -l"; char buf[100]; if((fp=popen(cmd,"r")) == NULL){ printf("popen error\n"); exit(1); } while(fgets(buf,100,fp)!= NULL){ printf("from fp:%s",buf); } pclose(fp); return 0; }
c、有名管道FIFO
讀資料的程式:
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<memory.h>
#include<errno.h>
#include<stdlib.h>
#define FIFO "pipetest"
int main(int argc,char* argv[]){
int fd,r;
char buff[1024];
if(access(FIFO,F_OK)==-1){ //判斷管道是否存在,管道是不可以重複建立的
if(mkfifo(FIFO,0666)<0){
if(errno==EEXIST)
printf("it already exist\n");
printf("create fifo error! it already exist\n");
exit(1);
}
}
if((fd=open("in1",O_RDONLY))<0){
printf("open in1 error!\n");
return 1;
}
while(1){
memset(buff,0,1024);
if(r=read(fd,buff,1024)>0){
printf("send to name pipe: %s\n",buff);
}
}
close(fd);
}
寫資料的程式:
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<memory.h>
#include<errno.h>
#include<stdlib.h>
#define FIFO "pipetest"
int main(int argc,char* argv[]){
int fd,w;
char buff[1024];
if(argc<2){
printf("Usage: ./%s <string>\n",argv[0]);
exit(0);
}
sscanf(argv[1],"%s",buff);
if(access(FIFO,F_OK)==-1){
if(mkfifo(FIFO,0666)<0){
if(errno==EEXIST)
printf("it already exist\n");
printf("create fifo error!\n");
exit(1);
}
}
if((fd=open("in1",O_WRONLY))<0){
printf("open in1 error!\n");
return 1;
}
if(w=write(fd,buff,1024)>0){
printf("send to name pipe: %s\n",buff);
}
close(fd);
}
3、訊號量
訊號量和訊號通訊( Signal )可不一樣。訊號量( Semaphore ) 是用於解決不同程式間的訪問問題。即同步和互斥。
同步是指多執行緒程式按照一定的順序執行一段完整的程式碼。互斥指多執行緒程式訪問同一個變數時只能有一個執行緒在訪問。
訊號量就是為了解決同步和互斥的IPC機制,訊號量為0則沒有資源,程序進入等待佇列,直到資源被釋放為止。
訊號量程式設計的步驟:
1、建立訊號量或獲得在系統中已經存在的訊號量
呼叫semget () 函式 。使用同一個訊號量鍵值即可獲得同一個訊號量。
2、初始化訊號量
使用semctl( ) 函式的SETVAL。當使用二維訊號量時,初始化為1。
3、進行訊號量的PV操作
呼叫semop( ) 。實現同步與互斥。
4、不需要訊號量則從系統中刪除
使用semclt( ) 函式的IPC_RMID 。
一個操作方法的封裝函式檔案:
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<memory.h>
#include<errno.h>
#include<stdlib.h>
#define FIFO "pipetest"
int main(int argc,char* argv[]){
int fd,w;
char buff[1024];
if(argc<2){
printf("Usage: ./%s <string>\n",argv[0]);
exit(0);
}
sscanf(argv[1],"%s",buff);
if(access(FIFO,F_OK)==-1){
if(mkfifo(FIFO,0666)<0){
if(errno==EEXIST)
printf("it already exist\n");
printf("create fifo error! it already exist\n");
exit(1);
}
}
if((fd=open("in1",O_WRONLY))<0){
printf("open in1 error!\n");
return 1;
}
if(w=write(fd,buff,1024)>0){
printf("send to name pipe: %s\n",buff);
}
close(fd);
}
呼叫的例子:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include"semcom.c"
int main(){
pid_t pid;
int semid;
if((semid=semget(ftok(".",'a'),1,0666|IPC_CREAT))<0){ //建立訊號量
printf("semget error!\n");
exit(1);
}
init_sem(semid,1);
if((pid = fork())<0){
printf("fork error!\n");
exit(1);
}else if(pid == 0){
sem_p(semid); //在未釋放之前父程序無法訪問
printf("child is running......\n");
sleep(3);
printf("son is %d wake\n",getpid());
sem_v(semid);
}else{
sem_p(semid);
printf("parent is running......\n");
sleep(3);
printf("parent is %d wake\n",getpid());
sem_v(semid);
sleep(6);
del_sem(semid);
}
return 0;
}