Linux--进程通信之信号量

一、信号量

信号量:有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码完成了。那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码的首末端,确认这些信号量VI引用的是初始创建的信号量。

二、信号量的分类

二进制信号量(binary semaphone): 只允许信号量取0或1值,其同时只能被一个线程获取。

整型信号量(integer semaphone):信号量取值是整数,它可以被多个线程同时获得,直到信号量的值变为0。

记录型信号量(record semaphone):每个信号量s除一个整数值value(计数)外,还有一个等待队列List,其中是阻塞在该信号量的各个线程的标识。当信号量被释放一个,值被加一后,系统自动从等待队列中唤醒一个等待中的线程,让其获得信号量,同时信号量再减一。

三、信号量的工作原理

这里主要讨论的是二进制信号量的工作原理。
信号量是一个特殊的变量,程序对它防问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作,二进制的信号变量只能取0和1的变量。
P(sv): 如果sv的值大于0,就给它减1;如果它的值为0,就挂起该进程的执行。
V(sv): 如果有其他进程因等待sv而挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给他加1。

四、操作函数

1.创建一个信号量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key,int nsems,int semflag);

返回值:成功返回信号量集的IPC标识符。失败返回-1.
参数:
key:所创建或打开信号量集的键值。
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。
semflag:调用函数的操作类型,也可以用于设置信号量集的访问权限,两个通过or表示。

2.删除信号量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid,int semum,int cmd,..)

返回值:成功返回0,失败返回-1。
参数:
semid:信号量的标志码,也就是semget()函数的返回值。
semum:操作信号在信号集中的编号。从0开始。
cmd:表示要进行的操作,删除时将cmd设置成IPC_RMID。
当这个函数有第四个参数时,类型是union,它的结构如下:

union semun
{
    int val;        //设置信号量的初始值
    struct semid_ds *buf;   //IPC_STAT、IPC_SET 使用缓存区
    unsigned short *array;  //GETALL、SETALL 使用的数组
    struct seminfo *__buf;  //IPC_INFO 使用缓存区
};

3.改变信号量的值

#include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/sem.h>
 int semop(int semid, struct sembuf *sops, unsigned nsops);

该函数是进行P,V操作时用到的函数。
返回值:成功返回0;失败返回-1。
参数:
semid:semget的返回值。
sops:指向存储信号操作结构的数组指针,信号操作结构的原型如下:

struct  sembuf {
        unsigned short  sem_num; //信号在信号集的索引
        short   sem_op;  //操作类型(P操作,V操作)
        short   sem_flg; //操作标志,在设置P,V操作时一般设置成为SEM_UNDO
};

当操作信号量(semop)时,sem_flg可以设置SEM_UNDO标识;SEM_UNDO用于将修改的信号量值在进程正常退出(调用exit退出或main执行完)或异常退出(如段异常、除0异常、收到KILL信号等)时归还给信号量。
如信号量初始值是20,进程以SEM_UNDO方式操作信号量减2,减5,加1;在进程未退出时,信号量变成20-2-5+1=14;在进程退出时,将修改的值归还给信号量,信号量变成14+2+5-1=20。

nsops:信号操作结构的数量,恒大与或等于1。


五、信号量的实现

sem.h:

#ifndef _SEM_H_
#define _SEM_H_

#define PATHNAME "."
#define PROJ_ID 0x6666
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>

union semun
{
    int val;                   //使用的值
    struct semid_ds *buf;      //IPC_STAT、IPC_SET  使用缓存
    unsigned short *array;     //AETALL、SETALL     使用的数组
    struct seminfo *__buf;     //IPC_INFO(Linux特有)使用缓存区
};

int ComSem(int nums,int flag);
static int op_sem(int semid,int op,int which);
int CreateSem(int nums);
int DestroySem(int semid);
int GetSem(int nums);
int InitSem(int semid,int which);
int P(int semid,int which);
int V(int semid,int which);
int  InitSem(int semid,int which);

#endif

sem.c

#include"sem.h"

int ComSem(int nums,int flag)
{
    key_t key=ftok(PATHNAME,PROJ_ID);
    if(key<0)
    {
        perror("ftok");
        return -1;
    }
    int semid=semget(key,nums,flag|0666);
    if(semid<0)
    {
        perror("semget");
        return -2;
    }
    return semid;
}

static int op_sem(int semid,int op,int which)
{
    struct sembuf sem;
    sem.sem_num=which;
    sem.sem_op=op;
    return semop(semid,&sem,1);
}

int P(int semid,int which)
{
    int ret=op_sem(semid,-1,which);
    if(ret==0)
        return 0;
    else
        return -1;
}

int V(int semid,int which)
{
    int ret=op_sem(semid,1,which);

    if(ret==0)
        return 0;
    else
        return -1;
}

int CreateSem(int nums)
{
    return ComSem(nums,IPC_CREAT|IPC_EXCL);
}

int GetSem(int nums)
{
    return ComSem(nums,IPC_CREAT);
}

int DestroySem(int semid)
{
    if(semctl(semid,0,IPC_RMID)<0)
    {
        perror("semctl");
        return -1;
    }
}

int InitSem(int semid,int which)
{
    union semun sem;
    sem.val=1;
    int ret=semctl(semid,which,SETVAL,sem);
    if(ret<0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}

main.c

#include"sem.h"

int main()
{
    int semid=CreateSem(12);
    if(semid<0)
    {
        perror("semid");
        return -1;
    }
    InitSem(semid,1);
    sleep(5);
    //DestroySem(semid);
    pid_t id=fork();
    if(id==0)
    {
        //child
        while(1)
        {
            P(semid,1);
            printf("A");
            fflush(stdout);
            usleep(100000);
            printf("A ");
            fflush(stdout);
            usleep(100000);
            V(semid,1);
        }
    }
    else
    {
        //father
        while(1)
        {
            P(semid,1);
            printf("B");
            fflush(stdout);
            usleep(100000);
            printf("B ");
            fflush(stdout);
            usleep(100000);
            V(semid,1);
        }
    }
    wait(NULL);
    return 0;
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值