进程间通讯:信号量

一、与信号量有关的几个知识点

 临界资源:同一时刻,只能被一个进程访问的资源
 临界区:访问临界资源的代码区
 原子操作:任何情况下都不能被打断的操作
 内核对象:对通讯值的记录,类似管道

二、信号量的实质和作用

信号量的实质是记录资源同时能被多少个进程访问,作用于进程间的同步控制,不同于锁,它可以有N个有限值。
举个例子,信号量类似一个停车场,而锁类似停车场中的一个停车位,当车辆进入停车场时,停车位数量减一,但是在有其它空余停车位时,车辆依然可以进入停车场。而一个停车位有车辆时,其它车辆不可以进入这个停车位。
总的来说,信号量的通讯信息是资源的可访问数量,不同于管道的数据。

三、信号量的操作

1. 创建或获取:Int semget((key_t)key, int nsems, int flag);
Key是键值,是一个唯一的非零整数,类似于一个“身份证号”,其它进程通过这个“身份证号”来使用这组信号量;
Nsems 用来确定内核创建的信号量数组大小;
Flag 用来确定操作和权限,可以通过与上PC_CREAT在不存在该信号量时创建,存在时获取;
返回值:非零的信号量标识符,其它函数根据信号量标识符来操作,而非键值;

栗子:int sem_id = semget( (key_t)1234, 1, 0666|IPC_CREAT );

2. P/V操作:int semop( int semid , struct sembuf * buf , int lenth);
Semid: semget返回的信号量标识符
struct sembuf * buf:一个结构体
    struct sembuf
    { 
         Short sem_num;//除非使用一组信号量,否则它为0  
         short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,
                        一个是-1,即P(等待)操作,//一个是+1,即V(发送信号)操作。 
         shortsem_flg;//通常为SEM_UNDO,使操作系统跟踪信号
    };
3. 初始化和删除:int semctl(int sem_id, int sem_num, int command, uniousemun);
 Semid: semget返回的信号量标识符
 sem_num:信号量数组的下标,如果只有一个元素,则为零
command:操作,SETVAL为初始化,IPC_RMID为删除
uniou semun:在初始化时,需要用到
            unionsemun 
            { 
               int val; //初始化资源的个数
               struct semid_ds *buf; 
               unsigned short *arry; 
            }; 

四、通过一个简单实例来掌握信号量,进程A负责读取键盘输入,直到读取“OK”,进程B打印出100以内的素数。

需要注意的是,在多个进程之间使用信号量通讯,可以将P操作之后的代码认为是临界区,即P操作不是简单的减一,而是要理解为:
如果能做P操作之后临界区的事件,那么减一,否则等待

sem.h 为头文件
sem.c 将信号量操作进行了简单的封装
maina.c和mainb.c是一个简单的示例:
maina读取键盘输入,直到输入OK,mainb打印100以内的素数

sem.h:

#ifndef _SEM_H
#define _SEM_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semid;

union semun
{
    int val;
    //struct semid_ds *buf;
    //unsigned short * arry;
};

void sem_get(int key,int len,int val);
void sem_p();
void sem_v();
void sem_del();

#endif

sem.c:

#include "sem.h"

void sem_get(int key,int len,int val)
{
    semid = semget((key_t)key,len,0666);
    if(semid == -1)
    {
        semid = semget((key_t)key,len,0666|IPC_CREAT);
        assert(semid != -1);

        union semun v;
        v.val = val;
        if(semctl(semid,0,SETVAL,v)==-1)
        {
            perror("error");
            exit(0);
        }
    }
}

void sem_p()
{
    struct sembuf sem;
    sem.sem_num = 0;
    sem.sem_op = -1;
    sem.sem_flg = SEM_UNDO;

    if(semop(semid, &sem, 1)==-1)
    {
        perror("perror");
        exit(0);
    }
}

void sem_v()
{

    struct sembuf sem;
    sem.sem_num = 0;
    sem.sem_op = 1;
    sem.sem_flg = SEM_UNDO;

    if(semop(semid, &sem, 1)==-1)
    {
        perror("perror");
        exit(0);
    }
}

void sem_del()
{
    if(semctl(semid,0,IPC_RMID)==-1)
    {
        perror("delperror");
        exit(0);
    }
}

进程A:

#include "sem.h"

int main()
{
    sem_get(111,1,0);
    while(1)
    {
        char buff[128]={0};
        fgets(buff,127,stdin);
        if(strncmp(buff,"ok",2)==0)
        {
            sem_v();
            break;
        }
    }
    sem_del();
    return 0;
}

进程B:

#include "sem.h"
void prime()
{
    int i,j;
    for(i=2;i<=100;i++)
    {
        for(j=2;j<=i-1;j++)
        {
            if(i%j==0)
                break;
        }
        if(j>=i)
          printf("%d\n",i);
    }
}
int main()
{
    sem_get(111,1,0);
    sem_p();
    prime();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值