我会用几篇博客总结一下在Linux中进程之间通信的几种方法,我会把这个开头的摘要部分在这个系列的每篇博客中都打出来

进程之间通信的方式

  • 管道
  • 消息队列
  • 信号
  • 信号量
  • 共享存储区
  • 套接字(socket)

进程间通信(五)—信号传送门:http://www.cnblogs.com/lenomirei/p/5656449.html

进程间通信(四)—共享存储区传送门:http://www.cnblogs.com/lenomirei/p/5651995.html

进程间通信(二)—消息队列传送门:http://www.cnblogs.com/lenomirei/p/5642575.html

进程间通信(一)—管道传送门:http://www.cnblogs.com/lenomirei/p/5636339.html

第三篇来了!前两篇访问量很多,真的是很感谢了

这次记下信号量的相关操作函数和方法,和以前一样会在博文的最后把测试代码贴出来!

学过操作系统这本书的话应该对信号量这个名词不会感到陌生,同时信号和信号量是不同的!

信号量多用于进程间的同步与互斥,简单的说一下同步和互斥的意思

同步:处理竞争就是同步,安排进程执行的先后顺序就是同步,每个进程都有一定的个先后执行顺序

互斥:互斥访问不可共享的临界资源,同时会引发两个新的控制问题(互斥可以说是特殊的同步)

竞争:当并发进程竞争使用同一个资源的时候,我们就称为竞争进程


简单说一下信号量的工作机制(因为真的很简单),可以直接理解成计数器(当然其实加锁的时候肯定不能这么简单,不只只是信号量了),信号量会有初值(>0),每当有进程申请使用信号量,通过一个P操作来对信号量进行-1操作,当计数器减到0的时候就说明没有资源了,其他进程要想访问就必须等待(具体怎么等还有说法,比如忙等待或者睡眠),当该进程执行完这段工作(我们称之为临界区)之后,就会执行V操作来对信号量进行+1操作。

临界区:不是个简单的区域!是加锁区间的代码!

临界资源:只能被一个进程同时使用(不可以多个进程共享),要用到互斥

我们可以说信号量也是进程间通信的一种方式,比如互斥锁的简单实现就是信号量,一个进程使用互斥锁,并通知(通信)其他想要该互斥锁的进程,阻止他们的访问和使用(老子正在用!@_@!)

其实信号量的操作函数和之前几个都是类似的,都是套路

  • 信号量的创建

一定会觉得熟悉的!和消息队列十分类似

  • 函数原型:int semget(key_t key, int nsems, int semflg);
  • 头文件:#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
  • 参数解析
    • key_t这个就不详细解释了,在消息队列篇就讲过了
    • nsems这个参数是指创造出几个信号量,准确的来说,semget这个函数创建出来一个信号量集(就是包含好多信号量的那种,可以简单的理解为信号量数组),这就是说创建有几个信号量的信号量集,可以选择只创建一个
    • semflg还是一样的,用到两个参数就够了IPC_CREAT 和 IPC_EXCL怎么用的话我在消息队列篇讲过了,就不多费口舌了

下面会看到信号量的一个缺点

  • 信号量的初始化

这说明信号量的初始化和创建是分开操作的,为什么是分开的,因为创建的时候没有给信号量的大小的参数,信号量是可以设置大的(不是只可以设置为1),当然设置为1就是互斥访问了,比如典型的生产者和消费者问题,但是如果像是读者写者中的读者信号量可不一样(因为可以有多个读者同时读),这里就不解释这个模型问题了。

缺点就是,一旦初始化和创建分开之后,就会有线程安全的问题,进程A刚创建一信号量还没有初始化,进程B便已经开始对该信号量进程P操作(哥哥!我还没赋值初始化!)根本无法进行-1操作,会阻塞的,我都不知道咋回事(亲身体验该错误)。。。

