LINUX inner-process communication

進程間通信方式

管道

管道是針對對本地計算機的兩個進程之間的通訊而設計的通訊方式,管道創建後,實際得到兩個文件描述符,一個讀取另外一個寫入。最多見的IPC機制,經過PIPE系統調用。管道是單工的,數據只能向一個方向流動,須要雙向通訊時,須要創建起兩個管道。管道的本質是內核中的緩存。linux

管道特性:

  1. 能夠經過兩個管道來建立一個雙向的管道
  2. 管道是阻塞性的,當進程從管道中讀取數據,若沒有數據進程會阻塞
  3. 管道有大小限制,管道滿再放則會報錯
  4. 不完整管道
  • 當讀一個寫端已經關閉的管道時,在全部數據被讀取後,read返回0,以表示到達了文件尾部
  • 若是寫一個讀端已經關閉的管道,剛產生信號SIGPIPE,若是忽略該信號或捕捉該信號並從處理程序返回,則write返回-1,同時errno設置爲EPIPE

管道的分類

匿名管道

  1. 在關係進程中進行(父進程各子進程,兄弟進程之間)
  2. 由PIPE系統調用
  3. 管道位於內核空間,實際上是一塊緩存
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

/**
*Desc:扇形多線程之間管道通訊
*author:xiao_dingo
*since:2018-03-07
*email:wwc0524@163.com
*/

char *cmd1[3] = {"/bin/cat","/etc/passwd",NULL};
char *cmd2[3] = {"/bin/grep","root",NULL};

int main(void){

        int fd[2];
        if(pipe(fd) < 0){
                perror("pipe error");
        }

        int i = 0;
        pid_t pid;
        for(;i < 2; i++){
                pid = fork();
                if(pid < 0){
                        perror("fork error");
                        exit(1);
                }else if(pid == 0){//child process

                        if(i == 0){
                                close(fd[0]);

                                //將標準輸出重定向到管道的寫端
                                if(dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO){
                                perror("dup2 error");
                                }
                                close(fd[1]);
                                if(execvp(cmd1[0],cmd1) < 0){
                                        perror("execvp error");
                                        exit(1);
                                }
                                break;
                        }
                        if(i == 1){
                                close(fd[1]);

                                //將標準輸入重定向到管道的讀端
                                if(dup2(fd[0],STDIN_FILENO) != STDIN_FILENO){
                                perror("dup2 error");
                                }       
                                close(fd[0]);
                                if(execvp(cmd2[0],cmd2) < 0){
                                        perror("execvp error");
                                        exit(1);
                                }
                                break;
                        }
                }else{//parent process
                        if(i == 1){
                                close(fd[0]);
                                close(fd[1]);
                                wait(NULL);
                                wait(NULL);
                        }
                }
        }
        exit(0);
}
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

/**
*Desc:不完整管道之間操做
*author:xiao_dingo
*since:2018-03-08
*email:wwc0524@163.com
*/

void sig_handler(int signo){
        if(signo == SIGPIPE){
                printf("sigpipe occured\n");
        }
}

void main(void){
        int fd[2];
        if(pipe(fd) < 0){
                perror("pipe error");
                exit(1);
        }
        pid_t pid;
        if((pid = fork()) < 0){
                perror("fork error");
                exit(1);
        }else if(pid > 0){//parent process
                sleep(5);
                close(fd[0]);
                if(signal(SIGPIPE,sig_handler) == SIG_ERR){
                        perror("signal sigpipe error");
                        exit(1);
                }
                char *s = "1234";
                if(write(fd[1],s,sizeof(s)) != sizeof(s)){
                        fprintf(stderr,"%s,%s\n",strerror(errno),(errno == EPIPE) ? "EPIPE" : ",UNKNOW");
                }
        }else{//child process
                close(fd[0]);
                close(fd[1]);
        }
}

命名管道(FIFO)

  1. 兩個沒有任何關係的進行之間通訊能夠經過命名管道進行數據傳輸,本質是內核中的一塊緩存,在文件系統中以一個特殊的設備文件(管道文件)存在。在文件系統中的管道文件只有一個索引塊存放文件路徑,沒有數據塊,全部數據存放在內核中。
  2. 經過系統調用mkfifo建立
  3. 命名管道必須讀和寫同時打開,不然會進入阻塞
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char* pathname,mode_t mode);

消息隊列

System v IPC對象(消息隊列,共享內存和信號量)存在於內核中而不是文件系統中,由用戶控制釋放(用戶管理IPC對象的生命週期),不像管道和釋放由內核控制。IPC對象經過標識符來引用和訪問,全部的IPC對象在內核空間有惟一標識ID,在用戶空間的惟一標識稱爲Key緩存

