Linux 进程间通信

进程间通信

进程间的通信方式有如下7种

  1. 传统的通信方式
    1. 无名管道
    2. 有名管道
    3. 信号
  2. system V IPC对象
    1. 共享内存
    2. 消息队列
    3. 信号灯集
  3. BSD
    1. 套接字(socket)

无名管道

特点

a. 只能用于具有==亲缘关系==的进程之间的通信
b. 半双工的通信模式
c. 管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数.
d. 管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述fd[0]和fd[1],其中fd[0]固定用于读管道,而fd[1]固定用于写管道。

函数模型

#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建无名管道
参数:pipefd:创建的文件描述符,下标为0和1的两个文件描述符
返回值:成功:0
        失败:-1

特性

  1. 当管道中无数据时,读操作会阻塞;

  2. 管道中装满(管道大小64K)数据时,写阻塞,一旦有4k空间,写继续

  3. 只有在管道的读端存在时,向管道中写入数据才有意义。否则,会导致管道破裂,向管道中写入数据的进程将收到内核传来的SIGPIPE信号 (通常Broken pipe错误)。

int main(int argc, char const *argv[])
{
    int fd[2] = {0};
    if(pipe(fd) < 0)
    {
        perror("pipe err");
        return -1;
    }
    printf("%d %d\n", fd[0], fd[1]); //3 4

    // char buf[32] = "";
    //1.当管道中没有数据时,读阻塞
    // read(fd[0], buf, 32);
    // printf("buf:%s\n", buf);

    //2.当管道写满(64k)时,写阻塞
    // char buf[65536] = "";
    // write(fd[1], buf, 65536);
    // 至少读出4k空间,才能继续写操作
    // read(fd[0], buf, 4096);

    // printf("write befor\n");
    // write(fd[1], "a", 1);
    // printf("write after\n");

    //3.关闭读端,写操作时会导致管道破裂(收到SIGPIPE信号进程结束)
    close(fd[0]);
    write(fd[1], "a", 1);
    //下面语句不会输出
    printf("aaa\n");
    return 0;
}

例子 : 创建子进程,父进程循环从终端输入字符串,子进程循环打印数据,当输入quit时程序结束。

int main(int argc, char const *argv[])
{
    int fd[2] = {0};
    char buf[32] = "";
    if (pipe(fd) < 0)
    {
        perror("pipe err");
        return -1;
    }
    pid_t t = fork();
    if (t < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (t == 0)
    {
        while (1)
        {
            read(fd[0], buf, 32);
            if (!strcmp(buf, "quit"))
                break;
            printf("buf:%s\n", buf);
        }
    }
    else
    {
        while (1)
        {
            scanf("%s", buf);
            write(fd[1], buf, strlen(buf) + 1);
            if (!strcmp(buf, "quit"))
                break;
        }
        wait(NULL);
    }
    return 0;
}

有名管道

特点

​ 1.有名管道可以使互不相关的两个进程互相通信
​ 2.有名管道可以通过路径名来指出,并且在文件系统中可见,但内容存放在内存中
​ 3.通过文件IO来操作有名管道
​ 4.遵循先进先出规则
​ 5.不支持如lseek() 操作

函数

int mkfifo(const char *pathname, mode_t mode);
功能:创建有名管道
参数:pathname:管道文件名
      mode:权限  用八进制数表示0666  0777 
返回值:成功:0
       失败:-1,并设置errno号

补充:
1.当管道文件存在(报错提示file exists)时的处理方式:
判断errno的值为EEXIST时,只是打印提示语句,if(errno == EEXIST)
2.注意代码中出现errno,需要添加头文件#include
3.注意写入管道的数据并不是写入文件的,数据是在内核空间

#include <errno.h>
int main(int argc, char const *argv[])
{
    if (mkfifo("./fifo", 0666) < 0)
    {
        //errno :错误码变量
        if (errno == EEXIST)
            printf("file exist\n");
        else
        {
            perror("mkfifo err");
            return -1;
        }
    }
    printf("mkfifo ok\n");
    int fd = open("./fifo", O_RDWR);
    if(fd < 0)
    {
        perror("open err");
        return -1;
    }
    char buf[32] = "";
    write(fd, "hello", 5);
    read(fd, buf, 32);
    printf("buf:%s\n", buf);
    return 0;
}

读写特性

a. 只写方式,写阻塞(阻塞在打开文件的位置),一直到另一个进程把读打开
b. 只读方式,读阻塞(阻塞在打开文件的位置),一直到另一个进程把写打开
c. 可读可写,如果管道中没有数据,读阻塞,
d.当写端关闭,读操作,如果管道中没有数据,read立刻返回

共享内存

信号灯集

信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制;

System V信号灯集是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯。

步骤

  1. 创建key值-ftok
  2. 创建或打开信号灯集-semget
  3. 初始化信号灯集-semctl
  4. PV操作-semop
  5. 删除信号灯集-semget

函数

上一篇
下一篇