无名信号量——相关进程间同步

Suzkfly 2021-01-29 原文

  无名信号量可以在相关进程间进行同步,所谓相关进程暂时先简单的理解为父子进程,最后再详细的解释一下。在上一篇博客 无名信号量——线程间同步 https://www.cnblogs.com/Suzkfly/p/14336610.html中已经介绍过信号量相关的各个函数,其中sem_init第二个参数,如果传入的值是非0值,就表示使用的信号量是在进程间共享。

  按照这个理解,先来写一个简单的范例程序:

  1. 1 #include <stdio.h>
  2. 2 #include <semaphore.h>
  3. 3
  4. 4 int main(int argc, const char *argv[])
  5. 5 {
  6. 6 int pid = 0;
  7. 7 sem_t sem;
  8. 8 int ret = 0;
  9. 9
  10. 10 ret = sem_init(&sem, 1, 1); /* 初始化信号量的值为1 */
  11. 11 printf("ret = %d\n", ret);
  12. 12
  13. 13 pid = fork();
  14. 14
  15. 15 if (pid == 0) { /* 子进程 */
  16. 16 printf("I'm child\n");
  17. 17 printf("child sem_wait...\n");
  18. 18 sem_wait(&sem);
  19. 19 printf("child sem_wait ok\n");
  20. 20 } else if (pid > 0) { /* 父进程 */
  21. 21 sleep(1);
  22. 22 printf("I'm parent\n");
  23. 23 printf("parent sem_wait...\n");
  24. 24 sem_wait(&sem);
  25. 25 printf("parent sem_wait ok\n");
  26. 26 }
  27. 27
  28. 28 return 0;
  29. 29 }

运行结果:

 

 

 代码分析:

  在代码第10行初始化了信号量初值为1,在父进程中睡眠1秒,表示让子进程先行,子进程wait_sem会成功,但是子进程sem_wait之后,信号量的值就变为0了,到了父进程它再执行sem_wait的时候应该不成功,但从结果上来看,父进程sem_wait也成功了。其实稍微分析一下就能理解,因为fork函数会将进程资源复制一份,此时虽然sem地址值是一样的,但实际上它们指向的是不同的sem。

  那么要解决这个问题的思路就很清晰了,让两个进程都能找到同一个sem就可以了。

  mmap在framebuffer程序中用到过,它能用来将一个文件映射到内存中,通过操作内存达到操作文件的效果,并且它映射出来的地址是进程共享的。简单介绍一下mmap函数:

函数原型 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
头文件 sys/mman.h
功能 虚拟地址映射
参数

[in]:addr:指定映射的起始地址,如果填入NULL,则表示由系统自动分配地址,一般都填入NULL。

[in]:length:映射的地址空间大小。

[in]:prot:映射区域的权限。

      #PROT_READ:可读

      #PROT_WRITE:可写

      #PRTO_EXEC:可执行

      #PRTO_NONE:不能被访问

[in]:flags: 标志位参数,常用标志如下(其他标志也可以在man手册中查到)

      #MAP_SHARED:与其他进程共享

      #MAP_PRIVATE:不与其他进程共享

        MAP_SHARED与MAP_PRIVATE二者必选其一。

      #MAP_ANONYMOUS:同MAP_ANON,匿名映射,映射区不与文件关联。

[in]:fd:文件描述符,如果是匿名映射,则该处填入-1

[in]:offset:文件偏移量,必须是4096的整数倍。

返回 成功返回映射区地址,失败返回MAP_FAILED,其值为((void*)-1),由于mmap很容易失败,因此必须要去接收mmap的返回值。

 

  mmap映射的地址用完之后要使用munmap释放,munmap的函数介绍如下:

函数原型 int munmap(void *addr, size_t length);
头文件

sys/mman.h

参数

[in]:addr:映射区首地址,就是mmap返回的那个地址。

[in]:length:映射区长度,就是mmap传入的长度。

返回 成功返回0,失败返回-1

  我不知道如果不使用munmap释放会造成什么后果,我自己写了测试程序,发现无论用不用munmap释放,用free命令查看都看不出区别, 并且用valgrind工具也看不出来。

  介绍这两个函数是为了得到一块共享的匿名地址区域。

  改后代码如下:

  1. 1 #include <stdio.h>
  2. 2 #include <sys/mman.h>
  3. 3 #include <string.h>
  4. 4 #include <semaphore.h>
  5. 5
  6. 6 int main(int argc, const char *argv[])
  7. 7 {
  8. 8 sem_t *p_sem = NULL;
  9. 9 int pid = 0;
  10. 10 int ret = 0;
  11. 11
  12. 12 p_sem = mmap(NULL, /* 由系统分配地址 */
  13. 13 sizeof(sem_t), /* 申请的地址大小 */
  14. 14 PROT_READ | PROT_WRITE, /* 读写权限 */
  15. 15 MAP_SHARED | MAP_ANON, /* 进程共享,映射区与文件不关联 */
  16. 16 -1, /* 文件描述符,匿名映射传入-1 */
  17. 17 0); /* 偏移量 */
  18. 18
  19. 19 printf("p_sem = %p\n", p_sem);
  20. 20
  21. 21 ret = sem_init(p_sem, 1, 1); /* 进程间使用的信号量,初值为1 */
  22. 22 printf("ret = %d\n", ret);
  23. 23
  24. 24 pid = fork();
  25. 25
  26. 26 if (pid == 0) { /* 子进程 */
  27. 27 printf("I'm child\n");
  28. 28 printf("child sem_wait...\n");
  29. 29 sem_wait(p_sem);
  30. 30 printf("child sem_wait ok\n");
  31. 31 } else if (pid > 0) { /* 父进程 */
  32. 32 sleep(1);
  33. 33 printf("I'm parent\n");
  34. 34 printf("parent sem_wait...\n");
  35. 35 sem_wait(p_sem);
  36. 36 printf("parent sem_wait ok\n");
  37. 37 }
  38. 38
  39. 39 return 0;
  40. 40 }

