zoukankan      html  css  js  c++  java
  • 进程间通信

    进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。进程间通信的方式有如下几种:

    1.管道通信

    特点:

    1. 管道只允许具有血缘关系的进程间通信,如父子进程间的通信。

    2. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

    3. 管道并非是进程所有的资源,而是和套接字一样,归操作系统所有。可以将它看成文件系统,但该文件系统只存在于内存当中。

    原型

    #include <unistd.h>
    /* Create a one-way communication channel (pipe).
       If successful, two file descriptors are stored in PIPEDES;
       bytes written on PIPEDES[1] can be read from PIPEDES[0].
       Returns 0 if successful, -1 if not.  */
    extern int pipe (int fd[2]) __THROW __wur;
    

    参数的说明:

    ​ 字符数组fd是管道传输或者接收时用到的文件描述符,其中fd[0]是接收的时候使用的文件描述符,即管道出口;而fd[1]是传输的时候用到的文件描述符,即管道入口。

    ​ 为了使数据可以双向传递,可以使用两个管道,一个管道负责进程1的写和进程2的读,另一个管道负责一个进程1的读和进程2的写。测试程序如下:

    #include <iostream>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #define BUF_SIZE 30
    
    int main(){
        int fds1[2], fds2[2];
        /*
         *注意:
         * 此处不能写char* str1 = "Who are you?";
         * 得到的sizeof(str1)等于8,该值实际上是指针的大小,并不是字符串数组的大小
         * 这个时候要用strlen()函数(strlen的唯一标准是找‘’)
         * 系统函数返回值是char *(#include <string.h>)类型的往往会在末尾加上''。
         * 要注意的是未初始化的情况下,用strlen是不可行的
         **/
        char str1[] = "Who are you?";
        char str2[] = "Thank you for your message.";
        char buf[BUF_SIZE];
        pipe(fds1);     //创建两个管道
        pipe(fds2);
    
        pid_t pid = fork();
        if(pid == 0){
            write(fds1[1], str1, sizeof(str1));
            read(fds2[0], buf, BUF_SIZE);
            printf("Child process copy the message: %s
    ", buf);
        }else {
            read(fds1[0], buf, BUF_SIZE);
            printf("Parent Process copy the message: %s
    ", buf);
            write(fds2[1], str2, sizeof(str2));
        }
        return 0;
    }
    

    2.FIFO

    ​ FIFO即命名管道,在磁盘上有对应的节点,但没有数据块—换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。

    /* Create a new FIFO named PATH, with permission bits MODE.  */
    extern int mkfifo (const char *__path, __mode_t __mode)
         __THROW __nonnull ((1));
    

    其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。

    当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

    • 若没有指定O_NONBLOCK(默认),以只读方式打开的FIFO要阻塞到其他的某个程序以写打开这个FIFO。同样以只写方式打开的FIFO要阻塞到其他某个进程以读方式打开该FIFO。
    • 若指定了O_NONBLOCK,则以只读方式打开会立刻返回而不阻塞(不是出错返回)。而以只写方式打开,若之前没有进程以读方式打开这个FIFO则立刻出错返回。

    示例代码:一个进程发送消息给另一个进程

    writefifo.cpp

    #include <iostream>
    #include <stdio.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <fcntl.h>  // O_WRONLY
    #include <time.h>   //time
    #include <unistd.h>
    using namespace std;
    
    int main(){
        int fd;
        int n;
        char buf[1024];
        time_t tp;
    
        if( mkfifo("fifo1",  0666) < 0 && errno != EEXIST) //创建FIFO管道
        {
            perror("Create FIFO Faileed");
        }
    
        printf("I am %d process.
    ", getpt());  //说明进程的ID
    
        if((fd = open("fifo1",O_WRONLY )) < 0){   //以只写方式打开FIFO
            perror("Open FIFO failed");
            exit(1);
        }
        for (int i = 0; i < 10; ++i) {
            time(&tp);  //获取当前系统时间
            n = sprintf(buf, "Process %d's time is %s",getpid(), ctime(&tp));
            printf("send message: %s", buf);
            if(write(fd, buf, n+1) < 0)
            {
                perror("write FIFO Failed");
                close(fd);
                exit(1);
            }
            sleep(1);
        }
        close(fd);
        return 0;
    }
    
    

    readfifo.cpp

    #include <stdio.h>
    #include <iostream>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/stat.h>
    #include <fcntl.h>  // O_WRONLY
    #include <time.h>   //time
    #include <unistd.h>
    using namespace std;
    
    int main(){
        int fd;
        int len;
        char buf[1024];
        if((fd = open("fifo1",  O_RDONLY)) < 0){   //以只读方式打开FIFO
            perror("Open FIFO failed");
            exit(1);
        }
        while ((len = read(fd, buf ,1024)) > 0)   //读取FIFO管道
        {
           printf("Read message: %s", buf);
        }
    
        close(fd);
        return 0;
    }
    
    

    如果在writefifo.cpp中修改如下,设置非阻塞标志:

    if((fd = open("fifo1",O_WRONLY | O_NONBLOCK)) < 0){   //以只写方式打开FIFO
    

    如果先运行writefifo,在运行readfifo,则会出错。

    3. 消息队列

    ​ 消息队列,就是一个消息的链表,是一系列保存在内核的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。

    特点:

    • 队列独立于发送与接收进程。进程终止时,消息队列及其内容并不删除。
    • 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

    4.共享内存

    ​ 共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。

    5. 信号量

    ​ 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

    6.套接字

    ​ 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

    参考资料

    进程间通信的方式——信号、管道、消息队列、共享内存

    进程间的五种通信方式介绍

  • 相关阅读:
    MySQL--字符集参数
    MySQL--字符集基础
    Cassandra基础2
    Cassandra基础
    Cassandra -- Cassandra 3.0版本安装
    Cassandra Demo--Python操作cassandra
    MySQL--批量插入导致自增跳号问题
    MySQL Disk--SSD和HDD的性能
    MySQL Lock--并发插入导致的死锁
    TCL-视图
  • 原文地址:https://www.cnblogs.com/helloworldcode/p/10742125.html
Copyright © 2011-2022 走看看