• 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

  • 相关阅读:
    LibreOJ #507. 「LibreOJ NOI Round #1」接竹竿
    BZOJ 4590: [Shoi2015]自动刷题机
    luogu P3808 【模板】AC自动机(简单版)
    cogs 2569. [東方] 博丽灵梦 梦想妙珠
    codeforces 1C. Ancient Berland Circus
    BZOJ 4570: [Scoi2016]妖怪
    HDU 1392 Surround the Trees
    cogs 999. [東方S2]雾雨魔理沙
    Uva 10652 Board Wrapping
    AC日记——[Sdoi2008]Cave 洞穴勘测 bzoj 2049
  • 原文地址:https://www.cnblogs.com/zheaven/p/14262401.html
Copyright © 2020-2023  润新知