Linux进程间通信(3)--信号量

来源:互联网 发布:wpf listbox 数据绑定 编辑:程序博客网 时间:2024/06/05 02:02

一、什么是信号量

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

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

信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。而在信号量的创建及其初始化上,不能保证操作均为原子性。

二、信号量的使用

由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

创建/获取信号量

#include <sys/sem.h>int semget(key_t key, int num_sems, int sem_flags);key:非0整数,不相关的进程可以通过它访问一个信号量(一般由ftok函数产生)num_sems: 指定需要的信号量数目,一般是1。sem_flags:是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

改变信号量的值

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

控制信号量

#include <sys/sem.h>int semctl(int sem_id, int sem_num, int command,...);sem_id: semget 返回的信号量标识符sem_num: 设置信号量值command : 一般为SETVAL 或者 IPC_RMIDSETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。如果有第四个参数,一般为union senum 结构体 union semun{    int val;    struct semid_ds *buf;    unsigned short *arry; };

三、使用信号量进程进程间通信

#include<stdio.h>#include<sys/types.h>#include<string.h>#include<sys/sem.h>#include<sys/ipc.h>#include<stdlib.h>#define PATHNAME "."#define PROJ_ID 6666union SemNo{    int val;    struct semid_ds* buf;    unsigned short* array;    struct seminfo* _buf;};int CreateSemSet(int nums);int GetSemSet();int InitSemSet(int semid, int nums);int P(int semid);int V(int semid);int DestorySemSet(int semid);/******************************************/int CommSemSet(int flags, int nums){    key_t _k=ftok(PATHNAME, PROJ_ID);    if(_k < 0){        perror("ftok");        sleep(3);        return -1;    }    int semid = semget(_k, nums, flags);    if(semid < 0){        perror("semget");        sleep(3);        return -2;    }    return semid;}/******************************************//******************************************/int CreateSemSet(int nums){    return CommSemSet(IPC_CREAT | IPC_EXCL | 0666, nums);}/******************************************//******************************************/int GetSemSet(){    return CommSemSet(0, 0);}/******************************************//******************************************/int InitSemSet(int semid,int nums){    union SemNo _SenNo;    _SenNo.val=1;    if(semctl(semid,nums,SETVAL,_SenNo)<0)    {        perror("semctl");        sleep(3);        return -1;    }    return 0;}/******************************************//******************************************/int CommPV(int semid ,int nums, int flags){    struct sembuf _sf[1];    _sf[0].sem_op=flags;    _sf[0].sem_num=nums;    if(semop(semid, _sf, 1) < 0){        perror("semop");        sleep(3);        return -1;    }    return 0;}/******************************************//******************************************/int P(int semid){    return CommPV(semid, 0, -1);}/******************************************//******************************************/int V(int semid){    return CommPV(semid, 0 ,1);}/******************************************//******************************************/int DestorySemSet(int semid){    if(semctl(semid, 0, IPC_RMID) < 0)    {        perror("semctl");        sleep(3);        return -1;    }    return 0;}/******************************************/int main(){    int semid = CreateSemSet(1);    InitSemSet(semid,0);    pid_t id = fork();    if(id == 0) {        int semid = GetSemSet();        while(1){            P(semid);            printf("A");            fflush(stdout);            usleep(20000);            printf("A\n");            fflush(stdout);            usleep(30000);            V(semid);        }    }    else{        while(1){            P(semid);            printf("B");            fflush(stdout);            usleep(30000);            printf("B\n");            fflush(stdout);            usleep(20000);            V(semid);        }    }    DestorySemSet(semid);}

编译

gcc -o  comm comm.c

结果:
这里写图片描述

原创粉丝点击