Linux IPC System V 信号量

时间:2022-05-27 15:17:43

模型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
ftok()              //获取key                 
semget()            //创建/获取信号量集     
semctl()            //初始化信号量集           
semop()             //操作信号量集            
semctl()            //删除信号量集            

ftok()

//获取key值, key值是System V IPC的标识符,成功返回key,失败返回-1设errno
//同pathname+同 proj_id==>同key_t;
key_t ftok(const char *pathname, int proj_id);

pathname :文件名
proj_id: 1~255的一个数,表示project_id

key_t key=ftok(".",100);    //“.”就是一个存在且可访问的路径, 100是假设的proj_id
    if(-1==key)
        perror("ftok"),exit(-1);

semget():

//创建/获取一个信号量集,成功返回semid,失败返回-1
int semget(key_t key, int nsems, int semflg);

nsems: 信号量集的大小/信号量的个数,0表示获取已经存在的信号量集
semflg

  • IPC_CREAT :若不存在则创建, 需要在msgflg中"|权限信息"; 若存在则打开
  • IPC_EXCL :若存在则创建失败
  • 0 :获取已经存在的信号量集
//create sem
semid=semget(key,1,IPC_CREAT|IPC_EXCL|0664);
if(-1==semid)
    perror("semid"),exit(-1);

semctl()

//主要用于对指定的信号量集/信号量执行指定的操作,成功返回0,失败返回-1设errno
int semctl(int semid, int semnum, int cmd, ...);

semid: 信号量集的编号(哪个信号量集)
semnum: 信号量集的下标(这个信号量集里的哪个信号量)
cmd:具体的操作命令

  • IPC_STAT 将内核中与semid相关的信息拷贝到arg.buf指向的结构体中
  • IPC_SET将buf指向的semid_ds结构体的部分内容写入到内核中的相关数据结构中,同时更新sem_ctime成员
  • IPC_RMID 立即销毁指定的信号量集,调用的进程的的effective UID必须和信号量集的创建者或所有者相匹配,或者这个进程有足够的特权级别,此时第四个参数会被忽略
  • IPC_INFO(Linux-specific)返回系统对信号量集的限制到__buf指向的结构体seminfo中
//_GNU_SOURCE
struct  seminfo {
    int     semmap;     /* Number of entries in semaphore map; unused within kernel */
    int     semmni;     /* Maximum number of semaphore sets */
    int     semmns;     /* Maximum number of semaphores in all semaphore sets */
    int     semmnu;     /* System-wide maximum number of undo structures; unused within kernel */
    int     semmsl;     /* Maximum number of semaphores in a set */
    int     semopm;     /* Maximum number of operations for  semop(2) */
    int     semume;     /* Maximum number of undo entries per process; unused within kernel */
    int     semusz;     /* Size of struct sem_undo */
        int     semvmx;     /* Maximum semaphore value */
    int     semaem;     /* Max. value that can be recorded for semaphore adjustment (SEM_UNDO) */
};

//semmsl, semmns, semopm, semmni可以通过/proc/sys/kernel/sem来设置
  • SEM_INFO (Linux-specific)返回和IPC_INFO一样的信息,除了以下方面:semusz成员返回当前系统中存在的信号量集的数目,semaem返回系统中所有信号量集中的信号量总数
  • SEM_STAT(Linux-specific)返回semid_ds结构,类似与IPC_STAT
  • GETALL 返回所有信号量的semval到arg.array中,忽略semnum
  • GETNCNT 返回信号量集第semnum个信号量的semcnt值
  • GETPID 返回信号量集第semnum个信号量的sempid值
  • GETVAL 返回信号量集第semnum个信号量的semval值
  • GETZCNT 返回信号量集第semnum个信号量的semzcnt值
  • SETALL 使用arg.array设置信号量集里的所有的信号量的semval值,同时更新信号量集的semid_ds结构体的sem_ctime成员的值
  • SETVAL返回信号量集第semnum个信号量的semval的值到arg.val中,同时更新信号量集的semid_ds结构体的sem_ctime成员的值

the fourth argument:

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) */
};

//<sys/sem.h>
struct semid_ds {
    struct ipc_perm sem_perm;   /* Ownership and permissions */
    time_t          sem_otime;  /* Last semop time */
    time_t          sem_ctime;  /* Last change time */
    unsigned short  sem_nsems;  /* No. of semaphores in set */
};

//<sys/ipc.h>
struct ipc_perm {
    key_t           __key;  /* Key supplied to semget(2) */
    uid_t           uid;    /* Effective UID of owner */
    gid_t           gid;    /* Effective GID of owner */
    uid_t           cuid;   /* Effective UID of creator */
    gid_t           cgid;   /* Effective GID of creator */
    unsigned short  mode;   /* Permissions */
    unsigned short  __seq;  /* Sequence number */
};
int res=semctl(semid,0,SETVAL,5);
if(-1==res)
    perror("semctl"),exit(-1);
int res=semctl(semid,0,IPC_RMID);
if(-1==res)
    perror("semctl"),exit(-1);

semop():

//操作指定的信号量集,成功返回0,失败返回-1设errno
int semop(int semid, struct sembuf *sops, unsigned nsops);

semid:信号集的ID(returned by semget())
sops:结构体指针, 既可以指向结构体变量, 也可以指向结构体数组信号量集本质上是若干个信号量的集合, 可以实现对信号量的批处理

struct sembuf{
    unsigned short  sem_num;    //信号量集的下标
    short           sem_op;     //正数表示增加, 0表示不变, 负数表示减小
    short           sem_flg;    //操作标志,默认给0
}

nsops:结构体指针指向的元素个数, 也就是数组的大小

例子

Sys V IPC sem
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
    //get key
    key_t key=ftok(".",200);
    if(-1==key)
        perror("ftok"),exit(-1);
    printf("key=%d\n",key);
    //create sem
    int semid=semget(key,0,0);
    if(-1==semid)
        perror("semget"),exit(-1);
    printf("semid=%d\n",semid);
    //creat 10 children to take the shared resource
    int i=0;
    for(i=0;i<10;i++){      //创建10个进程, 当然,需要只给parent或child单独fork(), 否则就是2^10个进程
        pid_t pid=fork();
        if(-1==pid)
            perror("fork"),exit(-1);
        if(0==pid){

            struct sembuf buf;      //准备占用资源, sem_op-1
            buf.sem_num=0;      //信号量集下标
            buf.sem_op=-1;      //信号量-1
            buf.sem_flg=0;      //操作标志
            int res=semop(semid,&buf,1/*结构体变量的个数*/);
            if(-1==res)
                perror("semop"),exit(-1);

            sleep(20);          //模拟正在占用共享资源

            buf.sem_op=1;       //占用完了, sem_op+1
            res=semop(semid,&buf,1);
            if(-1==res)
                perror("semop"),exit(-1);

            exit(0);                //终止子进程, 自然也就跳出了循环,防止再fork()
        //  break;
        }
    }   
    return 0;
}
//出现抢占的效果, 还没有全部释放完毕的时候就有进程抢到了已经释放的进程