消息隊列特性

  1. 消息隊列是內核中的一個鏈表
  2. 用戶進程將數據傳輸到內核後,內核從新添加一些如用戶ID,組ID,讀寫進程的ID和優先集等相關信息後並打成一個數據包稱爲消息
  3. 容許一個或者多個進程往消息隊列中寫消息和讀消息,但一個消息只能被一個進程讀取,讀取完畢就自動刪除
  4. 消息隊列具備必定的FIFO的特性,消息能夠按照順序發送到隊列中,也能夠幾種不一樣的方式從隊列中讀取,消息隊列在內核中用一個惟一的IPC標識ID表示
  5. 消息隊列的實現包括建立和打開隊列,發送消息,讀取消息,控制消息隊列四種操做
  6. linux 系統查看命令ipcs 刪除ipcrm
#include <sys/msg.h>
int msgget(key_t key,int flag);//查詢
int msgctl(int msgid,int cmd,struct msgid_ds *buf);//控制
int msgsnd(int magid,const void *ptr,szie_t nbytes,int flag);//發送
ssize_t msgrvc(int msgqid,void *ptr,size_t nbytes,long type,int flag);//接收

共享內存

共享內存容許系統內兩個或多個進程共享同一塊內存空間,而且數據不用在客戶進程和服務器進程間複製,所以共享內存是通訊速度最快的一種IPC。
實現的機制簡單描述以下:一個進程在系統中申請開闢了一塊共享內存空間,而後使用這個共享內存空間的各個進程分別打開這個共享內存空間,並將這個內存空間映射到本身的進程空間上,這樣各個進程就能夠共同使用這個共享內存空間,就如同使用本身進程地址空間的內存同樣
要實現共享內存空間,內核作了許多工做:好比給每一個共享內存塊分發一個「身份證」、容許用戶進程將共享內存映射到各自的地址空間上、在進程提出申請之後將共享內存和進程地址空間脫離,並在適當的時候講共享內存刪除,讓其回到能夠被建立的狀態。
用戶利用共享內存實現進程間的通訊,實際上就是使用內核提供的服務完成對共享內存的創建、映射、脫離、刪除等。當創建並映射成功之後,進程間就能經過共享內存實現數據的交互。服務器

內核提供的服務

/**
*shmget實現共享內存的創建或者打開
*當共享內存的鍵值key 還沒有存在時,調用這個函數而且指定shmflg 參數爲IPC_CREAT 能夠建立一個大小爲 size 的共享內存空間。假設key指定的共享內存已經存在,調用這個函數能夠打開這個共享內存,但不會建立。
*
*/
int shmget(key_t key, size_t size, int shmflg);
/**
*該函數將一個共享內存空間映射到調用進程的地址空間上,而且返回在進程地址空間中的地址。用戶拿到改地址後就能夠經過這個地址間接的訪問共享內存。
*shmid 參數就是shmget 函數的返回值,shmaddr 參數其實是指出了共享內存映射到進程地址空間上的位置,可是咱們通常不會指定這個地址,而是令其爲NULL ,讓內核選擇一個合適的地址。shmflg 參數是配合着shmaddr 參數使用的,在shmaddr 爲NULL時就變得沒有實際意義,所以一般指定爲0
*/
void *shmat(int shmid,const void* shmaddr,int shmflg);
/**
*這個函數將一個進程已經映射了的共享內存脫離進程地址空間。shmaddr 參數就是在進程地址空間的地址,實際就是shmat 函數的返回值
*/
int shmdt(const void* shmaddr);
/**
*此函數實際上有不少的功能,可是咱們通長用它將一個已經建立了的共享內存刪除,所謂刪除實際就是將它放回能夠被建立的共享內存隊列中。指定cmd 參數爲IPC_RMID,就能夠將shmid鍵值指定的共享內存刪除,而buf其實是能夠獲取這共享內存在內核中的狀態,若是不想了解能夠指定爲0
*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

進程信號量

用於進程間的huchi與同步,每種共享資源對應一個信號量,爲了便於大量共享資源的操做引入了信號量集,可對全部信息量一次性操做,對信號量集中全部操做能夠要求所有成功,也能夠部分紅功。
它是一個特殊變量,只容許對它進行等待和發送信號這兩種操做。多線程

  • P(信號量變量sv):等待。若是sv大於0,減少sv。若是sv爲0,掛起這個進程的執行。
  • V(信號量變量sv):發送信號。若是有進程被掛起等待sv,使其恢復執行。若是沒有進行被掛起等待sv,增長sv。
#include  <sys/sem.h>
/**
*
*
*/
int semget(key_t key,int nsems,int semflg);
相關文章
相關標籤/搜索