Linux-进程间通信:实现信号量

信号量

UNXI system v中的信号量系统调用是对PV原语的推广,在这些原语之上可同时进行多个操作,且增量和减量可大于1,内核自动完成所有操作,在完成操作前,任何进程都不能访问该信号量

信号量由如下元素组成:

  • 信号量的当前值
  • 信号量上最后一个操作的pid
  • 等待该信号量的值大于当前值的进程数
  • 等待该信号量的值为0的进程数

信号量实际是以集合的形式创建的,一个信号量集合中有一个或多个信号量,semctl系统调用允许同时设置集合中所有信号量的值。sem_op系统调用把一系列信号量操作作为参数,每个操作定义在集合中的一个信号量上。进行这一调用时,内核一次执行一个操作,每个操作的实际功能由sem_op指定。

信号量集结构

在/usr/include/linux/sem.h
这里写图片描述

信号量集函数

semget:用来创建和访问一个信号量集
原型:int semget(key_t key, int nsems, int semflg);
参数:
key:信号量集的名字
nsems:信号量集中的信号量的个数
semflg:用法与消息队列共享内存一样
返回值:
成功返回一个非负整数,即该信号量集的标识码;失败返回-1;


semctl:用于控制信号量集
原型:int semctl(int semid, int semnum, int cmd, …);
参数:
semid:由semget返回的标识符
semnum:信号量集中信号量的个数
cmd:采取的动作
返回值:
成功返回1;失败返回-1;


semop:用来设置一个信号量集
原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
参数:
semid:是该信号量集的标识符
sops:指向一个结构体的指针
nsops:信号量的个数
返回值:成功返回0;失败返回-1;
sembuf结构体:这里写图片描述

举例说明

用二元信号量实现对显示屏的互斥保护。
首先看下面的例子:

#include "comm.h"
int main()
{
    pid_t id=fork();
    if(id==0)
    {
        while(1)
        {
            printf("A");
            fflush(stdout);
            usleep(123456);
            printf("A ");
            fflush(stdout);
            usleep(234512);
        }
    }
    else
    {
        while(1)
        {
            printf("B");
            fflush(stdout);
            usleep(134532);
            printf("B ");
            fflush(stdout);
            usleep(121412);

        }
    }
    wait(NULL);
    return 0;
}

我想要实现的功能是,在显示屏上成对的打出A或B。
然而结果:可以看到并没有成对的打印,并且是乱的。原因是因为此时父子进程都要访问显示器这个临界资源,为了对该临界资源加以保护,我们用二元信号量(互斥锁)进行保护。
这里写图片描述
修改代码:

comm.c
#include "comm.h"

int CommSem(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 semid;

}
int CreatSem(int nums)
{
    return CommSem(nums,IPC_CREAT|IPC_EXCL|0666);
}

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

int InitSem(int semid,int nums,int initval)
{
    union semun un;
    un.val=initval;
    if(semctl(semid,nums,SETVAL,un)<0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}
static int commPV(int semid,int who,int op)
{
    struct sembuf buf;
    buf.sem_num=who;
    buf.sem_op=op;
    buf.sem_flg=0;
    if(semop(semid,&buf,1)<0)
    {
        perror("semop");
        return -1;
    }
    return 0;
}

int P(int semid,int who)
{
    return commPV(semid,who,-1);
}
int V(int semid,int who)
{
    return commPV(semid,who,1);
}
int DestroySem(int semid)
{
    if(semctl(semid,0,IPC_RMID)<0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}
test.c
#include "comm.h"
int main()
{
    int semid=CreatSem(1);
    InitSem(semid,0,1);
    pid_t id=fork();
    if(id==0)
    {
        int _semid=GetSem(0);
        while(1)
        {
            P(_semid,0);
            printf("A");
            fflush(stdout);
            usleep(123456);
            printf("A ");
            fflush(stdout);
            usleep(234512);
            V(_semid,0);
        }
    }
    else
    {
        while(1)
        {
            P(semid,0);
            printf("B");
            fflush(stdout);
            usleep(134532);
            printf("B ");
            fflush(stdout);
            usleep(121412);
            V(semid,0);
        }
    wait(NULL);
    }
    DestroySem(semid);
    return 0;
}

结果如下:
这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值