第三十章 System V信号量(一)

信号量

信号量和P、V原语由Dijkstra(迪杰斯特拉)提出

信号量:

  • 互斥: P、V在同一进程中
  • 同步: P、V在不同进程中

信号量值含义

  • S>0 : S表示可用资源个数
  • S=0 : 表示无可用资源,无等待进程
  • S<0 : |S|表示等待队列中进程个数

信号量集结构

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 long   sem_nsems; /* No. of semaphores in set */
};

信号量集函数

semget

int semget(key_t key, int nsems, int semflg);
功能:
    创建或访问一个信号量集
参数:
    key : 信号集的名字
    nsems : 信号集中信号量的个数
    semflg : 由9个权限位标志构成,它们的用法与创建文件时使用的mode模式标志是一样的
返回值:
    成功 : 返回一个非负整数,即该信号集的表示码
    失败 : -1

semctl

int semctl(int semid, int semnum, int cmd, ...);
功能:
    用于控制信号量集
参数:
    semid : 由semget返回的信号集标识码
    semnum : 信号集中信号量的序号
    cmd : 将要采取的动作
        SETVAL : 设置信号量集中的信号量的计数值
        GETVAL : 获取信号量集中的信号量的计数值
        IPC_STAT :把semid_ds结构中的数据设置为信号集的当前关联值
        IPC_SET :在进程有足够的权限的前提下,把信号集的当前关联值设置为semid_ds数据结构中给出的值
        IPC_RMID:删除信号集

    最后一个参数根据命令不同而不同

返回值:
    成功 : 0
    失败 : -1

semop

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

功能:
    操作一个或一组信号
参数:
    semid : 信号量的标识码,也就是semget函数的返回值
    sops : 指向一个结构数值的指针
        struct sembuf{
            unsigned short sem_num;   /* semaphore number 信号量的编号*/
            short          sem_op;    /* semaphore operation 信号量一次PV操作时加减的数值*/
                                      /* =0  操作会阻塞,直到信号量的计数变为0才返回(等待资源变为0)*/
                                      /*-1, 也就是P操作,等待信号量变得可用*/
                                      /*+1, 也就是V操作,发出信号量已经变得可用*/
            short          sem_flg;   /* operation flags IPC_NOWAIT|SEM_UNDO */
                                      /* IPC_NOWAIT P、V操作不阻塞直接返回 Resource temporarily unavailable */
                                      /* SEM_UNDO 进行P、V操作的进程结束后,操作会被撤销,资源会被归还,资源的计数值会恢复*/
        }
    nsops : 信号量的个数
返回值:
    成功 : 0
    失败 : -1

semget.c

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

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.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) */
};

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

int sem_create(key_t key)
{
    int semid;
    semid = semget(key, 1, IPC_CREAT|IPC_EXCL| 0666);
    if(semid == -1)
        ERR_EXIT("semget");
    
    return semid;
}

int sem_open(key_t key)
{
    int semid;
    semid = semget(key, 0, 0);
    if(semid == -1)
        ERR_EXIT("semget");

    return semid;
}

int sem_setval(int semid, int val)
{
    union semun su;
    su.val = val;
    int ret;
    ret = semctl(semid, 0, SETVAL, su);
    if(ret == -1)
        ERR_EXIT("semctl");

    return 0;
}

int sem_del(int semid)
{
    int ret;
    ret = semctl(semid, 0, IPC_RMID, 0);
    if(ret == -1)
        ERR_EXIT("semctl");

    return 0;
}

int sem_getval(int semid, int val)
{
    int ret;
    ret = semctl(semid, 0, GETVAL, 0);
    if(ret == -1)
        ERR_EXIT("semctl getval");

    return ret;
}

int sem_p(int semid)
{
    // 0   对信号量集中的第一个信号进行操作
    // -1  对信号量的操作是P操作,对信号量的计数值-1
    struct sembuf sops = {0, -1, 0};
    int ret;
    //第二个参数是指针,指针可以指向数组,数组可以保存多个对信号量集的操作
    ret = semop(semid, &sops, 1);
    if(ret == -1)
        ERR_EXIT("semop");

    return 0;
}

int sem_v(int semid)
{
    struct sembuf sops = {0, 1, 0};
    int ret;
    ret = semop(semid, &sops, 1);
    if(ret == -1)
        ERR_EXIT("semop");

    return 0;
}

int main(int argc, char* argv[])
{

    int semid;
    semid = sem_create(1234);
    sleep(5);
    sem_del(semid);

    return 0;
}

信号量示例

semtool.c

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.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) */
};

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

int sem_create(key_t key)
{
    int semid;
    semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
    if (semid == -1)
        ERR_EXIT("semget");

    printf("sem created...\n");

    return semid;
}

