第11章 system V 信号量

11.1 概述

  • 二值信号量:值为0或1的信号量
  • 计数信号量:在0和某个限制值之间的信号量
  • 计数信号量集:一个或多个信号量(构成一个集合),其中每个都是计数信号量。集合的信号量数存在一个限制
  • 对每个信号量集,内核维护一下如下信息结构:
struct semid_ds {
    struct ipc_perm ipc_perm; 权限
    struct sem *sem_base; //sem_base指向信号量数组
    ushort sem_nsems; //信号量数组sem_base中有多少个信号量
    time_t sem_otime; // time of last semop
    time_t sem_ctime; //time of creation or last IPC_SET
};
  • 上述结构体中的struct sem用于维护某个给定信号量的一组值的内部数据结构:
struct sem {
    int semval;   //信号量的值
    pid_t sempid; //最后一次p/v操作的进程的id
    int semncnt;  //wait其增长的进程数
    int semzcnt;  //wait其变为0的进程数
};

11.2 semget()函数

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
                        //出错返回-1
int semget(key_t key, int nsems, int semflg);
  • key: system v ipc对象的key
  • nsems: 你要创建的信号量集中的信号量的数量。
    如果我们不是创建而是打开一个已经存在的信号量集,此处参数可以指定为0.一旦创建完一个信号量集,其信号量的个数就不能改变啦。
  • semflg:标志位
    • SEM_R :用户属组读
    • SEM_A :用户属组改写
    • 可以和 IPC_CREAT | IPC_EXCL 按位与

11.3 semop函数

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

int semop(int semid, struct sembuf *sops, size_t nsops);

int semtimedop(int semid, struct sembuf *sops, size_t nsops,const struct timespec *timeout);
  • 在system v 信号量集中,对某个信号量的P/V操作,是用结构体 struct sembuf来描述的,即opsptr指向的结构体
struct sembuf   //可能还含有其他成员
{
    short sem_num; //0,1,2,..., nsems -1
//表示您要p/v操作哪个信号量
    short sem_op; //  <0, 0, > 0
// sem_op是描述对信号量值的一种操作
// semval = 原semval + sem_op
//sem_op < 0 => -  P操作
//sem_op > 0 => +  V操作
//sem_op = 0 =>  //试一试,看是否会阻塞!!!
    short sem_flg; // 操作标志,默认0
//IPC_NOWAIT 非阻塞,不等待
//如果是P操作,能获取则获取,不能获取则返回-1,表示不能获取。
//SEM_UNDO : 撤销!!!这个标志非常有意义!!!
//为了防止进程带锁退出的。
//if you set SEM_UNDO这个标志,内核会额外记录该进程对该信号量的p/v操作,在进程退出时,会还原!!
};
  • nops 指出opsptr指向结构体数组中元素的个数,表示表多少个信号量进行p/v操作
  • 返回值:成功返回0,失败返回-1, 同时errrno被设置。

11.4 semctl函数

int semctl(int semid, int semnum, int cmd, ...);
  • semnum:表示信号量集中的成员

11.5 system v 实现一个简单的文件锁

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

#define SEM_R 0400
#define SEM_A 0200
#define SVSEM_MODE  (SEM_R | SEM_A | SEM_R>>3 | SEM_R>>6)
/* default permissions for new SV semaphores */
union semun{  //用来描述第四个参数的类型
    int val ;// used for SETVAL only 
    struct semid_ds *buf; // used for IPC_SET and IPC_STAT 
    ushort *array; //used for GETALL and SETALL
};
#define LOCK_PATH "/tmp/svsemlock"
#define MAX_TRIES 10
int semid,initflag;
struct sembuf postop,waitop;

void my_lock(int fd){
    int oflag,i;
    union semun arg;
    struct semid_ds seminfo;
    int flag=0;

    if(initflag==0){//没有初始化
        oflag = IPC_CREAT | IPC_EXCL | SVSEM_MODE;//尝试创建
        if( (semid=semget(ftok(LOCK_PATH,0),1,oflag))>=0 ){
            arg.val=1;
            flag=semctl(semid,      //通过这个信号量集的id获取内核维护的信息
                        0,          //信号量集内的某个成员
                        SETVAL, //通过arg.buf返回所指定信号量集当前的semid_ds结构
                        arg);       //通过arg的成员接收返回值
        }else if(errno==EEXIST){    //已经存在了,就直接进行读写操作
            semid = semget(ftok(LOCK_PATH,0),1,SVSEM_MODE);
            assert(semid!=-1);
            arg.buf = &seminfo;
            for(i=0;i<MAX_TRIES;i++){
                flag=semctl(semid,     //通过这个信号量集的id获取内核维护的信息
                            0,         //信号量集内的某个成员
                            IPC_STAT,  //通过arg.buf返回所指定信号量集当前的semid_ds结构
                            arg);      //通过arg的成员接收返回值
                assert(flag!=-1);
                if(arg.buf->sem_otime!=0){
                    goto init;
                }
                sleep(1);
                printf("semget OK, but semaphore not initialized");
                exit(0);
            }
        }else{
                printf("semget error");
        }
init:
        initflag = 1;
        postop.sem_num = 0;
        postop.sem_op = 1;
        postop.sem_flg = SEM_UNDO;
        waitop.sem_num = 0;
        waitop.sem_op  = -1;
        waitop.sem_flg = SEM_UNDO;
    }
    flag=semop(semid,&waitop,1);
    assert(flag!=-1);
}

void my_unlock(int fd)
{
    int flag=semop(semid, &postop, 1);      /* up by 1 */
    assert(flag!=-1);
}

猜你喜欢

转载自blog.csdn.net/qq_36337149/article/details/81257140