废话不多说了,函数还是套路

  • 函数原型:int semctl(int semid, int semnum, int cmd, ...);
  • 头文件:#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
  • 参数解析
    • 第一个semid就不多说了标识信号量用的,semget成功时候的返回值就是它
    • semnum这个就很有用了,刚才说了申请的是一个信号量集,这个参数就是告诉函数要初始化的是第几个信号量,该参数是从0开始的,0表示第一个信号量
    • cmd这次用这个SETVAL表示设置变量

当SETVAL被设置之后,可变参数列表的第四个参数就可以用上了,这里传入一个神奇的联合体(我在手册里找到了,但是死活用不了它,还是自己写了一个)

1 union semun {
2                int              val;    /* Value for SETVAL */
3                struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
4                unsigned short  *array;  /* Array for GETALL, SETALL */
5                struct seminfo  *__buf;  /* Buffer for IPC_INFO
6                                            (Linux-specific) */
7            };

就是它!就是它!就看第一个吧,其他我也没用了(英语我也看不太懂),val就是设置的初值啦,传入的时候直接传入就行了,参数就是一个变量(不用传指针)

这个函数一会删除我们还会遇到它,ctl就是control控制的简写,很多操作都有它

  • 信号量操作

一开始就写了操作的两种方式,一种叫P操作,一种叫V操作,都是通过一个函数实现的,这个函数还关联一个结构体

  • 函数原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
  • 头文件:#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
  • 参数解析
    • 第一个semid老朋友了
    • 第二个就是那个结构体了,贴出来看
    • 第三个表示你想同时操作几个信号量(为什么不是表示index of信号量呢,在结构体里有一起说),因为信号量集可能有很多信号量,每次一个一个操作不现实,这个参数就是提供一个同时操作的
1 struct sembuf
2 {
3     unsigned short sem_num;  /* semaphore number */
4     short          sem_op;   /* semaphore operation */
5     short          sem_flg;  /* operation flags */
6 };

sem_num就是index of信号量,你想操作的信号量的下标,sem_op是个短整型,这里给1或者-1分别表示V和P操作,而最后一个semflg手册中提到了两个

IPC_NOWAIT:一看就是IPC通用的,非等待,不让忙等待只好去休眠(或者别的,反正不能循环等待)

SEM_UNDO:这个一看就是信号量独有的,看英文也能看出来UNDO是取消之前做过的,什么意思呢,不得不提到,当进程在临界区工作(持有锁)的时候,是不允许挂(挂掉更不好啦)起的!!!这很重要(又要加篇幅啦!@_@),因为一旦挂起或者休眠,有可能就醒不了啦!(挂起有可能等待事件)这样不好,会让等待的其他进程饥饿,尤其是挂掉的时候,肯定就更糟糕了,根本就无法执行V操作,等待的进程就会无限的等待下去,递归申请信号量也是不好的,尤其是互斥信号量的时候会造成死锁。这时候UNDO就出来把之前做过的东西都取消掉,P操作就当没执行过,信号量又从0变回了1,皆大欢喜。

  • 信号量的删除

semctl又来了,和消息队列的删除类似

  • 函数原型:刚才刚写了~!
  • 头文件:刚才刚写了~!
  • 参数解析:刚才刚写了~!
  • 不一样的只是把SETVAL为换成IPC_RMID就行了(当然后面的可变参数列表也不要了,可以看一下测试的例子)

事已至此,基本操作就说完了,废话少说,show me the code

我的程序分为comm.h(公共头文件)  comm.c(封装基本函数) server.c(采用fork完成父子进程间使用信号量) 一共3个文件

基本功能:模拟线程安全(简单版本),父进程会打印"A""A"子进程会打印"B""B",因为执行顺序的原因可能会交叉打印,但我不要这样,我要让AA和BB分别连着!完成这个功能

先看之前的版本

交叉打印不是我想要的

功能完成连续打印

comm.h

 1 #include <stdio.h>
 2 #include <sys/ipc.h>
 3 #include <string.h>
 4 #include <unistd.h>
 5 #include <sys//sem.h>
 6 #include <stdlib.h>
 7 #include <errno.h>
 8
 9
