• System V 信号量


    System V 信号量在内核中维护,其中包括二值信号量 、计数信号量、计数信号量集。
    二值信号量 : 其值只有0、1 两种选择,0表示资源被锁,1表示资源可用;
    计数信号量:其值在0 和某个限定值之间,不限定资源数只在0 1 之间;
    计数信号量集 :多个信号量的集合组成信号量集
    内核维护的信号量集结构信息如下:定义在头文件<sys/sem.h>

    struct semid_ds {
        struct     ipc_perm     sem_perm;
        struct     sem          *sem_base;
        ushort                  sem_nsems;
        time_t                  sem_otime;
        time_t                  sem_ctime;
    };

    其中ipc_perm 结构是内核给每个进程间通信对象维护的一个信息结构,其成员包含所有者用户id,所有者组id、创建者及其组id,以及访问模式等;semid_ds结构体中的sem结构是内核用于维护某个给定信号量的一组值的内部结构,其结构定义:

    struct sem {
       int semval;     /* current value */
       int sempid;     /* pid of last operation */
       struct list_head sem_pending; /* pending single-sop operations */
     };

    其中senval变量代表当前信号量的值,sempid 为最后一个成功操作该信号量的进程id,该结构体在内核以双向链表进行 维护
    semid_ds结构体中的sem_nsems成员代表该信号量标示符的信号量个数
    主要函数介绍:

    创建一个信号量或访问一个已经存在的信号量集。
    int semget(key_t key, int nsems, int semflg);
    该函数执行成功返回信号量标示符,失败返回-1
    参数key是通过调用ftok函数得到的键值,nsems代表创建信号量的个数,如果只是访问而不创建则可以指定该参数为0,我们一旦创建了该信号量,就不能更改其信号量个数,只要你不删除该信号量,你就是重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建;
    semflg 指定该信号量的读写权限,当创建信号量时不许加IPC_CREAT ,若指定IPC_CREAT |IPC_EXCL则创建是存在该信号量,创建失败。

    通过semget函数创建一个信号量集程序如下:(semsemget.c)

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/sem.h>
     5 #include <sys/ipc.h>
     6 #define SEM_R    0400   //用户(属主)读
     7 #define SEM_A    0200   //用户(属主)写
     8 #define SVSEM_MODE (SEM_R | SEM_A | SEM_R>>3 | SEM_R>>6)
     9 
    10 int main(int argc,char *argv[])
    11 {
    12     int   c,oflag,semid,nsems;
    13     oflag = SVSEM_MODE | IPC_CREAT;   //设置创建模式
    14     //根据命令行参数e判断是否制定了IPC_EXCL模式
    15     while((c = getopt(argc,argv,"e"))!= -1)   
    16     {
    17         switch(c)
    18         {
    19             case 'e':
    20                 oflag |= IPC_EXCL;
    21                 break;
    22         }
    23     }
    24     //判断命令行参数是否合法
    25     if (optind != argc -2)
    26     {
    27         printf("usage: semcreate [-e] <pathname> <nsems>");
    28         exit(0);
    29     }
    30     //获取信号量集合中的信号量个数
    31     nsems = atoi(argv[optind+1]);
    32     //创建信号量,通过ftok函数创建一个key,返回信号量 标识符
    33     semid = semget(ftok(argv[optind],0),nsems,oflag);
    34     exit(0);
    35 }

    打开一个信号量集后,对其中一个或多个信号量的操作。
    int semop(int semid, struct sembuf *sops, unsigned nsops);
    该函数执行成功返回0,失败返回-1;
    第一个参数semid 为信号量标示符;nops为第二个参数的操作数组的个数,第二个参数sops为一个结构体数组指针,结构体定义在sys/sem.h中,结构体如下

    struct sembuf {
       unsigned short sem_num; /* semaphore index in array */
       short sem_op; /* semaphore operation */
       short sem_flg; /* operation flags */
    };

    sem_num 操作信号的下标,其值可以为0 到nops
    sem_flg为该信号操作的标志:其值可以为0、IPC_NOWAIT 、 SEM_UNDO
    0 在对信号量的操作不能执行的情况下,该操作阻塞到可以执行为止;
    IPC_NOWAIT 在对信号量的操作不能执行的情况下,该操作立即返回;
    SEM_UNDO当操作的进程推出后,该进程对sem进行的操作将被取消;
    sem_op取值 >0 则信号量加上它的值,等价于进程释放信号量控制的资源
    sem_op取值 =0若没有设置IPC_NOWAIT, 那么调用进程将进入睡眠状态,直到信号量的值为0,否则进程直接返回
    sem_op取值 <0则信号量加上它的值,等价于进程申请信号量控制的资源,若进程设置IPC_NOWAIT则进程再没有可用资源情况下,进程阻塞,否则直接返回。

    采用setmop函数对一个信号量执行操作程序如下:(semop.c)

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/sem.h>
     5 #include <sys/ipc.h>
     6 
     7 int main(int argc,char *argv[])
     8 {
     9     int     c,i,flag,semid,nops;
    10     struct  sembuf *ptr;
    11     flag = 0;
    12         //根据命令行参数设置操作模式
    13     while( ( c = getopt(argc,argv,"nu")) != -1)
    14     {
    15         switch(c)
    16         {
    17             case 'n':
    18                 flag |= IPC_NOWAIT;   //非阻塞
    19                 break;
    20             case 'u':
    21                 flag |= SEM_UNDO;   //不可恢复
    22                 break;
    23         }
    24     }
    25     if(argc - optind < 2)
    26     {
    27         printf("usage: semops [-n] [-u] <pathname> operation...");
    28         exit(0);
    29     } 
    30     //打开一个已经存在的信号量集合
    31     if((semid = semget(ftok(argv[optind],0),0,0)) == -1)
    32     {
    33         perror("semget() error");
    34         exit(-1);
    35     }
    36     optind++;  //指向当前第一个信号量的位置
    37     nops = argc - optind;   //信号量个数
    38     ptr = calloc(nops,sizeof(struct sembuf));
    39     for(i=0;i<nops;++i)
    40     {
    41         ptr[i].sem_num = i;  //信号量变换
    42         ptr[i].sem_op = atoi(argv[optind+i]);   //设置信号量的值
    43         ptr[i].sem_flg = flag;   //设置操作模式
    44     }
    45     //对信号量执行操作
    46     if(semop(semid,ptr,nops) == -1)  
    47     {
    48         perror("semop() error");
    49         exit(-1);
    50     }
    51     exit(0);
    52 }

    对信号量执行各种控制操作。
    int semctl(int semid, int semnum, int cmd, ...);
    该函数执行成功返回非负值,失败返回-1
    参数semid为信号集的标识符,参数 semnum标识一个特定信号,该参数仅用于 SETVAL、GETVAL、GETPID命令
    cmd控制类型,...说明函数参数是可选的,通过该共用体变量semun选择操作参数,各字段如下:

    union semun {
        int val; /* value for SETVAL */
        struct semid_ds __user *buf; /* buffer for IPC_STAT & IPC_SET */
        unsigned short __user *array; /* array for GETALL & SETALL */
        struct seminfo __user *__buf; /* buffer for IPC_INFO */
        void __user *__pad;
     };

    IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
    IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
    IPC_RMID将信号量集从系统中删除
    GETALL用于读取信号量集中的所有信号量的值,存于semnu的array中
    SETALL 设置所指定的信号量集的每个成员semval的值
    GETPID返回最后一个执行semop操作的进程的PID。
    LSETVAL把的val数据成员设置为当前资源数
    GETVAL把semval中的当前值作为函数的返回,即现有的资源数,返回值为非负数。

    调用semctl函数设置信号量的值程序如下(semsetvalues.c):

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/sem.h>
     5 #include <sys/ipc.h>
     6 
     7 //定义信号量操作共用体结构
     8 union semun
     9 {
    10     int                val;
    11     struct semid_ds    *buf;
    12     unsigned short     *array;
    13 };
    14 
    15 int main(int argc,char *argv[])
    16 {
    17     int semid,nsems,i;
    18     struct semid_ds seminfo;
    19     unsigned short *ptr;
    20     union semun arg;
    21     if(argc < 2)
    22     {
    23             printf("usage: semsetvalues <pathname>[values ...]");
    24             exit(0);
    25     }
    26     //打开已经存在的信号量集合
    27     semid = semget(ftok(argv[1],0),0,0);
    28     arg.buf = &seminfo;
    29         //获取信号量集的相关信息
    30     semctl(semid,0,IPC_STAT,arg);
    31     nsems = arg.buf->sem_nsems;  //信号量的个数
    32     if(argc != nsems + 2 )
    33     {
    34         printf("%s semaphores in set,%d values specified",nsems,argc-2);
    35         exit(0);
    36     }
    37     //分配信号量
    38     ptr = calloc(nsems,sizeof(unsigned short));
    39     arg.array = ptr;
    40     //初始化信号量的值
    41     for(i=0;i<nsems;i++)
    42         ptr[i] = atoi(argv[i+2]);
    43     //通过arg设置信号量集合
    44     semctl(semid,0,SETALL,arg);
    45     exit(0);
    46 }

     调用semctl获取信号量的值,程序如下(semgetvalues.c):

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <sys/sem.h>
     5 #include <sys/ipc.h>
     6 
     7 union semun
     8 {
     9     int             val;
    10     struct semid_ds *buf;
    11     unsigned short     *array;
    12 };
    13 
    14 int main(int argc,char *argv[])
    15 {
    16     int     semid,nsems,i;
    17     struct semid_ds seminfo;
    18     unsigned short *ptr;
    19     union semun arg;
    20     if(argc != 2)
    21     {
    22         printf("usage: semgetvalues<pathname>");
    23         exit(0);
    24     }
    25     //打开已经存在的信号量
    26     semid = semget(ftok(argv[1],0),0,0);
    27     arg.buf = &seminfo;
    28     //获取信号量集的属性,返回semid_ds结构
    29     semctl(semid,0,IPC_STAT,arg);
    30     nsems = arg.buf->sem_nsems; //信号量的数目
    31     ptr = calloc(nsems,sizeof(unsigned short));
    32     arg.array = ptr;
    33     //获取信号量的值
    34     semctl(semid,0,GETALL,arg);
    35     for(i=0;i<nsems;i++)
    36         printf("semval[%d] = %d\n",i,ptr[i]);
    37     exit(0);
    38 }

    System V 信号量是具有内核的持续性,可以结合上面介绍的三个函数和程序进行简单的测试。测试结果如下所示:

    演示SEM_UNDO属性,测试结果如下:

  • 相关阅读:
    类和对象
    关联查询
    重点函数
    三大范式
    主外键
    软件开发的项目周期
    什么是事务
    索引
    视图
    数据库对象
  • 原文地址:https://www.cnblogs.com/Anker/p/2859352.html
Copyright © 2020-2023  润新知