IPC--进程间通讯

常用的进程间通讯方式:
a.管道(使用最简单)匿名管道
b.信号(开销最小)
c.共享映射区(无血缘关系)
d.本地套接字(最稳定)
e.FIFO(命名管道)

1.管道:
pipe:管道一般读写行为

2.fifo:(有名管道)
用于非血缘关系进程间通讯
命令:mkfifo
函数:mkfifo
3.共享映射区:
mmap
函数的参数使用注意事项
用于非血缘关系进程间通讯

管道的概念:

管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
1.其本质是一个伪文件(实为内核缓冲区)
七种文件类型:
1). - 文件 2). d 目录 3). l 字符链接 此三种为真实文件,占据磁盘存储空间
4). s 套接字 5). b 块设备 6). c 字符设备 7). p 管道 此四种为伪文件
2.有两个文件描述符引用,一个表示读端,一个表示写端。
3.规定数据从管道的写端流入管道,从读端流出。

管道的原理:管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。【使用ulimit -a查看,函数fpathconf 参2:_PC_PIPE_BUF】
管道的局限性:
1.数据自己读不能自己写
2.数据一旦被读走,便不在管道中存在,不可反复读取。
3.由于管道采用半双工通讯方式。因此数据只能在一个方向上流动
4.只能在有公共祖先的进程间使用管道
常见的通信方式有:单工通信、半双工通信、全双工通信。

模拟一个管道间pipe的通信:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    int main(void)
    {
        int fd[2];
        pid_t pid;
        
        int ret = pipe(fd);
        if(ret == -1){
            perror("pipe error.");
            exit(1);
        }
        
        pid = fork();
        if(pid == -1){
            perror("fork error.");
            exit(1);
        } else if(pid == 0) {
            //规定子进程负责读端,父进程负责写端
            //默认fd[0]--读端,fd[1]--写端
            //关闭子进程的写端
            close(fd[1]);
            char buf[1024];
            ret = read(fd[0], buf, sizeof(buf));
            if(ret == 0){
                printf("---finished read---");
            } 
            write(STDOUT_FILENO, buf, ret);
        } else {
            //关闭父进程的读端
            close(fd[0]);
            write(fd[1], "hello pipe
", strlen("hello pipe
"));
        }
    }

共享内存:

     1.mmap函数:参数、返回值
        void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
        返回:
            成功:返回创建的映射区首地址
            失败:MAP_FAILED 宏
        参数:
            addr: 建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL
            length: 预创建映射区的大小
            prot: 映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
            flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
                MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上
                MAP_PRIVATE: 映射区所做的修改不会反映到物理设备
            fd: 用来建立映射区的文件描述符
            offset: 映射文件的偏移(4k的整数倍)
            

mmap示例:

        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <string.h>
        #include <fcntl.h>
        #include <sys/mman.h>
        
        int main(void)
        {
            int len, ret;
            char *p = NULL;
            int fd = open("mytest.txt", O_CREAT|O_RDWR, 0644);
            if(fd < 0) {
                perror("open error:");
                exit(1);
            }
            len = ftruncate(fd, 5);
            if(len == -1) {
                perror("ftruncate error:");
                exit(1);
            }
            p = mmap(NULL, 5, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
            if(p == MAP_FAILED) {
                perror("mmap error:");
                exit(1);
            }
            strcpy(p, "abc
");
            ret = munmap(p, 5);
            if(ret == -1) {
                perror("munmap error:");
                exit(1);
            }
            close(fd);
            return 0;
        }

munmap函数
同malloc函数申请内存空间类似的,mmap建立的映射区在使用之后也应调用类似free的函数来释放。

使用mmap时的注意事项:
1).创建映射区的过程中,隐含着一次对映射文件的读操作
2).当MAP_SHARED时,要求:映射区的权限应<=文件打开的权限(出于对映射区的保护)。而MAP_PRIVATE则无所谓,
因为mmap中的权限是对内存的限制。
3).映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭。
4).特别注意,当映射文件大小为0时,不能创建映射区。所以:用于映射的文件必须要有实际大小!
mmap使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
5).munmap传入的地址一定是mmap的返回地址,坚决杜绝指针++操作。
6).如果有文件偏移量必须为4k的整数倍(4*1024*n)
7).mmap创建映射区的出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。

    2.借助共享内存放磁盘文件,借助指针访问磁盘文件
    3.父子进程、血缘关系进程通信
    4.匿名映射区
        通过使用我们发现,使用映射区来完成文件读写操作十分方便,父子进程间通信也比较容易。但缺陷是,每次创建映射区
      一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件,创建好了再unlink、close掉,比较麻烦。可以直接
      使用匿名映射来代替。其实Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区。同样需要借助
      标志位参数flags来指定。
        使用MAP_ANONYMOUS (或 MAP_ANON),如:
        int *p=mmap(NULL,4,PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
        "4"可以为任意值,可以根据实际需要填写。
        
        需注意的是,MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏。在类Unix系统中无该宏定义,可使用下面两步来创建匿名映射区:
        1). fd=open("/dev/zero", O_RDWR);
        2). P=mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    5.mmap无血缘关系进程间通信

mmap_r.c和mmap_w.c共用一个文件file_shared模拟无血缘关系进程间通信
mmap_r.c表示读进程

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    
    struct STU {
        int id;
        char name[20];
        char sex;
    };
    
    void sys_err(char *str){
        perror(str);
        exit(1);
    }
    
    int main(int argc, char *argv[]){
        int fd;
        struct STU student;
        struct STU *mm;
        
        if(argc < 2) {
            printf("./a.out file_shared
");
            exit(-1);
        }
        
        fd = open(argv[1], O_RDONLY);
        if(fd == -1){
            sys_err("open error:");
        }
        
        mm = mmap(NULL, sizeof(student), PROT_READ, MAP_SHARED, fd, 0);
        if(mm == MAP_FAILED){
            sys_err("mmap error:");
        }
        
        close(fd);
        
        while(1) {
            printf("id=%d	name=%s	%c
", mm->id, mm->name, mm->sex);
            sleep(2);
        }
        
        munmap(mm, sizeof(student));
        return 0;
    }

mmap_w.c表示写进程

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    
    struct STU {
        int id;
        char name[20];
        char sex;
    };
    
    void sys_err(char *str){
        perror(str);
        exit(1);
    }
    
    int main(int argc, char *argv[]){
        int fd;
        struct STU student = {10, "xiaoming", 'm'};
        char *mm;
        
        if(argc < 2) {
            printf("./a.out file_shared
");
            exit(-1);
        }
        
        fd = open(argv[1], O_RDWR | O_CREAT, 0664);
        ftruncate(fd, sizeof(student));
        
        mm = mmap(NULL, sizeof(student), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if(mm == MAP_FAILED){
            sys_err("mmap error:");
        }
        
        close(fd);
        
        while(1) {
            memcpy(mm, &student, sizeof(student));
            student.id++;
            sleep(1);
        }
        
        munmap(mm, sizeof(student));
        return 0;
    }

先执行mmap_w.c后执行mmap_r.c

可以使用 strace mmap_w.out 来追踪mmap_w.out执行过程中调用了哪些系统函数。
yum install strace -y 来安装strace

模拟进程组:cat | cat | cat | cat
使用 ps ajx来查看
杀掉进程组 kill -9 -gpid

原文地址:https://www.cnblogs.com/zheaven/p/14262401.html