10 #define _PATH_NAME_ "/tmp"
11 #define _PROJ_ID_ 0x6666
12
13 static int comm_create_sem(int flags,int num);
14 int create_sem(int num);
15 int get_sem();
16 void P_sem(int sem_id,int index);
17 void V_sem(int sem_id,int index);

comm.c

 1 #include "comm.h"
 2
 3 union semun {
 4                int              val;    /* Value for SETVAL */
 5                struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
 6                unsigned short  *array;  /* Array for GETALL, SETALL */
 7                struct seminfo  *__buf;  /* Buffer for IPC_INFO
 8                                            (Linux-specific) */
 9            };
10
11
12 static int comm_create_sem(int flags,int num)
13 {
14   key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
15   if(_key<0)
16   {
17     printf("%d:%s",errno,strerror(errno));
18   }
19   int sem_id;
20   if((sem_id=semget(_key,num,flags))<0)
21   {
22     printf("semget errno,%d:%s",errno,strerror(errno));
23   }
24   return sem_id;
25 }
26
27 int create_sem(int num)
28 {
29   int flags=IPC_CREAT | IPC_EXCL;
30   int sem_id=comm_create_sem(flags,num);
31   union semun v;
32   v.val=1;
33   if(semctl(sem_id,0,SETVAL,v)<0)//init
34   {
35     printf("init error,%d:%s",errno,strerror(errno));
36   }
37   return sem_id;
38 }
39
40 int get_sem()
41 {
42   int flags=0;
43   return comm_create_sem(flags,0);
44 }
45
46
47 void P_sem(int sem_id,int index)
48 {
49   struct sembuf s;
50   s.sem_num=index;
51   s.sem_op=-1;
52   s.sem_flg=0;
53   if(semop(sem_id,&s,1)<0)
54   {
55     printf("op errro,%d:%s",errno,strerror(errno));
56   }
57 }
58
59 void V_sem(int sem_id,int index)
60 {
61   struct sembuf s;
62   s.sem_num=index;
63   s.sem_op=1;
64   s.sem_flg=0;
65   if(semop(sem_id,&s,1)<0)
66   {
67     printf("op error,%d:%s",errno,strerror(errno));
68   }
69
70 }
71
72 void destory_sem(int sem_id)
73 {
74   semctl(sem_id,0,IPC_RMID);
75 }

server.c

 1 #include "comm.h"
 2
 3
 4
 5 int main()
 6 {
 7   int pid;
 8   pid=fork();
 9   if(pid>0)
10   {
11     //father
12     int sem_id=create_sem(1);
13     while(1)
14     {
15       P_sem(sem_id,0);
16       printf("A");
17       fflush(stdout);
18       sleep(1);
19       printf("A");
20       fflush(stdout);
21       V_sem(sem_id,0);
22     }
23   }
24   else
25   {
26     //child
27     while(1)
28     {
29       int sem_id=get_sem();
30       P_sem(sem_id,0);
31       printf("B");
32       fflush(stdout);
33       sleep(1);
34       printf("B");
35       fflush(stdout);
36       V_sem(sem_id,0);
37     }
38   }
39
40   return 0;
41 }

转载于:https://www.cnblogs.com/lenomirei/p/5649792.html

