【Linux学习笔记】11-进程通信

本文深入探讨了进程间通信的三种主要方式:匿名管道、命名管道和共享内存。详细阐述了每种方式的工作原理、使用步骤及示例代码。匿名管道适用于有血缘关系的进程,而命名管道扩展到无血缘关系进程。共享内存则提供了快速通信的方式,但不包含同步互斥机制。消息队列允许不同类型的消息传递,确保了通信的灵活性。这些技术在系统编程和并发处理中起着关键作用。
摘要由CSDN通过智能技术生成

1、管道

1. 匿名管道

匿名管道供具有血缘关系的进程进行通信

匿名管道通信步骤:
1、一个进程分别以读写方式打开同一个文件,这样就有两个文件描述符,一个以read方式指向该文件,一个以write方式指向该文件
2、父进程fork创建一个子进程,因为子进程创建以父进程为模版,则子进程的file_struct的fd_array与父进程一样
3、让父进程read,子进程write,则关闭父进行的write,保持父进程的read打开;关闭子进程的read,保持子进程的write打开

测试1

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int main() {
  5     int pipe_fd[2] = {0};
  6     if (pipe(pipe_fd) < 0) {
  7         perror("pipe");
  8         return 1;
  9     }
 10     printf("%d %d\n", pipe_fd[0], pipe_fd[1]);                                                                                       
 11     return 0;
 12 }
~

在这里插入图片描述
测试2

    1 #include <stdio.h>
    2 #include <unistd.h>
    3 #include <string.h>
    4 #include <sys/types.h>
    5 #include <sys/stat.h>
    6 #include <stdlib.h>
    7                                                                                                                                    
    8 
    9 int main() {
   10     int pipe_fd[2] = {0};
   11     if (pipe(pipe_fd) < 0) {
   12         perror("pipe");
   13         return 1;
   14     }
   15     printf("%d %d\n", pipe_fd[0], pipe_fd[1]);
   16     pid_t id = fork();
   17     if (id < 0) {
   18         perror("fork");
   19     }
   20     if (id == 0) { //write
   21         //child
   22         close(pipe_fd[0]);
W> 23         char *msg = "hello world, i am child process";
   24         int count = 5;
   25         while (count) {
   26             write(pipe_fd[1], msg, strlen(msg));
   27             sleep(1);
   28             count--;
   29         }
   30         close(pipe_fd[1]);
   31         exit(0);
   32     }else {     // read
   33         //parent
   34         close(pipe_fd[1]);
   35         ssize_t size = 0;
   36         char buf[64] = {0};
   37         while(1) {
   38             size = read(pipe_fd[0], buf, sizeof(buf)-1);
   39             if (size > 0) {
   40                 buf[size] = 0;
   41                 printf("parent get message from child#: %s\n", buf);
   42             } else if (size == 0) {
   43                 printf("pipe file close, child quit!\n");
   44                 break;
   45             }
   46         }
   47         close(pipe_fd[0]);
   48         int status = 0;
   49         pid_t ret = waitpid(id, &status, 0);
   50         if (ret == -1) {
   51             perror("waitpid");
   52             return 1;
   53         }
   54         printf("child is waited, the exit code is %d\n", WEXITSTATUS(status));
   55     }
   56     return 0;
   57 }   

在这里插入图片描述
测试3:读端不读且关闭了read,写端会怎么样?
系统直接kill了写端,因为没人读,写就是做的无用功,系统不允许任何无用功

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>


int main() {
    int pipe_fd[2] = {0};
    if (pipe(pipe_fd) < 0) {
        perror("pipe");
        return 1;
    }
    printf("%d %d\n", pipe_fd[0], pipe_fd[1]);
    pid_t id = fork();
    if (id < 0) {
        perror("fork");
    }
    if (id == 0) { //write
        //child
        close(pipe_fd[0]);
        char *msg = "hello world, i am child process";
        int count = 5;
        while (count) {
            write(pipe_fd[1], msg, strlen(msg));
            sleep(1);
            count--;
        }
        close(pipe_fd[1]);
        exit(0);
    }else {     // read
        //parent
        close(pipe_fd[1]);
        ssize_t size = 0;
        char buf[64] = {0};
        while(1) {
            size = read(pipe_fd[0], buf, sizeof(buf)-1);
            if (size > 0) {
                buf[size] = 0;
                printf("parent get message from child#: %s\n", buf);
            } else if (size == 0) {
                printf("pipe file close, child quit!\n");
                break;
            }else{
                break;
            }
            close(pipe_fd[0]);
        }
        //close(pipe_fd[0]);
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if (ret == -1) {
            perror("waitpid");
            return 1;
        }
        printf("child is waited, the sig code is %d, the exit code is %d\n", (status & 0x7F), WEXITSTATUS(status));
    }
    return 0;
}

在这里插入图片描述
管道的生命周期是随着进程的生命周期的,因为一个进程关闭了,则该进程打开的文件也会关闭

2. 命名管道

用于没有血缘关系的两个进程之间的通信
mkfifo:用于创建一个命名管道的命令,mkfifo filename可以只在命令行创建一个名为filename的命名管道文件。
但是,向filename写数据时,我们的数据并不会写到磁盘上,只会写到struct file结构体的缓冲区里。(普通文件需要将数据刷新到磁盘来持久化存储)

测试1:在创建了myfifo的命名管道文件后
在bash1里:

while :; do echo "hello world"; sleep 1; done > myfifo 

在bash2里:

cat < myfifo 

可以看到“hello world”在bash2中被打印出来

测试2:代码级命名管道通信

int mkfifo(const char *filename, mode_t mode)
成功返回0,失败返回-1

创建一个servre,一个client,client发消息,server接收到消息

