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

来源:互联网 发布:鲜花抢单软件 编辑:程序博客网 时间:2024/06/05 15:38

1、为什么要用信号量

       管道和消息队列都是两个或多个进程访问一个共享资源,而为了防止出现因多个线程同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就是提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来协调进程对共享资源的访问,其中共享内存的使用就要用信号量。

开始之前先了解以下概念

*****临界区和临界资源

       临界资源:多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。 对于临界资源的访问,必须是互诉进行。也就是当临界资源被占用时,另一个申请临界资源的进程会被阻塞,直到其所申请的临界资源被释放。
       临界区:每个进程中访问临界资源的那段代码称为临界区。

*****同步与互斥

互斥:进程互斥是进程之间的间接制约关系。互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。对于一个进程进入临界区使用临界资源时,另一个进程必须等待。只有当使用临界资源的进程退出临界区后,这个进程才会解除阻塞状态。

同步:进程同步是进程之间的直接制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

*****原子
原子操作:指的是该操作完成的动作非0即1。只有两种状态,要么是完成了,要么是没有完成,没有中间状态。

2、概念

       信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而通过控制其他的通信资源(文件、外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能,描述临界资源的个数,用数组保存,可用下标查看。

      当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断是否可用。大于0,资源可以请求;等于0,无资源可用,进程就会进入睡眠状态直至资源可用。

      当进程不再使用一个信号量控制的共享资源时,信号量的值为-1,对信号量的值进行的增减操作均为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。而在信号量的创建及初始化上,不能保证操均为原子型。

2、工作原理

     信号只能进行两种操作:等待和发送信号,即P(sv)和V(sv),具体如下:(信号量sv)

     P(sv):如果sv的值大于0,就给它减1,;如果它的值为0,就挂起该进程的执行

     V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行;如果没有进程因等待sv而挂起,就给它加1。

3、信号量机制

信号量的本质不具有数据交换的功能,但是其在进程间通信中负责数据操作的互斥及同步的功能,从这一点上来看,信号量也是进程间通信的一种方式。

system V版本:允许一次创建多个信号量,叫信号量集;缺点:创建和初始化分离开来。

Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,但这些函数都是对成组的信号量值进行操作的。


semctl() 函数:得到一个信号量集标识符或创建一个信号量集对象并返回信号量集标识符在 semid 标识的信号量集上,或者该集合的第 semnum 个信号量上执行 cmd 指定的控制命令。

-------semid:信号量集标识符

-------semnum:信号量集数组上的下标,表示某一个信号量

-------cmd:

              IPC_STAT :从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中
              IPC_SET   :设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值
              IPC_RMID:从内核中删除信号量集合

信号量集合索引起始于零,根据 cmd 不同,这个函数有三个或四个参数。当有四个参数时,第四个参数的类型是 union。调用程序必须按照上图方式定义这个联合体。注意:该联合体没有定义在任何系统头文件中,因此得用户自己声明。 <Centos 下确实是这样,但是UNIX下不同,不需要自己定义声明>


semget()函数:得到一个信号量集标识符或创建一个信号量集对象并返回信号量集标识符。
-----key:由ftok()函数的得到,和消息队列中的一样,具体可查看本人上一篇博客。
-----nsems:创建信号量集中信号量的个数,该参数只在创建信号量集时有效。成功:返回信号量集的标识符;失败:返回-1

-----semflg:

            IPC_CREAT:若内核中不存在键值与key相等的信号量集,则创建,否则,返回此信号量集的标识符
            IPC_EXCL:单独使用无意义
            IPC_CREAT | IPC_EXCL :创建一个新的信号量集并返回信号量集的标识符,否则,返回-1.


semop()函数:对信号量集标识符为semid中的一个或多个信号量进行P操作或V操作

1)semid:信号量集标识符
2)nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作
3)sops:指向进行操作的信号量集结构体数组的首地址,此结构的具体说明如下:
struct sembuf {
    unsigned short sem_num; /*信号量集合中的信号量编号,0代表第1个信号量*/
    short sem_op;
    short sem_flg;  

  };

sem_op

-----若sem_op>0进行V操作信号量值加sem_op,表示进程释放控制的资源 

-----若sem_op<0进行P操作信号量值减sem_op,若(semval-sem_op)<0(semval为该信号量值),则调用进程阻塞,直到资源可用;若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误。
-----若sem_op==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误

sem_flag:

-----0 设置信号量的默认操作
-----IPC_NOWAIT设置信号量操作不等待
-----SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值

      sem_flag一般为0,若sem_flag包含IPC_NOWAIT,则该操作为非阻塞操作若sem_flag包含SEM_UNDO,则当进程退出的时候会还原该进程的信号量操作,这个标志在某些情况下是很有用的,比如某进程做了P操作得到资源,但还没来得及做V操作时就异常退出了,此时,其他进程就只能都阻塞在P操作上,于是造成了死锁。若采取SEM_UNDO标志,就可以避免因为进程异常退出而造成的死锁。

3、通过信号量机制的设置使终端的父子进程输出两个A和两个B字符时交叉显示。

comm.h函数:


comm.c函数:


sem.c函数:


makefile函数:


运行结果如下:


主函数中用到了usleep控制等待毫秒数量级,短时看到父子进程输出的字符是否是按规律先后输出,二元信号量实现了进程间的互斥功能。



0 0
原创粉丝点击