Linux进程通讯之信号量

简单来讲,信号量是一个用来描述临界资源的资源个数的计数器。信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件、外部设备等)来实现进程间通信,他本身更只是一种外部资源的标识。信号量在此过程中负责数据操作的同步、互斥等功能。
当请求 一个使用信号量来表示 的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求;等于0,无资源可用,进程会进入睡眠状态直至资源可用。在Linux系统中可以通过 【ipcs -s】的命令查看信号量,通过【rmipc -s 信号量】删除指定的信号量。

1.信号量创建
函数原型如下:

int semget(key_t key,int nsems,int semflg);
其中:
key:信号量键值,可以理解为信号量的唯一性标记;
num_sems:信号量的数目,一般为1;
sem_flags:标志位IPC_CREATE和IPC_EXCL;
		IPC_CREATE表示若信号量已存在,返回该信号量标识符;
		IPC_EXCL表示若信号量已存在,返回错误;

返回值:成功相应的信号量标识符,失败返回-1

2.修改信号值
函数原型如下:

int semop(int sem_id,struct sembuf *sem_opa,size_t num_sem_ops);
其中:
sem_id:信号量标识符,即semget函数的返回值;
sem_opa:结构如下
		struct sembuf{  
		    short sem_num;//除非使用一组信号量,否则它为0  
		    short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,  
		                          //一个是1,即V(发送信号)操作。  
		    short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,  
		                    //并在进程没有释放该信号量而终止时,操作系统释放信号量  
		}; 
num_sem_ops:指的是结构数组的元素个数;

返回值:失败返回-1

3.信号量控制
函数原型如下:

int semctl(int sem_id, int sem_num, int command, ...); 
其中:
sem_id:信号量标识符,即semget函数的返回值;
sem_num:信号量的下标,从0开始。;
command:具体的操作命令,有SETVAL(设置初值)、IPC_RMID(移除信号量;
可变参数:它是一个union semun结构,包含的成员如下:
			union semun
			{
			    int val;
			    struct semid_ds *buf;
			    usigned short *array;
			}一般只使用val这个成员,来为信号量赋初值。当信号量值为0时,进程会阻塞运行。 

返回值:失败返回-1

4.示例代码
封装API函数:

sem.h:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sem.h>

struct semun
{
    int val;
};

int sem_init(int key);
void sem_p(int sem_id);
void sem_v(int sem_id);
void sem_destroy(int sem_id);


sem.c:
#include "sem.h"

int sem_init(int key)
{
	int semid;
    semid = semget((key_t)key,1,IPC_CREAT | IPC_EXCL| 0600);
    if(semid == -1){
         printf("semget error");
         return -1;    
    }
    union semun a;//a传值
    a.val = 1;
    if(semctl(semid,0,SETVAL,a)==-1){//0代表信号量下表
            perror("semctl init error");
            return -1;
      }
    retrun semid;
}

void sem_p(int sem_id)
{
    struct sembuf buf;
    buf.sem_num = 0;//信号量下标
    buf.sem_op = -1;//p操作
    buf.sem_flg = SEM_UNDO;
    if(semop(sem_id,&buf,1)==-1) {
        perror("p error");
    }
}

void sem_v(int sem_id)
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = 1;
    buf.sem_flg = SEM_UNDO; //设置在进程出现错误时信号量值自动恢复,防止一个进程占着信号量
    if(semop(sem_id,&buf,1)==-1){ //1表示操作数,sembuf的数量
        perror("v error");
    }
}

void sem_destroy(int sem_id)
{
    if(semctl(sem_id,0,IPC_RMID)==-1){  //0代表信号量集
        perror("semctl destroy error");
    }
}

示例进程A:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include "sem.h"

int main(void)
{
    int sem_id =  sem_init(214);
    if(sem_id == -1)
    	return -1;
    	
    int i = 0;
    for(;i<10;++i) {
        sem_p(sem_id );
        printf("A\n");

        int n = rand()%3;
        sleep(n);
        printf("A\n");
  
        sem_v(sem_id );
        n = rand()%3;
        sleep(n);
    }
	return 0;
}

示例进程B:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>

#include "sem.h"

int main(void)
{
    int sem_id =  sem_init(214);
    if(sem_id == -1)
    	return -1;
    	
    int i = 0;
    for(;i<10;++i){
        sem_p(sem_id);
        printf("B\n");
        
        int n = rand()%3;
        sleep(n);
        printf("B\n");
      
        sem_v(sem_id);
        n = rand()%3;//产生随机睡眠时间
        sleep(n);
    }
    sleep(10);
    sem_destroy(sem_id);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值