• 进程间通信学习笔记


    进程间通信的目的

    进程间通信的目的主要体现在5个方面:

    • 传输数据:一个进程可能需要将它的数据传输给另外一个进程
    • 资源共享:多个进程之间可能想共享同样的资源
    • 通知事件:一个进程可能需要向另一个进程通知它们发生了某事件
    • 进程控制:某些进程可能希望控制另一个进程的执行。

    进程通信多种方式的原因

    要实现进程间的相互通信就必须让这些进程“看到”同一份资源。但进程之间是相互独立的,一个进程无法看到另一个进程的资源。所以这份公共资源不能属于这些进程,而是由操作系统系统提供。操作系统提供公共资源的方式可以以文件的方式、队列的方式、也有可能是以原始内存块的方式,这也就导致了进程通信的方式有多种。

    进程通信的多种方式

    管道

    • 匿名管道
    • 命名管道

    System V IPC

    • System V 共享内存
    • System V 消息队列
    • System V 信号量

    管道

    匿名管道

    匿名管道的五个特点:

    • 1、只能单向通信的通信信道。
    • 2、面向字节流。
    • 3、只能“具有血缘关系”的进程进行通信,例如父子进程。
    • 4、自带同步机制,写入是原子性的。
    • 5、生命周期随通信进程。

    站在文件描述符的角度来看管道(此处需要掌握文件描述符的知识)
    在这里插入图片描述
    匿名管道是单向通信的通信信道,且因为子进程会继承父进程相关信息,所以父进程需要先打开读端和写端,后面再根据需要关闭读端/关闭写端,否则子进程只拥有读端/写端。管道的本质就是文件。

    创建匿名管道

    #include 
    int pipe(int fildes[2]);
    //fildes是文件描述符数组,一个输出型参数,用于获取读端/写端的文件描述符
    //fildes[0]是读端、fildes[1]是写端
    //成功返回0
    //失败返回错误代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    测试

      1 #include <stdio.h>                                                                                                                                                  
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 #include <sys/types.h>
      6 #include <sys/wait.h>
      7 
      8 int main(void)
      9 {
     10   int pipe_fd[2] = {0};
     11   if(pipe(pipe_fd) != 0) //创建匿名管道
     12   {
     13     return 1;
     14   }
     15   printf("pipe_fd[0] = %d, pipe_fd[1] =  %d\n",pipe_fd[0], pipe_fd[1]);
     16   
     17   pid_t id = fork();
     18   //打算让子进程写入、父进程读取
     19   if(id < 0)
     20   {
     21     perror("fork");
     22     return 2;
     23   }
     24   else if(id == 0)
     25   {
     26     //child process
     27     //写入,则关闭读端
     28     close(pipe_fd[0]);
     29 
     30     //写入,按照文件的方式
     31     const char *msg = "hello parent, I am child";
     32     while(1)
     33     {
     34        write(pipe_fd[1], msg, strlen(msg));
     35        sleep(1);
     36     }
     37     close(pipe_fd[1]);
     38     exit(0);
     39   }
     40   else
     41   {
     42 
     43     //father process
     44     //读取,关闭写端
     45     close(pipe_fd[1]);
     46     char buf[64];
     47     while(1)
     48     {
     49       buf[0] = 0;
     50       ssize_t st = read(pipe_fd[0], buf, sizeof(buf));
     51       if(st > 0)
     52       {                                                                                                                                                             
     53         buf[st] = 0;
     54         printf("child send to pipe :%s\n", buf);
     55       }
     56       else if(st == 0)
     57       {
     58         printf("pipe file close, child quit\n");
     59         break;
     60       }
     61       else
     62       {
     63         break;                                                                                                                                                      
     64       }
     65     }
     66     //father wait
     67     int status = 0;
     68     pid_t ret = waitpid(-1, &status, 0);
     69     if(ret > 0)
     70     {
     71       //wait success
     72       //获取退出子进程退出状态
     73       printf("exit code:%d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
     74     }
     75     close(pipe_fd[0]);
     76   }
     77 
     78   return 0;
     79 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    管道通信的4种情况

    • a.读端不读/读端读的慢,写端等待读端
    • b.读端关闭,写端会收到SIGPIPE信号直接终止
    • c.写端不写/写端写的慢,读端会等待写端
    • d.写端关闭,读端会读完pipe内的数据,然后再读就会读到0,表明读到文件末尾,然后读端关闭。

    管道是有大小的,我们可以通过程序知道当前我们创建的匿名管道的大小。

      1 #include <stdio.h>                                                                                                                                                  
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 #include <sys/types.h>
      6 #include <sys/wait.h>
      7 
      8 //编写程序查看当前所创建的管道大小
      9 int main(void)
     10 {
     11   int pipe_fd[2] = {0};
     12   if(pipe(pipe_fd) != 0)
     13   {
     14     return 1;
     15   }
     16   
     17   //计划子进程写入、父进程读取
     18   pid_t id = fork();
     19   if(id < 0)
     20   {
     21     perror("fork");
     22     return 2;
     23   }
     24   else if(id == 0)
     25   {
     26     //child process
     27     close(pipe_fd[0]);
     28     char c = 'x';
     29     int count = 0;
     30     while(1)
     31     {
     32       write(pipe_fd[1], &c, 1);
     33       count++;
     34       printf("count: %d\n", count);
     35     }
     36 
     37     close(pipe_fd[1]);
     38     exit(0);
     39   }
     40   else
     41   {
     42     //father process
     43     close(pipe_fd[1]);
     44     sleep(20); //父进程休眠时间根据子进程来定,要保证休眠时间内管道被写满
     45 
     46     char buf[64];
     47     while(1)
     48     {
     49       buf[0] = 0;//每读取一次,把缓冲区清零
     50       ssize_t st = read(pipe_fd[0], buf, sizeof(buf));                                                                                                              
     51       if(st > 0)
     52       {
     53         buf[st] = 0;
     54         printf("child send to pipe :%s\n", buf);
     55       }
     56       else if(st == 0)
     57       {
     58         printf("pipe file close, child quit\n");
     59         break;
     60       }
     61       else
     62       {
     63         break;
     64       }
     65     }
     66     //father wait
     67     int status = 0;
     68     pid_t ret = waitpid(-1, &status, 0);
     69     if(ret > 0)
     70     {
     71       //wait success
     72       //获取退出子进程退出状态
     73       printf("exit code:%d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
     74     }
     75     close(pipe_fd[0]);
     76   }
     77                                                                                                                                                                     
     78   return 0;
     79 }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    得到的现象是,写端快速写入,知道写了很多次后,就暂时不写(实际上不是不写,而是在等待读端),
    在这里插入图片描述
    因为一次写入一字节,而写入65536字节后就开始等待读端了,说明此时所创建的匿名管道满了,可知大小为65536字节,也就是64KB大小。

    命名管道

    命名管道的5个特点:

    • 1、只能单向通信的通信信道
    • 2、面向字节流
    • 3、用于“没有血缘关系”的进程通信
    • 4、写端写入是原子性的,自带同步机制
    • 5、管道生命周期随通信进程

    命名管道与匿名管道的特点差异在于:
    匿名管道只用于“具有血缘关系”的进程之间相互通信
    命名管道用于“不具有血缘关系”的进程之间相互通信

    创建命名管道
    方式一:在命令行上创建,使用下面这个命令:

    mkfifo filename
    
    • 1

    方式二:命名管道也可以从程序里创建,相关函数有:

    #include 
    #include 
    int mkfifo(const char *filename,mode_t mode);
    //成功返回0,失败返回-1
    //filename:创建出的管道文件,路径+文件名
    //mode:管道文件的权限
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
      1 #include <stdio.h>
      2 #include <sys/stat.h>
      3 #include <sys/types.h>
      4 int main(void)                                                                                                                                                      
      5 {
      6   //创建命名管道,并判断是否创建成功
      7   if(mkfifo("./fifo", 0666) < 0)
      8   {
      9     perror("mkfifo");
     10     return 1;
     11   }
     12 
     13   return 0;
     14 }         
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    创建成功
    在这里插入图片描述
    在这里插入图片描述

    但它的权限是rw-rw-r–,按8进制来显示则是0664,实际上它受到了权限掩码的影响。

    //使用umask查看权限掩码
    [YDY@VM-0-2-centos fifo]$ umask
    0002
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    上述例子:my_mode = 0666 & (~0002) = 0664.
    解决办法之一:将权限掩码设置为0,即可不受权限掩码的影响。

    //设置权限掩码
    mode_t umask(mode_t mask)
    
    • 1
    • 2

    命名管道的测试
    上述已经给出了server.c文件,并创建出了一个命名管道。命名管道用于不具有血缘关系之间的进程进行相互通信,所以可以再创建出一个程序(运行后变成另外一个进程),以下是这次测试所创建的文件

    [YDY@VM-0-2-centos fifo]$ ls -l
    total 16
    -rw-rw-r-- 1 YDY YDY 572 Jul 27 14:56 client.c
    -rw-rw-r-- 1 YDY YDY 123 Jul 27 14:26 comm.h
    -rw-rw-r-- 1 YDY YDY 146 Jul 27 14:14 Makefile
    -rw-rw-r-- 1 YDY YDY 970 Jul 27 14:59 server.c
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    Makefile文件内容
      1 .PHONY:all
      2 all: client server
      3 
      4 client : client.c
      5     gcc -o $@ $^
      6 server:server.c
      7     gcc -o $@ $^
      8 
      9 .PHONY:clean
     10 clean:
     11     rm -f client server fifo                                                                                                                                        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    comm.h文件内容
     1 #include <stdio.h>
      2 #include <sys/types.h>
      3 #include <sys/stat.h>
      4 #include <fcntl.h>
      5 #include <unistd.h>
      6 #include <string.h>           
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    server.c文件内容
      1 #include "comm.h"                                                             
      2 #include <sys/wait.h>
      3 #include <stdlib.h>
      4 
      5 int main(void)
      6 {
      7   //创建命名管道,并判断是否创建成功
      8   umask(0);
      9   if(mkfifo("./my_fifo", 0666) < 0)
     10   {
     11     perror("mkfifo");
     12     return 1;
     13   }
     14   //打开管道文件
     15   int fd =  open("./my_fifo", O_RDONLY);
     16   if(fd < 0)
     17   {
     18     perror("open");
     19     return 2;
     20   }
     21 
     22   //开始业务逻辑
     23   while(1)
     24   {
     25     char buf[64];
     26     ssize_t st = read(fd, buf, sizeof(buf) - 1);
     27     if(st > 0)
     28     {
     29       buf[st] = 0;
     30       if(strcmp(buf, "show") == 0)
     31       {
     32         if(fork() == 0)
     33         {
     34           //child
     35           execl("/usr/bin/ls", "ls", "-l", NULL);
     36           exit(0);
     37         }
     38         waitpid(-1, NULL, 0);
     39       }
     40       else
     41       {
     42          printf("server read# %s\n", buf);
     43       }
     44     }
     45     else if(st == 0)
     46     {
     47       //读端、写端关闭
     48       printf("client quit!\n");
     49       break;
     50     }
     51     else
     52     {
     53       perror("read");                                                         
     54       break;
     55     }
     56 
     57   }
     58   close(fd);
     59   return 0;                                                                   
     60 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    client.c文件内容
      1 #include "comm.h"                                                             
      2 
      3 int main(void)
      4 {
      5   //这里就不需要创建管道文件了,直接打开
      6   int fd = open("./my_fifo", O_WRONLY);
      7   if(fd < 0)
      8   {
      9     perror("open");
     10     return 2;
     11   }
     12   
     13   //业务逻辑部分
     14   while(1)
     15   {
     16     printf("输入# ");
     17     fflush(stdout); //立即刷新
     18 
     19     //先将标准输入里的数据拿到进程内部
     20     char buf[64];
     21     ssize_t st = read(0, buf, sizeof(buf) - 1);
     22     if(st > 0)
     23     {
     24       buf[st - 1]= 0;
     25       //将buf内的数据写入管道文件
     26       write(fd, buf, strlen(buf));
     27     }
     28   }
     29   close(fd);
     30   return 0;
     31 } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行截图
    在这里插入图片描述
    管道文件的数据不刷新到磁盘上。
    在这里插入图片描述
    在这里插入图片描述
    为了让不同的进程看到同一个文件,这个文件必须有文件名+文件路径。

    System V标准

    管道是基于文件的进程间通信方式,而Systm V标准的进程间通信方式是专门在OS层面定制的,是同一主机内的进程间通信方案。

    System V进程间通信,一定存在专门用来通信的system call接口。
    System V 通信的三种方式:

    • System V 共享内存
    • System V 消息队列
    • System V 信号量

    System V 共享内存

    共享内存的原理:
    在这里插入图片描述
    1、通过某system call,在内存中创建一份内存空间。
    2、通过某system call, 让参与通信的进程都“挂接”到这份内存。

    这就使得不同的进程能够看到同一份资源,进而达到通信目的。


    准备工作
    1、共享内存可能同时存在多份,所以OS会对共享内存进行管理,管理的办法是“先描述,后组织”。共享内存描述的方法是将它的各种信息存放在结构体中
    2、共享内存有唯一标识它的标识符,目的是让不同进程能够识别到同一份共享内存资源。这个标识符需要由用户设定。


    共享内存部分system call接口

    • 创建/获取共享内存
    #include 
    #include 
    int shmget(key_t key, size_t size, int shmflag);
    
    • 1
    • 2
    • 3
    • 控制共享内存
    #include 
    #include 
    int shmctl(int shmid, int cmd, shmid_ds *buf);
    
    • 1
    • 2
    • 3
    • 关联共享内存
    #include 
    #include 
    void *shmat(int shmid, const void *shmaddr, int shmflag);
    
    • 1
    • 2
    • 3
    • 去关联共享内存
    #include 
    #include 
    int shmdt(const void *shmaddr);
    
    • 1
    • 2
    • 3

    创建/获取共享内存

    #include 
    #include 
    int shmget(key_t key, size_t size, int shmflag);
    
    • 1
    • 2
    • 3

    key是需要用户自己设定的,只要形成key的算法和原始数据相同,就能形成同一个ID,保证不同的进程能够看到同一份共享内存资源。

    key可以使用函数ftok生成

    ftok作用:将路径名和项目标识符转换为System V IPC的key
    #include 
    #include 
    key_t ftok(const char* pathname, int proj_id);
    //返回值,成功返回一个key值,失败返回-1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    pathname必须是已经存在的可访问文件,proj_id由用户自己填写,虽然proj_id是一个int类型,但是到现在为止,它只使用低8位。如果使用同一个proj_id去标定同一个文件,ftok的返回值是一样的。

    shmget中的参数size
    获取的共享内存大小,建议为当前所在机器物理页面大小的整数倍。因为共享内存申请的单位是。假设,当前机器的一个物理页面大小是4KB(4096字节),而我们申请了4097字节,OS就会申请2个页面,但它只会给我们4097个字节使用,那么就浪费了很大的一段内存空间。

    shmget中的参数shmflag
    可填的几种值:

    • 0:创建一个共享内存,如果已经存在,返回当前已经存在的共享内存
    • IPC_CREAT:创建一个共享内存,如果已经存在,返回当前已经存在的共享内存
    • IPC_CREAT | IPC_EXCL:如果共享内存不存在,创建一个;如果共享内存已经存在,则返回错误。
    • 还可以在前面的基础上|上共享内存段指明的权限

    返回值
    成功返回一个有效的shmid(共享内存标识符)
    失败返回-1

    key VS shmid
    key:只是用于在系统层面进行标识唯一性的,不能用于管理共享内存
    shmid:是OS给用户返回的ID,用于在用户层管理共享内存。

    建议一定要在最后一个参数的基础上|指明共享内存段的权限,否则后面访问不了共享内存段(访问不了,去访问就会出现段错误),那还如何利用共享内存段实现进程间的通信呢?


    查看共享内存的命令ipcs -m

    [YDY@VM-0-2-centos shared_memory]$ ipcs -m
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status      
    0x33010c0a 10         YDY        666        4096       1              
    
    • 1
    • 2
    • 3
    • 4
    • 5

    查看System V IPC资源的命令ipcs

    [YDY@VM-0-2-centos shared_memory]$ ipcs
    
    ------ Message Queues --------
    key        msqid      owner      perms      used-bytes   messages    
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status      
    0x33010c0a 10         YDY        666        4096       1          
    
    ------ Semaphore Arrays --------
    key        semid      owner      perms      nsems     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    删除共享内存的命令ipcrm -m shmid

    [YDY@VM-0-2-centos shared_memory]$ ipcrm -m 10
    [YDY@VM-0-2-centos shared_memory]$ ipcs -m
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status  
    
    • 1
    • 2
    • 3
    • 4
    • 5

    控制共享内存(只了解共享内存的删除)

    #include 
    #include 
    int shmctl(int shmid, int cmd, shmid_ds *buf);
    
    • 1
    • 2
    • 3

    共享内存的删除,cmd使用宏IPC_RMID即可,第三个参数设置为NULL。


    共享内存的关联和去关联。

    #include 
    #include 
    //关联
    void *shmat(shmid, const void* shmaddr, int shmflag);
    //去关联
    int shmdt(const void *shmaddr);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果关联成功, shmat返回虚拟地址。

    利用共享内存实现通信的操作,在共享内存关联成功之后,去关联之前。


    共享内存的测试
    共有如下测试文件

    [YDY@VM-0-2-centos shared_memory]$ ls -l
    total 16
    -rw-rw-r-- 1 YDY YDY 675 Jul 28 09:12 client.c
    -rw-rw-r-- 1 YDY YDY 168 Jul 28 08:47 comm.h
    -rw-rw-r-- 1 YDY YDY 141 Jul 27 16:43 Makefile
    -rw-rw-r-- 1 YDY YDY 689 Jul 28 09:08 server.c
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    Makefile文件
     1 .PHONY:all
     2 all : client server
     3 
     4 client : client.c
     5     gcc -o $@ $^
     6 server : server.c
     7     gcc -o $@ $^
     8 
     9 .PHONY:clean                                                                  
    10 clean:
    11     rm -f client server
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    comm.h文件
      1 #include <stdio.h>
      2 #include <sys/types.h>
      3 #include <sys/shm.h>
      4 #include <sys/ipc.h>                                                         
      5 #include <unistd.h>
      6 
      7 
      8 #define PATHNAME "./"
      9 #define PROJ_ID 0X333
     10 #define SIZE 4096
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    server.c文件
      1 #include "comm.h"                                                             
      2 #include <sys/shm.h>
      3 int main(void)
      4 {
      5   //创建共享内存
      6   key_t key = ftok(PATHNAME, PROJ_ID);
      7   if(key < 0)
      8   {
      9     perror("ftok");
     10     return 1;
     11   }
     12   int shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL|0666);
     13   if(shmid < 0)
     14   {
     15     perror("shmget");
     16     return 2;
     17   }
     18   printf("key: %d, shmid :%d\n", key, shmid);
     19   //关联共享内存
     20   char* mem = (char*)shmat(shmid, NULL, 0);
     21   if(mem == NULL)
     22   {
     23     perror("shmat");
     24     return 1;
     25   }
     26   printf("shm attach success!\n");
     27   sleep(10);
     28   //通信
     29   //读取
     30   while(1)
     31   {
     32     sleep(1);
     33     printf("%s\n", mem);
     34   }
     35 
     36   //共享内存去关联
     37   shmdt(mem);
     38   printf("shm detaches success!\n");
     39 
     40   return 0;
     41 }                                    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    client.c文件
      1 #include "comm.h"
      2 
      3 int main(void)
      4 {
      5   //映射到同一块共享内存
      6   key_t key = ftok(PATHNAME, PROJ_ID);
      7   if(key < 0)
      8   {
      9     perror("ftok");
     10     return 1;
     11   }
     12   int shmid = shmget(key, SIZE, IPC_CREAT | 0666);//存在,则直接获取         
     13   if(shmid < 0)
     14   {
     15     perror("shmget");
     16     return 2;
     17   }
     18   //关联共享内存
     19   char* mem = (char*)shmat(shmid, NULL, 0);
     20   printf("shm attches success!\n");
     21 
     22   //写入
     23   char c = 'A';
     24   while(c <= 'Z')
     25   {
     26     mem[c - 'A'] = c;
     27     c++;
     28     mem[c - 'A'] = 0;
     29     sleep(2);
     30   }
     31   
     32   //去关联
     33   shmdt(mem);
     34   printf("shm detaches success!\n");
     35 
     36   shmctl(shmid, IPC_RMID, 0);
     37   printf("shm delete success!\n");
     38   return 0;
     39 }                             
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    共享内存是目前所有进程间通信方式中最快的一种。

    System V 信号量

    准备知识:
    1、临界资源:能够被多个执行流同时访问的资源。
    2、临界区:用来访问临界资源的代码
    3、原子性:一件事情要么一口气做完,要么不做。没有中间态。
    4、互斥:任意时刻只允许有一个执行流(单核CPU)访问临界资源。

    管道、共享内存、消息队列等是以传输数据为目的的,而信号量不是以传输数据为目的的,通过资源共享的方式,来达到进程同步和互斥的目的。信号量的本质,是一个计数器,用来衡量临界资源中资源数目的。

  • 相关阅读:
    c 技巧 之 ungetc 函数 回退字符
    Hadoop3教程(十七):MapReduce之ReduceJoin案例分析
    将bitmap转化为1位黑白像素图像(仅保留黑色,其它颜色会删除)
    手把手带你学python—牛客网python基础 生成字典
    VUE搭建云音乐播放器(App版本)
    Java多线程/线程池
    c++ logic_error() 异常的使用方法
    上海张江×百度飞桨打了个样,AI赋能这事儿可算有“参考答案”了
    Flink集群部署
    SkeyeVSS果园防盗智能监控系统 保障果农收益好帮手
  • 原文地址:https://blog.csdn.net/qq_56870066/article/details/126001246