Linux之信号量


信号量

1.概念:

本质上是一个计数器,用于协调多个进程对共享数据对象的读/写。保证共享资源在一个时刻只有一个进程独享。可以用来保证两个或多个关键代码段不被并发调用。

临界资源:多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。

用ipcs -s 可以查看系统的信号量。

用ipcrm sem + 信号量编号,可以手动删除信号量。

2.二元信号量和通用信号量

信号量实际上是一个计数器,最简单的信号量是取值0和1的二元信号量,这是信号量最常见的形式,1表示可以访问,0表示加锁。

3.函数

//创建或获取一个信号量组,成功会返回信号量集 ID ,失败返回 -1
int semget(key_t key, int nsems, int semflg);
//对信号量组进行操作,改变信号量的值,成功返回 0,失败返回 -1 (用于 PV 操作)
int semop(int semid, struct sembuf *sops, unsigned nsops);    
//控制信号量的相关信息 (用于给信号量初始化)
int semctl(int semid, int semnum, int cmd, ...);
 

1、semget函数

semget函数用来获取或创建信号量。

int semget(key_t key, int nsems, int semflg);
  • 参数key是信号量的键值,typedef unsigned int key_t,是信号量在系统中的编号,不同信号量的编号不能相同。key用十六进制表示比较好。
  • 参数nsems是创建信号量集中信号量的个数,该参数只在创建信号量集时有效,这里固定填1。
  • 参数sem_flags是一组标志,如果希望信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。如果没有设置IPC_CREAT标志并且信号量不存在,就会返会错误。
  • 如果semget函数成功,返回信号量集的标识;失败返回-1。

2、semctl函数

(1)该函数用来控制信号量(用于设置信号量的初始值和销毁信号量)

int semctl(int semid, int semnum, int cmd, ...);
  • 参数semid是由semget函数返回的信号量标识。
  • 参数sem_num是信号量集数组上的下标,表示某一个信号量,填0。
  • 参数cmd是对信号量操作的命令种类,常用的有以下两个:

        IPC_RMID:销毁信号量,不需要第四个参数;

        SETVAL:初始化信号量的值(信号量成功创建后,需要设置初始值),这个值由第四个参数决定。第四参数是一个自定义的共同体,如下:

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

(2)初始化信号量的值为1,信号量可用。

  union semun sem_union;
  sem_union.val = 1;
  semctl(semid,0,SETVAL,sem_union);

(3)销毁信号量。

  semctl(semid,0,IPC_RMID);

3、semop函数

该函数有两个功能:

  1. 等待信号量的值变为1,如果等待成功,立即把信号量的值置为0,这个过程也称之为等待锁;
  2. 把信号量的值置为1,这个过程也称之为释放锁。
int semop(int semid, struct sembuf *sops, unsigned nsops);
  • 参数semid是由semget函数返回的信号量标识。
  • 参数nsops是操作信号量的个数,即sops结构变量的个数,设置它的为1(只对一个信号量的操作)。
  • 参数sops是一个结构体,如下:
struct sembuf
{
  short sem_num;   // 信号量集的个数,单个信号量设置为0。
  short sem_op;    // 信号量在本次操作中需要改变的数据:-1-等待操作;1-发送操作。
  short sem_flg;   // 把此标志设置为SEM_UNDO,操作系统将跟踪这个信号量。
                   // 如果当前进程退出时没有释放信号量,操作系统将释放信号量,避免资源被死锁。
};

示例:

1)等待信号量的值变为1,如果等待成功,立即把信号量的值置为0;

  struct sembuf p_sem;
  p_sem.sem_num = 0;
  p_sem.sem_op = -1;
  p_sem.sem_flg = SEM_UNDO;
  semop(sem_id, &p_sem, 1);

2)把信号量的值置为1。

  struct sembuf v_sem;
  v_sem.sem_num = 0;
  v_sem.sem_op = 1;
  v_sem.sem_flg = SEM_UNDO;
  semop(sem_id, &v_sem, 1);

demo:

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

union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
};

void p_sem(int semid)
{
        struct sembuf p_sembuf;
        p_sembuf.sem_num = 0;
        p_sembuf.sem_op = -1;
        p_sembuf.sem_flg = SEM_UNDO;
        if(semop(semid,&p_sembuf,1) == -1){
                perror("semop p error");
        }
}

void v_sem(int semid)
{
        struct sembuf v_sembuf;
        v_sembuf.sem_num = 0;
        v_sembuf.sem_op = 1;
        v_sembuf.sem_flg = SEM_UNDO;
        if(semop(semid,&v_sembuf,1) == -1){
                perror("semop v error");
        }
}
int main()
{
        int semid;
        key_t key;
        pid_t pid;
        int i;

        key = ftok("./",1);

        semid = semget(key,1,IPC_CREAT|0666);//获取创建信号量

        union semun sem_union;
        sem_union.val = 1;
        if(semctl(semid,0,SETVAL,sem_union) == -1) perror("semctl error!");

        if((pid = fork()) > 0){
                sleep(1);
                p_sem(semid);//等待锁并加锁
                printf("in father process\n");
                for(i=0;i<3;i++){
                        printf("father i=%d\n",i);
                        sleep(1);
                }
                v_sem(semid);//释放锁
               semctl(semid,0,IPC_RMID);//销毁锁
        }

        if(pid == 0){
                p_sem(semid);//等待锁并加锁
                printf("in child process\n");
                for(i=0;i<3;i++){
                        printf("child i=%d\n",i);
                        sleep(1);
                }
                v_sem(semid);//释放锁
        }

        if(pid < 0){
                perror("fork error");
                exit(-1);
        }

        return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值