进程间通信之信号量

什么是信号量?

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


信号量的操作

当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用:大于0,资源可以请求,将信号量的值-1(P操作);

等于0,无资源可用,进程会进入睡眠状态直至资源可用;

当进程不再使用一个信号量控制的共享资源时,信号量的值+1(V操作)。

PV操作均为原子操作。这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。而在信号量的创建及初始化上,不能保证操作均为原子性(SystemV版本信号量的缺陷),因为这个版本的信号量的创建与初始化是分开的。


使用信号量的优势

信号量可以用来控制多个进程对共享资源的访问,它作为一种锁机制,可以防止某个进程正在访问共享资源时,其他进程也访问该资源。它主要作为进程间以及同一进程内不同线程之间的同步手段。


函数原型

semget:用来创建和访问一个信号量集
原型:
    int semget(key_t key,int nsems ,int semflg);
参数:
    key:信号集的名字
    nsems:信号集中信号的个数
    semfig:和消息队列的flg参数一模一样:IPC_CREAT:存在则打开,否则创建;IPC_CREAT | IPC_EXCL存在则出错返回,否则创建,这样保证了打开的是一个全新的信号量集。还是要注意,这个IPC_EXCL单独使用没有任何意义。
返回值:
     成功返回一个非负整数,即该信号集的表示码,失败返回-1
shmctl:用于控制信号量集
原型:
    int semctl(int semid, int semnum,int cmd, ...);
参数:
    semid:由semget返回的信号集标识码
    semnum:信号集中信号量的序号
    cmd:将要采取的动作(三个可取值)
    最后一个参数根据命令不同而不同
返回值:
     成功返回0,失败返回-1
semop:创建和访问一个信号量集
原型:
    int semop(int semid, struct sembuf * sops, unsigned nsops);
参数:
    semid:是该信号量的标识码,也就是semget函数的返回值
    sops:是一个指向一个结构数值的指针
    nsops:信号量的个数
返回值:成功返回0,失败返回-1
sembuf结构体:
    struct sembuf{
          short sem_num;
          short sem_op;
          short sem_flg;
    };
sem_num是信号量的编号。
sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值:
一个是"-1",也就是P操作,等待信号量变的可用;

实例

comm.h
#pragma once  

#include <sys/types.h>  
#include <sys/ipc.h>  
#include <sys/sem.h>  
#include <stdio.h>  

#define _PATH_NAME_ "/tmp"  
#define _PROJ_ID_ 0x666  

union semun   
{  
    int val;  
        struct semid_ds *buf;  
        unsigned short  *array;  
        struct seminfo  *__buf;   
};  


int create_sem_set(int nums);  
int get_sem_set(int nums);  
int init_sem_set(int sem_id, int which, int val);  
int P(int sem_id);  
int V(int sem_id);  
int destroy(int sem_id);  
comm.c
#include "comm.h"  


static int comm_sem_set(int nums, int flag)  
{  
    key_t key = ftok(_PATH_NAME_, _PROJ_ID_);  
    if (key < 0)  
    {  
        perror("ftok");  
        return -2;  
    }  

    return semget(key, nums, flag);   
}  

int create_sem_set(int nums)  
{  
    int flag = IPC_CREAT | IPC_EXCL | 0644;  
    return comm_sem_set(nums, flag);  
}  

int get_sem_set(int nums)  
{  
    int flag = IPC_CREAT;  
    return comm_sem_set(nums, flag);  
}  

int init_sem_set(int sem_id, int which, int val)  
{  
    union semun un;  
    un.val = val;  

    return semctl(sem_id, which, SETVAL, un);  
}  

static int pv(int sem_id, int op)  
{  
    struct sembuf buf;  
    buf.sem_num = 0;  
    buf.sem_op = op;  
    buf.sem_flg = 0;  

    return semop(sem_id, &buf, 1);  
}  

int P(int sem_id)  
{  
    return pv(sem_id, -1);  
}  

int V(int sem_id)  
{  
    return pv(sem_id, 1);  
}  

int destroy(int sem_id)  
{  
    return semctl(sem_id, 0, IPC_RMID);   
}
#include <stdio.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include "comm.h"  

int main()  
{  
    int sem_id = create_sem_set(1);  
    init_sem_set(sem_id, 0, 1);  

    pid_t id = fork();  
    if (id < 0)  
    {  
        perror("fork");  
        return 1;  
    }  
    else if (id == 0) // child  
    {  
        while (1)  
        {  
            P(sem_id);  

            printf("A");  
            fflush(stdout);  
            usleep(rand()%12345);  
            usleep(200000);  
            printf("A");  
            fflush(stdout);  
            usleep(rand()%12345);  

            V(sem_id);  
        }  
    }  
    else // father  
    {  
        while (1)  
        {  
            P(sem_id);  

            printf("B");  
            fflush(stdout);  
            usleep(rand()%12345);  
            usleep(200000);  
            printf("B");  
            fflush(stdout);  
            usleep(rand()%12345);  

            V(sem_id);  
        }  
    }  


    destroy(sem_id);  
    return 0;  
} 

这里写图片描述
两个进程同时打印,此时显示器为临界资源通过使用二元信号量后,父子进程对显示器这个临界资源实行互斥访问,保证了父子进程打印的消息成对出现:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值