信号量_系统V

信号量概念

  信号量可以让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问。
  最简单的信号量是只能取01的变量,叫做二进制信号量,而可以取多个正整数的信号量被称为通用信号量

工作原理

  信号量只能进行两种操作,即申请信号量P(sv)和释放信号量V(sv)

  • P(sv):如果sv的值大于零,就给它减1;如果它的值为0,就挂起该进程的执行。
  • V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行;如果没有进程因等待sv而挂起,就给它加1

  假如有两个进程共享信号量sv

  1. 其中一个进程执行了P(sv),它将得到信号量,并使sv1,然后就可以进入临界区。
  2. 第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv0,它会被挂起。
  3. 第一个进程离开临界区域,执行V(sv)释放信号量,这时第二个进程就可以继续执行。

semget

  该函数创建一个新的信号量集,或者获取一个已经存在的信号量集:

#include <sys/sem.h>
int semget ( key_t key, int num_sems, int sem_flags );
  • key:一个键值,用来标识一个全局唯一的信号量集,就像文件名全局唯一的标识一个文件一样。要通过信号量通信的进程需要使用相同的键值来创建或获取该信号量。
  • num_sems:指定要创建或获取的信号量集内部信号量的数目。如果是创建信号量,则该值必须指定;如果是获取已存在的信号量,则可以设置为0
  • sem_flags:指定一组标志,其格式和含义与openmode相同。
  1. 可以和IPC_CREAT标志做按位运算,此时即使信号量存在,semget也不会报错。
  2. 可以用IPC_CREAT | IPC_EXCL来确保创建一组新的、唯一的信号量集,此时若信号量存在,semget则返回错误。

  如果函数执行成功,则返回一个正整数,它是信号量集的标识符;失败时则返回-1

semop

  该函数改变信号量的值,即执行PV操作:

#include <sys/sem.h>
int semop ( int sem_id, struct sembuf *sem_ops, size_t num_sem_ops );
  • sem_idsemget返回的信号量集标识符。
  • num_sem_ops:指定要执行的操作个数,即sem_ops数组中元素的个数。
  • sem_ops:指向一个sembuf结构体的数组:
struct sembuf {
    unsigned short int sem_num;
    short int sem_op;
    short int sem_flg;
};

内部成员的说明如下:

  • sem_num是信号量集内部信号量的编号,像数组一样,从0开始。
  • sem_op是信号量在一次操作中需要改变的数据,通常是两个数:
  1. 一个是-1,即P操作。
  2. 一个是+1,即V操作。
  • sem_flg的可选值是IPC_NOWAITSEM_UNDO
  1. IPC_NOWAIT指无论信号量操作是否成功,semop调用都将立即返回。
  2. SEM_UNDO指当进程退出时,取消正在进行的semop操作。

  如果函数执行成功,则返回0,失败则返回-1

semctl

  该函数允许调用者对信号量进行直接控制:

#include <sys/sem.h>
int semctl ( int sem_id, int sem_num, int command, ... );
  • sem_idsemget返回的信号量集标识符。
  • sem_num:指定被操作的信号量在信号量集中的编号。
  • command:指定要执行的命令,有如下选项:
  1. SETVAL:初始化信号量集。
  2. IPC_RMID:删除信号量集标识符。

  有的命令需要第4个参数,由用户自己定义,但sys/sem.h给出了推荐格式:

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

IPC_PRIVATE

  semget可以给参数key传递一个特殊的键值IPC_PRIVATE,这样无论该信号量是否存在,semget都将创建一个新的信号量。

信号量操作

  代码实例:

#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

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

/* op为“-1”时执行P操作,op为1时执行V操作 */
void pv ( int sem_id, int op ) {
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = op;
    sem_b.sem_flg = SEM_UNDO;
    semop ( sem_id, &sem_b, 1 );
}

int main ( int argc, char *argv[] ) {
    int sem_id = semget ( IPC_PRIVATE, 1, 0666 );
    union semun sem_un;
    sem_un.val = 1;
    semctl ( sem_id, 0, SETVAL, sem_un );
    pid_t id = fork();

    if ( id < 0 ) {
        fprintf ( stderr, "fork failed.\n" );
        return 1;
    } else if ( id == 0 ) {
        printf ( "child try to get binary sem\n" );
        pv ( sem_id, -1 );
        printf ( "child get the sem and would release it after 5 seconds\n" );
        sleep ( 5 );
        pv ( sem_id, 1 );
        exit ( 0 );
    } else {
        printf ( "parent try to get binary sem\n" );
        pv ( sem_id, -1 );
        printf ( "parent get the sem and would release it after 3 seconds\n" );
        sleep ( 3 );
        pv ( sem_id, 1 );
    }

    waitpid ( id, NULL, 0 );
    semctl ( sem_id, 0, IPC_RMID, sem_un );
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值