Linux中的信号量
来源:互联网 发布:网络工程学什么 编辑:程序博客网 时间:2024/06/07 14:46
为什么要使用信号量:
当我们在多用户系统、或者多进程系统下编程时,我们经常会发现我们有段临界代码,在此处我们需要保
证当前仅有一个进程访问它,为了阻止多个程序同时访问一个共享资源引起的问题,我们需要一种方法生
成一个标记从而保证在临界区部分一次只有一个进程执行。而这种方法就是信号量
一、信号量:
信号量的本质是一种数据操作锁,说的通俗一点就是一个计数器。它本身不具有数据交换的功能,而是通
过控制其他的通信资源(文件、外部设备)来实现进程之间的通信,它本身只是一种外部资源的标识。信
号量在此过程中负责数据操作的互斥、同步功能。
当请求一个使用信号量来表示的资源时,进程要先读取信号量的值来判断资源是否可用。大于0
资源可以请求,等于0,无资源可用进程会进入睡眠状态直至资源可用
当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减操作均为原子操
作,这是由于信号量的主要作用是维护资源的互斥或多进程的访问。而在信号量的创建及初始化上,不能
保证操作均为原子性
原子性:如果一个程序,它要么完整的被执行,要么完全不执行。这种特性就叫做原子性
互斥:就是说两个进程只能在某一时刻执行一个,这种结果可能是因为共同争夺资源而产生的
同步:就是进程之间可以同时运行,之间不存在“利益冲突”
二、信号量的工作原理(申请减一。释放加一)
信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv)
P(sv):如果sv的值大于0,就给他减一,如果它的值为0,就挂起该进程的执行;
V(sv):如果有其他进程因等待sv被挂起,就让它恢复运行,如果没有进程因等待sv而挂起就加1;
例如:就是两个进程共享信号量sv,一个进程执行了p(sv)操作,它将得到信号量并进入临界区,
是sv减一。而第二个进程将被阻止进入临界区,因为当它试图执行p(sv),sv为0.它会挂起等待,
当第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。
三、信号量机制
1.创建/获取一个信号量
int semget(key_t key,int nsems,int semflg);
返回值:成功返回信号量集合的semid,失败返回-1;
key:第一个参数key是一个用来允许不相关的进程访问相同信号量的整数值。可以由ftok函数获得
key_t ftok(const char* pathname,int proj_id)
nsems:这个参数表示你要创建的信号量集合中信号量的个数。信号量只能以集合的方式创建。
semflg:同时使用IPC_CREAT和_IPC_EXCL则会创建一个新的信号量,若已经存在则返回-1,
单独使用IPC_CREAT则会返回一个新的或者已经存在的信号量。
测试信号量的创建:
//Makefiletest_sem: test_sem.c comm.c gcc -o $@ $^.PHONY:cleanclean: rm -f test_sem//comm.h头文件#ifndef __COMM_H__#define __COMM_H__#include<stdio.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>#define PATHNAME "."#define PROJ_ID 0x6666int creatSem(int nums);int getSem(int nums);#endif//comm.c信号量的接口#include "comm.h"static int commSet(int nums,int flags){ key_t _key=ftok(PATHNAME,PROJ_ID); if(_key<0) { perror("ftok()"); return -1; } int semid=semget(_key,nums,flags); if(semid<0) { perror("semget"); return -2; } return 0;}int creatSem(int nums){ return commSet(nums,IPC_CREAT|IPC_EXCL|0666);}//test_sem.c测试#include "comm.h"int main(){ int semid=creatSem(1); sleep(4); printf(" Sem proc is done!\n"); return 0;}
我们可以使用
ipcs -s //查看系统当中的信号量
ipcrm -s semid //删除信号量
2.初始化/删除信号量
int semctl(int semid,int semnum,int cmd,...);
semctl()在semid标识的信号量集合上,或者该信号量集合上第semnum个信号量上执行cmd指定的控制命令。根据cmd不同,这个函数有三个或四个参数,当有第四个参数时,第四个参数的类型是union。
union semun{
int val; //使用的值
struct semid_ds *buf; //IPC_STAT、IPC_SET使用缓存区
unsigned short *array; //GETALL、SETALL使用的缓存区
struct seminfo *__buf; //IPC_INFO(linux特有)使用缓存区
};
sem_id:是信号量集合的标识符。
semnum:信号量在集合中的标号
这两个通常的cmd值为:
SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
IPC_RMID:当信号量不再需要时用于删除一个信号量标识。
3.信号量结合的操作
int semop(int semid,struct sembuf *sops,unsigned nsops);
返回值成功返回0,失败返回-1;
第一个参数,sem_id,是由semget函数所返回的信号量标识符。
第二个参数,sem_ops,是一个指向结构数组的指针,其中的每一个结构至少包含下列成员:
struct sembuf{ unsigned short sem_num; /* semaphore number */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */};
sem_num: 为信号量是以集合的形式存在的,就相当所有信号量在一个数组里边,
sem_num表示信号量在集合中的编号。 通常设为0;
sem_op:表示该信号量的操作(P操作还是V操作)。通常情况下中使用两个值,-1是我们的P操作,用来等待一个信号量变得可用,而+1是我们的V操作,用来通知一个信号量可用。
sem_flg:信号操作标志,它的取值有两种。IPC_NOWAIT和SEM_UNDO。
IPC_NOWAIT:对信号量的操作不能满足时,semop()不会阻塞,而是立即返回,同时设定错误信息。
SEM_UNDO: 这会使得操作系统跟踪当前进程对信号量所做的改变,而且如果进程终止而没有释放这个信号量, 如果信号量为这个进程所占有,这个标记可以使得操作系统自动释放这个信号量。将sem_flg设置为SEM_UNDO是一个好习惯,除非我们需要不同的行 为。如果我们确实变我们需要一个不同的值而不是SEM_UNDO,一致性是十分重要的,否则我们就会变得十分迷惑,当我们的进程退出时,内核是否会尝试清 理我们的信号量。
nsops:表示要操作信号量的个数。因为信号量是以集合的形式存在,所以第二个参数可以传一个数组,同时对一个集合中的多个信号量进行操作。
例子:
父进程中打印AA,子进程中打印BB。利用信号量使得AA和BB之间不出现混叠。因为打印的内容都要输入到显示器上,要不混叠的话,显示器就是临界资源。我们需要在父子进程的临界区进行加锁。
//comm.h#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>union semun{ int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */ };#define PATHNAME "."#define PROJ_ID 0x6666int creatSemSet(int nums); //创建int getSemSet(int nums); //接受int initSem(int semid,int nums,int initval); //初始化int P(int semid,int which); int V(int semid,int which);int destorySemSet(int semid);#endif//comm.c#include "comm.h"static int commSemSet(int nums,int flags)//nums表示创建信号集中的个数{ key_t _key=ftok(PATHNAME,PROJ_ID); if(_key<0) { perror("ftok"); return -1; } int semid=semget(_key,nums,flags); if(semid<0) { perror("semget"); return -2; } return semid;}int creatSemSet(int nums){ return commSemSet(nums,IPC_CREAT|IPC_EXCL|0666);}int getSemSet(int nums){ return commSemSet(nums,IPC_CREAT);}int initSem(int semid,int nums,int initVal) //nums信号集中的标号 initval初始化值{ union semun _un; _un.val=initVal; if(semctl(semid,nums,SETVAL, _un)<0) //SETVAL表示初始化信号量 { perror("Semctlinit"); return -1; } return 0;}static int SemPV(int semid,int which,int op) //which表示哪个信号量 op指的是P/V操作{ struct sembuf _sf; _sf.sem_num=which; _sf.sem_op=op; _sf.sem_flg=SEM_UNDO; if(semop(semid,&_sf,1)<0)//这里的1指的是对一个信号量进行操作 { perror("semop"); return -1; } return 0;}int P(int semid,int which){ return SemPV(semid,which,-1);}int V(int semid,int which){ return SemPV(semid,which,1);}int destorySemSet(int semid){ if(semctl(semid,0,IPC_RMID)<0) { perror("semctl"); return -1; } return 0;}
测试:不加锁
#include "comm.h"int main(){ int semid=creatSemSet(1); initSem(semid,0,1); pid_t id=fork(); if(id==0) { //child; while(1) { usleep(100000); printf("A"); usleep(300000); fflush(stdout); printf("A"); usleep(500000); fflush(stdout); } } else { //parent; while(1) { printf("B"); usleep(103000); fflush(stdout); printf("B"); usleep(200000); fflush(stdout); } pid_t ret=waitpid(id,NULL,0); if(ret>0) { printf("wait Success!\n"); } } destory(semid); return 0;}
结果:
AB打印无规律
测试:加锁
#include "comm.h"int main(){ int semid=creatSemSet(1); initSem(semid,0,1); pid_t id=fork(); if(id==0) { //child; while(1) { int _semid=getSemSet(0); P(_semid,0); usleep(100000); printf("A"); usleep(300000); fflush(stdout); printf("A"); usleep(500000); fflush(stdout); V(_semid,0); } } else { //parent; while(1) { P(semid,0); printf("B"); usleep(103000); fflush(stdout); printf("B"); usleep(200000); fflush(stdout); V(semid,0); } pid_t ret=waitpid(id,NULL,0); if(ret>0) { printf("wait Success!\n"); } } destory(semid); return 0;}
结果:
要么就不打印打印就打印两个;
- linux中的信号量详解
- linux中的信号量机制
- linux中的信号量操作
- linux中的信号量
- Linux中的信号量
- Linux内核中的信号量
- 信号量在Linux中的实现
- Linux中的信号量(上)
- Linux-信号量操作函数中的SEM_UNDO标志
- Linux设备驱动中的并发控制---信号量
- Linux中的进程通信(二)--信号量
- Linux 内核中的并发--信号量与互斥体
- linux线程间通信中的信号量
- linux信号量
- linux 信号量
- linux 信号量
- Linux信号量
- linux信号量
- IntelliJ Idea 2017 免费激活方法
- java中i++和++i的区别
- ThreadLocal源码分析与使用场景
- django 出现的错误
- hping3 使用详解
- Linux中的信号量
- 随机数组
- Python安装Image库
- 请求如何进入ASP.NET MVC框架
- STC89C52中断系统
- 序列化注意事项
- Oracle SQL排列组合之排列问题
- Dubbo
- Bootstrap