执行结果:

 

   此时由于子进程获取到了信号量并且没有释放,因此父进程将一直阻塞。该程序中mmap映射的区域不会因为fork而被一分为二,因此p_sem指向的区域其实是同一个东西。malloc有类似的功能,但是malloc出来的地址空间在fork之后也会变为两份。

  那么问题来了,现在我知道了p_sem的地址,我如果另外编写一个程序去调用sem_post会怎样呢。设改程序为程序A,另一个测试程序称为程序B,程序B的代码如下:

  1. 1 #include <stdio.h>
  2. 2 #include <semaphore.h>
  3. 3
  4. 4 int main(int argc, const char *argv[])
  5. 5 {
  6. 6 sem_t *p_sem = (sem_t *)0xb7758000; /* 该地址为p_sem的地址 */
  7. 7
  8. 8 sem_post(p_sem);
  9. 9
  10. 10 return 0;
  11. 11 }

  注意上面程序传入的p_sem的值根据另一个程序打印出来的结果为准。在程序A的父进程等待信号量的时候执行程序B,程序A能继续执行下去吗,程序B的运行结果如下:

 

   段错误!

  我想如果程序B能运行下去,并且让程序A成功获得信号量,那才怪事了,这正是虚拟地址存在的意义啊。这也解释了无名信号量在进程将的同步应该是父子进程的原因。

发表于
2021-01-29 16:21 
Suzkfly 
阅读(0
评论(0
编辑 
收藏

 

版权声明:本文为Suzkfly原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Suzkfly/p/14345613.html

无名信号量——相关进程间同步的更多相关文章

  1. Linux编程 5 (目录重命名与移动mv,删除文件rm,目录创建mkdir删除rmdir,查看file,cat,more,tail,head)

    Linux编程 5 (目录重命名与移动mv,删除文件rm,目录创建mkdir删除rmdir,查看file,ca […]...

  2. Linux编程 23 shell编程(结构化条件判断 命令if -then , if-then … elif-then …else,if test)

    Linux编程 23 shell编程(结构化条件判断 命令if -then , if-then … […]...

  3. Linux编程 – 使用C在mysql中插入数据

    1. 代码编写 #include <stdlib.h>#include <stdio.h&g […]...

  4. 进程——父子进程共享

    一、fork()   1. 在谈fork之前,先简单说一下进程的相关知识点。   (1)进程不同于程序是动态运 […]...

  5. Linux编程 22 shell编程(输出和输入重定向,管道,数学运算命令,退出脚本状态码)

    Linux编程 22 shell编程(输出和输入重定向,管道,数学运算命令,退出脚本状态码) 1. 输出重定向 […]...

  6. 10个常用的ps命令总结,参数

    Linux系统中10个常用的ps命令总结   PS 命令是什么 查看它的man手册可以看到,ps命令 […]...

  7. Linux编程 8 (挂载mount,查看磁盘df du,搜索grep,压缩zgip,归档tar) – 花阴偷移

    Linux编程 8 (挂载mount,查看磁盘df du,搜索grep,压缩zgip,归档tar) 一. 挂载 […]...

  8. Linux编程 21 shell编程(环境变量,用户变量,命令替换) – 花阴偷移

    Linux编程 21 shell编程(环境变量,用户变量,命令替换) 一.概述   这篇介绍shell的变量使 […]...

随机推荐

  1. EdoGantt易度甘特图功能示例和开发手册

    易度甘特图具备如下几大特性: 1. 甘特图显示功能:根据任务的开始日期/工期、任务相关性、项目日历、项目开始日 […]...

  2. spring-ssm整合

    SSM框架整合 目录结构20.cnblogs.com/blog/1678155/202108/1678155- […]...

  3. 华为-eNSP模拟器路由器无法正常启动一直显示“#”

     问题项如截图: 解决方案:   1. 打开自己电脑的控制面板 –>> 系统和安全 & […]...

  4. SQL语句汇总(三)——聚合函数、分组、子查询及组合查询

    拖了一个星期,终于开始写第三篇了。走起!   聚合函数: SQL中提供的聚合函数可以用来统计、求和、求最值等等 […]...

  5. 想要学深度学习但是没有GPU?我帮你找了一些不错的平台

    本文始发于个人公众号:TechFlow,原创不易,求个关注 上次给大家推荐了免费的spark集群之后,就有很多 […]...

  6. 这一次搞懂SpringBoot核心原理(自动配置、事件驱动、Condition)

    @ 目录 前言 正文 启动原理 事件驱动 自动配置原理 Condition注解原理 总结 前言 SpringB […]...

  7. ssl证书国内如何申请?在哪里申请ssl数字证书?

    ssl证书越来越多的被运用到网站的安全策略上。主要是能够加密网站和客户之间的信息传递,以及标示自己的网站身份, […]...

  8. PS 字体在系统上 但需要更改版面

      实际解决方法 对于自己确实有效 不知道其他的了 方法1:菜单栏 进编辑——首选项——常规——确定,重启PS […]...

展开目录

目录导航