【C语言】【unix c】信号量集(system v ipc)



二、信号量集(system v ipc)
    信号量集就是数组,数组里的每个元素都是信号量的类型
    1、获取键值
        ftok(3)
    2、使用键值获取信号量集的id
        semget(2)
            #include 
            #include 
            #include 
            int semget(key_t key, int nsems, int semflg);
                功能:获取信号量集的id
                参数:
                key:ftok(3)的返回值
                nsems:指定了信号量集中的元素的个数
            semflg:
                IPC_CREAT
                IPC_EXCL
            mode:指定信号量集的权限
            返回值:非负整数 是信号量集的id
        -1 错误errno被设置
    3、设置信号量集中的信号量的初值或者获取信号量的值
        semctl(2)
            #include 
            #include 
            #include 
            int semctl(int semid, int semnum, int cmd, ...);
                功能:信号量的控制操作(得到一个信号量集标识符或创建一个信号量集对象并返回信号量集标识符)
                参数:
                    semid:指定了信号量集
                    semnum:指定了信号量在数组中的下标
                    cmd:指定了对信号量的控制操作命令
                        GETVAL:第几个信号的semval值  
                        SETVAL:设置第几个信号量的semval值为arg,val。 0
                    ...:这个参数有没有,多少个取决于cmd
                返回值:成功 返回一个正数
                        失败 -1 errno返回

                需要自定义:
                    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) */
                    };

    4、对指定的信号量对指定的信号量加法或者减法操作
        semop(2)
            #include 
            #include 
            #include 
            int semop(int semid, struct sembuf *sops, unsigned nsops);
                功能:信号量操作
                参数:
                    semid:指定了信号量集
                    sops:指定了对某个信号量的具体操作
                    nsops:指定了要操作的信号量的个数
                返回值:0 成功
                      -1 错误 errno被设置

                操作定义在这个结构体中:
                    struct sembuf{
                        unsigned short sem_num;  /* semaphore number */
                        short          sem_op;   /* semaphore operation */
                        short          sem_flg;  /* operation flags */
                    }

                    sem_flg:
                        SEM_UNDO:进程终止自动撤销
                        IPC_NOWAIT:
                    sem_num:指定了要操作的信号量在数组的下标
                    sem_op:指定了操作的类型
                        操作类型分为3种:
                            >0: semval+sem_op demval做加法
                            =0:
                            <0:
                            semval>|sem_op|:这个操作立即执行,
                            semval<|sem_op|
                            申请的资源数>可用资源数,IPC_NOWAIT 非阻塞
                                                sem_flg=0 阻塞等待资源时间

    举例说明:使用信号量集实现进程间的通信
    两个进程 PA.C(设置信号量的值,每隔3s semval的值减一) PB.C(每隔1s获取信号量的值)
    PA.c:
        #include 
        #include 
        #include 
        #include 
        typedef union semun{
            int val;
        }semun_t;

        int main(void){
            key_t key;
            struct sembuf sb={0,-1,IPC_NOWAIT};
            semun_t arg;
            //获取键值
            key=ftok(".",31);
            if(key==-1){
                perror("ftok");
                return -1;
            }
            //使用键值获取信号量集的id
            int semid=semget(key,1,IPC_CREAT|0664);
            if(semid==-1){
                perror("semget");
                return -1;
            }
            arg.val=5;
            //设置信号量的初值
            int ctl=semctl(semid,0,SETVAL,arg);
            if(ctl==-1){
                perror("semctl");
                return -1;
            }
            //每隔三秒第一个信号量的值减1
            while(1){
                sleep(3);
                int op=semop(semid,&sb,1);
                if(op==-1){
                    perror("semop");
                    return -1;
                }
            }
            return 0;
        }

    PB.c:
        #include 
        #include 
        #include 
        #include 

        int main(void){
            key_t key;
            //获取键值
            key=ftok(".",31);
            if(key==-1){
                perror("ftok");
                return -1;
            }
            //使用键值获取信号量集的id
            int semid=semget(key,1,IPC_CREAT|0664);
            if(semid==-1){
                perror("semget");
                return -1;
            }
            //每隔一秒取一次信号量的值
            while(1){
                sleep(1);
                int val=semctl(semid,0,GETVAL);
                if(val==-1){
                    perror("semctl");
                    return -1;
                }
                printf("semval=%d\n",val);
            }
            return 0;
        }