Linux--进程间通信之信号量
来源:互联网 发布:崩坏3舰团矩阵buff 编辑:程序博客网 时间:2024/05/17 04:25
现在已经写了两种进程间通信方式
Linux--进程间通信之匿名管道及命名管道:http://blog.csdn.net/sayhello_world/article/details/59556670
Linux--进程间通信之消息队列:http://blog.csdn.net/sayhello_world/article/details/59690231
今天我们来说第三种进程间通信方式--信号量。
一.什么是信号量?
信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。所测试的线程可以锁定使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
二.为什么要有信号量?
当我们进行进程间通信的时候,若写端向其写hello world,读端读取,则很有可能写端还未写完hello world,读端就已经读取了。这样子便会导致数据不一致问题,为了避免这一问题,便有了信号量。
介绍几个概念:
原子性:通俗的来说就是一件事要么做了,要么没做,如果他做了一定是做完了。
临界资源:不同的进程看到共同的资源。
临界区:共同访问临界资源的那段代码。
所以这里的保护机制,保护的是临界区,因为我们只能控制代码。
三.信号量两种操作
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂
起,就给它加1.
那么我们可以知道,PV操作为原子性的,二元信号量本身就为一把锁。
同步:访问临界资源并以特定的顺序访问。(一般同步都在互斥的情况下)
互斥:在任意时刻只允许一个人进入临界区访问临界资源。
饥饿:长时间没有得到资源。
那么就会有一个问题:
如果有一个全局变量,他能让两个进程同时看到,它能取代信号量吗?
不能,因为变量开始存于内存中,但是加减运算在CPU中,若要给此变量加减1,则应该先将他取到CPU中,CPU中加一,再返给内存中,需要三步。任意一步都可能会被中断,所以与另一进程不具有互斥性。
那么我们来实现一下信号量:
思路:两个进程两行打印输出A和B,如果没有信号量之前,则可能是AB可能是乱序输出,如果加了PV操作,则应该是成对输出。
需要用到的函数:
信号量的创建:
int semget(key_t key,int nsems,int semflg)
key:与消息队列中key值含义相同,用ftok生成
nsems:信号量特有的参数,在system V下申请信号量是以信号量集的方式,次变量表示信号量的个数是多少个。
semflg:与消息队列中相同,两个参数IPC_CREAT与IPC_EXCL
详情见:http://blog.csdn.net/sayhello_world/article/details/59690231
信号量的获取:
与消息队列创建相同,只是semflg传的参数不同,获取时只需传IPC_CREAT
信号量的销毁:
int semctl(int semid,int semnum,int cmd,.....)
返回值:失败返回-1
semnum:操作信号量集中的哪一个信号量
cmd:销毁时用IPC_RMID
信号量的初始化:
与信号量的销毁相同,只是这时传的值不同。
semctl(semid,which,SETVAL,un)
这里un为一个结构体如下
union semun {
int val; // 使用的值
struct semid_ds *buf; // IPC_STAT、IPC_SET 使用缓存区
unsigned short *array; // GETALL,、SETALL 使用的数组
struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区
};
PV操作:
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
函数参数:第一个参数,sem_id,是由semget函数所返回的信号量标识符。
第二个参数,sem_ops,是一个指向结构数组的指针,其中的每一个结构至少包含下列成员:
struct sembuf{ short sem_num;//除非使用一组信号量,否则它为0,一般从0,1,...num_secs-1 short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作, 一个是+1,即V(释放信号)操作。 short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,操作系统释放信号量 };
着重说一下第三个成员:sem_flg:通常设置为SEM_UNDO,这会使得操作系统跟踪当前进程对信号量所做的改变,而且如果进程终止而没有释放这个信号量, 如果信号量为这个进程所占有,这个标记可以使得操作系统自动释放这个信号量。
第三个参数num_sem_ops,表示进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作。
函数返回值:成功:返回信号量集的标识符,错误,返回-1
comm.h
#ifndef __COMM_H__#define __COMM_H__#include<stdio.h>#include<stdlib.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include<unistd.h>#include<string.h>#define PATHNAME "."#define PROJ_ID 0X666typedef 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) */}_semun;int create_sem(int semnum);int get_sem();int init_sem(int semid,int which,int _val);int destroy_sem(int semid);int P(int semid,int which);int V(int semid,int which);#endif
comm.c
#include"comm.h"static int comm_sem(int semnum,int flags){ key_t k = ftok(PATHNAME,PROJ_ID); int semid = semget(k,semnum,flags); if(semid < 0) { perror("semget\n"); return -1; } return semid;}int creat_sem(int semnum){ return comm_sem(semnum,IPC_CREAT|IPC_EXCL|0666);}int get_sem(){ return comm_sem(0,IPC_CREAT);}int destroy_sem(int semid){ if(semctl(semid,0,IPC_RMID) < 0) { perror("semctl"); return -1; } return 0;}int init_sem(int semid,int which,int _val){ _semun un; un.val = _val; if(semctl(semid,which,SETVAL,un) < 0) { perror("init_sem"); return -1; } return 0;}static int comm_op(int semid,int which,int op){ struct sembuf sbuf; sbuf.sem_num = which; sbuf.sem_op = op; sbuf.sem_flg = 0; return semop(semid,&sbuf,1);}int P(int semid,int which){ return comm_op(semid,which,-1);}int V(int semid,int which){ return comm_op(semid,which,1);}
测试代码:
comm_test.c
#include"comm.h"int main(){ int semid = creat_sem(1); init_sem(semid,0,1); int status = 0; pid_t id = fork(); if(id< 0){ perror("fork"); return -1; } else if(id == 0){//child while(1){ int _semid = get_sem(); P(_semid,0); printf("A"); usleep(12345); fflush(stdout); printf("A"); usleep(55787); fflush(stdout); V(_semid,0); } exit(1); }else{//father while(1) { P(semid,0); printf("B"); usleep(16745); fflush(stdout); printf("B"); usleep(59887); fflush(stdout); V(semid,0); } wait(&status); destroy_sem(semid); } return 0;}
未添加信号量之前:有单个出现的
用信号量之后:都是成双出现的
- linux进程间通信之信号量
- 9、linux进程间通信之信号量
- Linux进程间通信之信号量
- Linux进程间通信之信号量
- Linux进程间通信之信号量
- linux进程间通信之信号量(semaphore)
- linux进程间通信之信号量
- linux进程间通信之信号量集
- Linux进程间通信之信号量
- linux进程间通信之信号量
- Linux进程间通信之信号量
- linux进程间通信之信号量(semaphore)
- 【Linux】进程间通信之信号量
- linux进程间通信之信号量
- Linux下进程间通信之信号量
- linux进程间通信之信号量(semaphore)
- Linux -- 进程间通信之信号量
- Linux 进程间通信之信号量
- Message Filter
- 购房预算
- 文件上传
- 小飞鱼通达商务平台课程 OA开发程序基础课 3月11日 PHP第二部分课程签到及作业
- 如何写好gitthbuREADEME?github中READEME中的操作和markdown操作一样的,
- Linux--进程间通信之信号量
- 巧用 MySQL Group By 和IF 解法
- Servlet--微信自定义菜单
- Message Endpoint
- poj2503
- hibernate映射文件
- Dynamic Router
- 通过args数组获取数据
- 跟踪内核从start_kernel到init进程启动