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);
相关文章
相关标签/搜索