LINUX inner-process communication

进程间通讯方式

管道

管道是针对对本地计算机的两个进程之间的通信而设计的通信方式,管道建立后,实际获得两个文件描述符,一个读取另一个写入。最常见的IPC机制,通过PIPE系统调用。管道是单工的,数据只能向一个方向流动,需要双向通信时,需要建立起两个管道。管道的本质是内核中的缓存。

管道特性:

  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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值