1. 程式人生 > >Linux程序間通訊(一)——管道、訊號量

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;
}