//server.c
  1 #include "stdio.h"
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <stdlib.h>
  5 #include <sys/types.h>
  6 #include <sys/stat.h>
  7 #include <fcntl.h>
  8 
  9 #define FIFO "./fifo"
 10 
 11 int main() {
 12     int ret = mkfifo(FIFO, 0644);
 13     if (ret < 0) {
 14         perror("mkfifo");
 15     }
 16     int fd = open(FIFO, O_RDONLY);
 17     if (fd < 0) {
 18         perror("open");
 19         return 1;
 20     }
 21 
 22     char buf[64] = {0};
 23 
 24     while (1) {
 25         ssize_t s = read(fd, buf, sizeof(buf)-1);
 26         if (s > 0) {
 27             buf[s] = 0;
 28             printf("parent get message from client#: %s", buf);                                                                      
 29         }else if(s == 0) {
 30             printf("client quit\n");
 31             break;
 32         }else {
 33             break;
 34         }
 35     }
 36     close(fd);
 37     return 0;
 38 }
//client.c
  1 #include "stdio.h"
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <stdlib.h>
  5 #include <sys/types.h>
  6 #include <sys/stat.h>
  7 #include <fcntl.h>
  8 
  9 #define FIFO "./fifo"
 10 
 11 int main() {
 12     int fd = open(FIFO, O_WRONLY);
 13     if (fd < 0) {
 14         perror("open");
 15         return 1;
 16     }
 17 
 18     char buf[128];
 19 
 20     while (1) {
 21         printf("please enter# ");
 22         fflush(stdout);
 23         buf[0] = 0;
 24         ssize_t s = read(0, buf, sizeof(buf)-1);                                                                                     
 25         if (s > 0) {
 26             buf[s] = 0;
 27             write(fd, buf, strlen(buf));
 28         }else if(s == 0) {
 29             break;
 30         }else {
 31             break;
 32         }
 33     }
 34     close(fd);
 35     return 0;
 36 }

请添加图片描述
请添加图片描述

2、共享内存

不需要read/write等接口读和写,因为已经映射到内存了
共享内存:在物理内存上申请的一段空间,由OS将其分别通过两个进程的页表映射到各自的虚拟内存中(进程双方就看到了同一份资源),然后将地址返回给用户,就可以通过这段空间进行通信。
系统会对所有的共享内存进行管理:先描述再组织

1. 创建共享内存(man shmget

创建共享内存
shmflg后面也可以跟权限:0644,一样的用分隔

grep -ER ‘IPC_CREAT’ /usr/include/:查看IPC_CREAT
可以看到其定义在vim /usr/include/bits/ipc.h中
在这里插入图片描述

2. 获取唯一性key(man get

在这里插入图片描述
只要两个进程ftok参数填一样的,那个获得的返回值key也是一样的,也就能指示同一个共享内存(管理共享内存的struct中存在标识它自己的key字段)

3. 删除共享内存(man shmctl

在这里插入图片描述
在*ctl中可以查看描述其对应资源的结构体(共享内存/消息队列/信号量)
在这里插入图片描述

第一个参数为共享内存标识码,第二个参数设置为IPC_RMID表示删除共享内存,最后一个暂时设置为NULL

4. 将进程关联到共享内存/去关联共享内存(man shmat

在这里插入图片描述
第二个参数和第三个参数暂时设为NULL0

// 用于不断查看共享内存信息
while :; do ipcs -m | head -3 && ipcs -m | grep "xupeng";echo "###########"; sleep 1; done

5. 获取共享内存中的内容

(因为已经通过shmat获得到了虚拟内存的地址,直接像打印buffer也就是数组中的内容一样打印)
在这里插入图片描述

6. 把内容写入共享内存

在这里插入图片描述

共享内存生命周期随OS
共享内存不提供任何同步互斥,彼此独立
共享内存通信是最快的;没有拷贝到文件中的操作,没有用户态内核态的转换
shim是用户层操作的概念
key是系统表示唯一性

grep -ER ‘struct ipc_perm’ /usr/include/ :查找ipc_perm
在这里插入图片描述

3、消息队列

1. 获取消息队列(man msgget

在这里插入图片描述
参数与shmget一样

2. 删除消息队列(man msgctl

在这里插入图片描述
信号的前缀是sem,semget/semctl…
发送消息时的结构体中的类型,可以指定两个进程进行通信,只有发送类型和接收类型一样的才能进行通信(type=0表示任何进程都可以接收这个消息)。

//client.c
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/ipc.h"
#include "sys/msg.h"

#define SIZE 100

struct Buf{
    long type;
    char text[SIZE];
};


int main() {
    key_t key = ftok("/home/xupeng", 10);
    if (key < 0) {
        perror("ftok");
        return 2;
    }

    int msgid = msgget(key, IPC_CREAT | 0644);
    if (msgid < 0) {
        perror("msgid");
        return 2;
    }

    struct Buf bf;
    bzero(&bf, sizeof(bf));
    bf.type = 1;
    bf.text[0] = 0;
    strcpy(bf.text, "hello world");

    msgsnd(msgid, &bf, strlen(bf.text), 1);


    return 0;
}
//server.c
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/ipc.h"
#include "sys/msg.h"

#define SIZE 100

struct Buf{
    long type;
    char text[SIZE];
};


int main() {
    key_t key = ftok("/home/xupeng", 10);
    if (key < 0) {
        perror("ftok");
        return 2;
    }

    int msgid = msgget(key, IPC_CREAT);
    if (msgid < 0) {
        perror("msgid");
        return 2;
    }

    struct Buf bf;

    size_t size = msgrcv(msgid, &bf, SIZE, 1, 0);
    printf("msgrcv return : %d", size);
    bf.text[size] = 0;
    printf("i get meg from client : %s\n", bf.text);
    

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值