首页 > 学院 > 开发设计 > 正文

Linux:浅谈信号量

2019-11-08 03:28:03
字体:
来源:转载
供稿:网友

1.信号量的概念。 信号量的本质是一种数据操作锁,它本⾝身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本⾝身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。 当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于0,无资源可用,进程会进⼊入睡眠状态直至资源可用。 当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减 操作均为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。 而在信号量的创建及初始化上,不能保证操作均为原子性。 2.信号量是如何工作的 由于信号量只能进⾏行两种操作等待和发送信号,即P(sv)和V(sv),他们的⾏行为是这样的: P(sv):如果sv的值⼤大于零,就给它减1;如果它的值为零,就挂起该进程的执⾏行 V(sv):如果有其他进程因等待sv⽽而被挂起,就让它恢复运⾏行,如果没有进程因等待sv⽽而挂 起,就给它加1. 3.相关函数  函数原型:int semget(key_t key,int nsems,int semflg);  参数解释: key:所创建或打开信号量集的键值。需要是唯一的非零整数。   nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。几乎总是取值为1.   flag:调用函数的操作类型,也可用于设置信号量集的访问权限,用或来表示。

  功能描述   获取与某个键关联的信号量集标识。信号量集被建立的情况有两种:   1.如果键的值是ipC_PRIVATE。   2.或者键的值不是IPC_PRIVATE,并且键所对应的信号量集不存在,同时标志中指定IPC_CREAT。   当调用semget创建一个信号量时,他的相应的semid_ds结构被初始化。ipc_perm中各个量被设置为相应   值:   sem_nsems被设置为nsems所示的值;   sem_otime被设置为0;   sem_ctime被设置为当前时间。 函数原型:int semop( int semid, struct sembuf semoparray[], size_t nops ); 参数解释: 参数semid是一个通过semget函数返回的一个信号量标识符 参数nops标明了参数semoparray所指向数组中的元素个数 参数semoparray是一个struct sembuf结构类型的数组指针, 结构sembuf来说明所要执行的操作,其成员如下: 这里写图片描述 其中sem_flg说明函数semop的行为。通常被设置为SEM_UNDO。它将使得操作系统跟着当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。

函数原型:int semctl(int semid,int semnum,int cmd,…); 参数解释: sem_id是由semget返回的信号量标识符。 sem_num与前面一个函数相同。 cmd:表示将要采取的动作。 如果有第四个参数,一般为联合体semun,成员如下: 这里写图片描述

信号量使用的简单示例: 我们创建一个进程,代码如下:

#include"head.h"#include<unistd.h>#include<sys/wait.h>int main(){ int sem_id = my_semget(1); initial(sem_id, 0); pid_t id = fork();//创建一个子进程 if (0 == id){ while(1){ //上锁 if ( sem_p(sem_id, 0) == -1){ perror("children p error"); exit(0); } usleep(10200); printf("A"); fflush(stdout); usleep(23043); printf("A"); fflush(stdout); if ( sem_v(sem_id,0) == -1){ perror("children v error"); exit(0); } } } else{ while(1){ //上锁 if ( sem_p(sem_id, 0) == -1){ perror("father p error"); exit(0); } usleep(10200); usleep(16200); printf("B"); fflush(stdout); usleep(33043); printf("B"); fflush(stdout); if ( sem_v(sem_id,0) == -1){ perror("father v error"); exit(0); } } int stat = 0; wait(&stat); }}

在没有信号量之前,因为有睡眠延时,系统会随机打印A和B,如: 这里写图片描述 添加信号量之后,因为A进程在信号量上锁后,sv值变为0,则B进程会挂起,直到A进程解锁,则系统会有规律的打印A和B。 这里写图片描述

head.h:#ifndef _HEAD_#define _HEAD_#include<stdio.h>#include<stdlib.h>#include<sys/sem.h>#include<sys/types.h>#include<sys/ipc.h>#define _PATH "."#define _NUM 0x98union semun { int val; // 使⽤用的值 struct semid_ds *buf; // IPC_STAT、IPC_SET 使⽤用缓存区 unsigned short *array; // GETALL,、SETALL 使⽤用的数组 struct seminfo *__buf; // IPC_INFO(linux特有) 使⽤用缓存区};int my_semget(int sem_num);int getsem();int initial(int sem_id, int which);int sem_p(int sem_id, int which);int sem_v(int sem_id, int which);int destroy(int sem_id);#endifhead.c:#include "head.h"#include<string.h>int static semgeting(int sem_num, int flag){ key_t key = ftok(_PATH, _NUM); return semget(key,sem_num, flag);}int static op_chang(int sem_id, int which, int op){ sembuf sem_t; memset(&sem_t, 0, sizeof(sembuf)); sem_t.sem_num = which; sem_t.sem_op = op; sem_t.sem_flg = 0; return semop(sem_id, &sem_t,1);}int my_semget(int sem_num){ return semgeting(sem_num, IPC_CREAT|IPC_EXCL|0666);}int getsem(){ return semgeting(0, 0);}int initial(int sem_id, int which){ semun sem_t; sem_t.val = 1; return semctl(sem_id, which, SETVAL, sem_t);}int sem_p(int sem_id, int which){ sembuf sem_t; return op_chang(sem_id, which,-1); }int sem_v(int sem_id, int which){ return op_chang(sem_id, which, 1); }int destroy(int sem_id){ return semctl(sem_id, 0, IPC_RMID, NULL);}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表