int sem_del(int semid)
{
    int ret;
    ret = semctl(semid, 0, IPC_RMID, 0);
    if (ret == -1)
        ERR_EXIT("semctl");

    printf("sem deleted...\n");
    return 0;
}

int sem_open(key_t key)
{
    int semid;
    semid = semget(key, 0, 0);
    if (semid == -1)
        ERR_EXIT("semget");
    
    return semid;
}

int sem_setval(int semid, int val)
{
    union semun su;
    su.val = val;
    int ret;
    ret = semctl(semid, 0, SETVAL, su);
    if (ret == -1)
        ERR_EXIT("semctl");

    printf("sem value updated...\n");

    return 0;
}

int sem_getval(int semid)
{
    int ret;
    ret = semctl(semid, 0, GETVAL, 0);
    if (ret == -1)
        ERR_EXIT("semctl getval");

    printf("current value is %d\n", ret);
    return ret;
}

int sem_p(int semid)
{
    // struct sembuf sops = {0, -1, 0};

    //0 , 当p操作减为0, 阻塞
    //IPC_NOWAIT, 当p操作减为0, Resource temporarily unavailable
    //IPC_UNDO, 进行一次p操作会减1,假如进程结束了,p操作会被撤销,资源会被归还,资源的计数值会恢复
    struct sembuf sops = {0, -1, /*0*//* IPC_NOWAIT */SEM_UNDO};
    int ret;

    ret = semop(semid, &sops, 1);
    if (ret == -1)
        ERR_EXIT("semop");

    return 0;
}

int sem_v(int semid)
{
    // struct sembuf sops = {0, 1, 0};
    //当进程终止时,操作会被撤销,资源会被归还,资源计数会恢复
    struct sembuf sops = {0, -1, /*0*//* IPC_NOWAIT */SEM_UNDO};
    int ret;

    ret = semop(semid, &sops, 1);
    if (ret == -1)
        ERR_EXIT("semop");

    return 0;
}

int sem_getmode(int semid)
{
    union semun su;
    struct semid_ds sem;
    su.buf = &sem;

    int ret = semctl(semid, 0, IPC_STAT, su);
    if(ret == -1)
        ERR_EXIT("senctl");
    
    printf("current permission is %o\n", su.buf->sem_perm.mode);
    return ret;
}

int sem_setmode(int semid, char* mode)
{
    union semun su;
    struct semid_ds sem;
    su.buf = &sem;

    int ret = semctl(semid, 0, IPC_STAT, su);
    if(ret == -1)
        ERR_EXIT("senctl");
    printf("current permission is %o\n", su.buf->sem_perm.mode);

    sscanf(mode, "%o", (unsigned int*)&su.buf->sem_perm.mode);
    ret = semctl(semid, 0, IPC_SET, su);
    if(ret == -1)
        ERR_EXIT("senctl");

    printf("permissions updated...\n");

    return ret;
}

void usage()
{
    fprintf(stderr,"usage:\n");
    fprintf(stderr,"semtool -c\n");
    fprintf(stderr,"semtool -d\n");
    fprintf(stderr,"semtool -p\n");
    fprintf(stderr,"semtool -v\n");
    fprintf(stderr,"semtool -s <val>\n");
    fprintf(stderr,"semtool -g\n");
    fprintf(stderr,"semtool -f\n");
    fprintf(stderr,"semtool -m <mode>\n");
}

int main(int argc, char *argv[])
{

    int opt;
    opt = getopt(argc, argv, "cdpvs:gfm:");
    if (opt == '?')
        exit(EXIT_FAILURE);
    
    if(opt == -1)
    {
        usage();
        exit(EXIT_FAILURE);
    }

    key_t key = ftok(".",'s');
    int semid;

    switch (opt)
    {
    case 'c':
        sem_create(key);
        break;
    case 'p':
        semid = sem_open(key);
        sem_p(semid);
        sem_getval(semid);
        break;
    case 'v':
        semid = sem_open(key);
        sem_v(semid);
        sem_getval(semid);
        break;
    case 'd':
        semid = sem_open(key);
        sem_del(semid);
        break;
    case 's':
        semid = sem_open(key);
        sem_setval(semid, atoi(optarg));
        break;
    case 'g':
        semid = sem_open(key);
        sem_getval(semid);
        break;
    case 'f':
        semid = sem_open(key);
        sem_getmode(semid);
        break;
    case 'm':
        semid = sem_open(key);
        sem_setmode(semid, argv[2]);
        break;

    default:
        break;
    }

    return 0;
}
posted @ 2019-09-25 15:22  sfdevs  阅读(173)  评论(0编辑  收藏  举报