进程间通信(三)—信号量相关推荐

  1. Linux IPC进程间通信(三):信号量

    系列文章: Linux IPC进程间通信(一):管道 Linux IPC进程间通信(二):共享内存 Linux IPC进程间通信(三):信号量 Linux IPC进程间通信(四):消息队列 文章目录 ...

  2. 【 Linux 】进程间通信之信号量

    进程间通信方式---信号量 1.概念 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临 ...

  3. Linux进程间通信三 System V 信号量简介与示例

    1. System V信号量简介 SystemV信号量主要用于解决生产者和消费者问题,一个信号量能够控制多个资源,说它是信号量集也不为过. 2. API接口介绍 2.1 创建或打开信号量集 #incl ...

  4. 进程间通信(三)(消息队列,信号量)

    消息队列 本质: 一个内核中的优先级队列 通信原理: 在内核中创建一个优先级队列,多个进程通过对同一个队列添加或获取节点实现数据通信 特性: 自带同步与互斥,不会取到同一个节点 生命周期随内核 信号量 ...

  5. linux下清理信号量,Linux下进程间通信方式——信号量(Semaphore)

    1.信号量 信号量本质上是一个计数器(不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据 ...

  6. 210224阶段三信号量、互斥锁

    目录 一.学习的知识点 进程 线程 进程和线程 信号量和互斥锁 应用场景 二.上课没有听懂或者没有理解的地方 三.当天学习的收获 一.学习的知识点 ls -a 显示隐藏文件 cd ~ 回到home目录 ...

  7. Linux进程间通信(信号量)

    ★什么是信号量 为了防止出现多个程序同时访问一个共享数据资源而引发的问题,需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区.信号量就可以提供这样一种访问机制. ...

  8. linux 进程uhxuhao,linux 进程间通信三 消息队列以及实例

    转自 http://blog.csdn.net/liang890319/article/details/8280934 代码来自:嵌入式Linux应用开发标准教程 消息可以理解为写信给某个人,这里在应 ...

  9. Linux 进程间通信之 - 信号量

    前言 很久不用,基本概念虽然还记得,估计不会用了,写个demo练练手. 概念 信号量(信号灯)本质上是一个计数器,用于协调多个进程(包括但不限于父子进程)对共享数据对象的读/写.它不以传送数据为目的, ...

  10. Linux下进程间通信方式——信号量(Semaphore)

    https://www.cnblogs.com/wuyepeng/p/9748552.html

最新文章

  1. mysql thread safe_Windows环境下完全手工配置Apache、MySQL和PHP(Thread Safe)
  2. 独家 | 为什么Z世代会掉入网络错误信息的陷阱
  3. pytorch中arange()函数用法
  4. ZooKeeper官方文档资源
  5. 计算机网络现在成功,百收计算机网络努力的人是怎么成功的
  6. java模拟登陆_java-模拟登陆练习(示例代码)
  7. 阿里云应用高可用 AHAS 正式商用,可一键提升云上应用可用性
  8. Hystrix原理讲解
  9. .NET Framework也可以开发托管了
  10. AcWing 798. 差分矩阵
  11. 常见的设计模式 ----- 桥接模式
  12. oracle反生异常会回滚吗,Oracle transaction rollback 事务异常回滚问题分析
  13. mysql2008 密钥_怎么查看sql server 2008密钥
  14. Matplotlib调整字体大小
  15. Android adb 安装apk程序
  16. Ubuntu16.04下网易云音乐点击图标打不开——已解决
  17. 西南大学网络作业答案计算机,2019西南大学继续教育学院《计算机基础》作业答案...
  18. [学习]BES蓝牙芯片开发
  19. 模型损失函数变化曲线图_第3章 第6节 模型融合和提升的算法
  20. python京东自动签到_python 使用selenium登陆京东签到哪京豆

热门文章

  1. 关于poll机制应用及驱动
  2. spring2.5 mvc使用注解upload上传文件
  3. 201803考试批次2C 程序设计语言,201803考试批次2可视化程序设计(VB)D卷
  4. python中类的定义方法_python中类的定义方法
  5. matlab数据无量纲化_MATLAB数据预处理——归一化和标准化
  6. android 关闭jack_Android7.0 配置JACK支持多用户同时编译
  7. 栈的输出_栈和队列--十进制转化为二进制
  8. 如何将 CentOS迁移到 AlmaLinux?
  9. 新时代的网络工程师需要掌握哪些技能
  10. mysql 如何搜索自增列_mysql 如何搜索自增列