• Linux IPC实践(11) --System V信号量(1)


    信号量API

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    int semget(key_t key, int nsems, int semflg);
    int semctl(int semid, int semnum, int cmd, ...);
    int semop(int semid, struct sembuf *sops, unsigned nsops);

    semget

    int semget(key_t key, int nsems, int semflg);

    创建/访问一个信号量集

    参数:

       key: 信号集键(key)

       nsems:信号集中信号量的个数

       semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志一致

    返回值:成功返回一个非负整数,即该信号集的标识码;失败返回-1;

       此时创建的信号量集中的每一个信号量都会有一个默认值: 0, 如果需要更改该值, 则需要调用semctl函数->更改初始值;

    /** 示例1: 封装一个创建一个信号量集函数 
    该信号量集包含1个信号量;
    权限为0666
    **/
    int sem_create(key_t key)
    {
        int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);
        if (semid == -1)
            err_exit("sem_create error");
        return semid;
    }
    /** 示例2: 打开一个信号量集
    nsems(信号量数量)可以填0,
    semflg(信号量权限)也可以填0, 表示使用默认的权限打开
    **/
    int sem_open(key_t key)
    {
        int semid = semget(key, 0, 0);
        if (semid == -1)
            err_exit("sem_open error");
        return semid;
    }

    shmctl

    int semctl(int semid, int semnum, int cmd, ...);

    控制信号量集

    参数

       semid:由semget返回的信号集标识码

       semnum:信号集中信号量的序号(注意: 从0开始The semaphores in a set are numbered starting at 0.)

       cmd:将要采取的动作(常用取值如下)


       如果该函数需要第四个参数(有时是不需要第四个参数的, 取决于cmd的取值), 则程序中必须定义如下的联合体:

    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)*/
    };
    //struct semid_ds : Linux内核为System V信号量维护的数据结构
    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 */
    };
    /** 示例1: 将信号量集semid中的第一个信号量的值设置成为value(SETVAL)
    注意: semun联合体需要自己给出(从man-page中拷贝出来即可)
    **/
    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) */
    };
    int sem_setval(int semid, int value)
    {
        union semun su;
        su.val = value;
        if (semctl(semid, 0, SETVAL, su) == -1)
            err_exit("sem_setval error");
        return 0;
    }
    /** 示例2: 获取信号量集中第一个信号所关联的值(GETVAL)
    注意: 此时第四个参数可以不填, 而信号量所关联的值可以通过semctl的返回值返回(the value of semval.)
    **/
    int sem_getval(int semid)
    {
        int value = semctl(semid, 0, GETVAL);
        if (value == -1)
            err_exit("sem_getval error");
        return value;
        return 0;
    }
    /** 示例3: 删除一个信号量集(注意是删除整个集合)
    	IPC_RMID  Immediately  remove(立刻删除)  the  semaphore  set,  awakening  all  processes blocked in semop(2) calls on the set (with an error return and errno set to  EIDRM)[然后唤醒所有阻塞在该信号量上的进程]. The  argument  semnum  is ignored[忽略第二个参数].
    **/
    int sem_delete(int semid)
    {
        if (semctl(semid, 0, IPC_RMID) == -1)
            err_exit("sem_delete error");
        return 0;
    }
    
    //测试代码
    int main(int argc,char *argv[])
    {
        int semid = sem_create(0x1234);	//创建一个信号量集
        sem_setval(semid, 500);			//设置值
        cout << sem_getval(semid) << endl;	//获取值
        sleep(10);
        sem_delete(semid);		//删除该集合
    }
    /**示例4: 获取/设置信号量的权限
    注意:一定要设定struct semid_ds结构体, 以指定使用semun的哪个字段
    **/
    int sem_getmode(int semid)
    {
        union semun su;
    
        // 注意: 下面这两行语句一定要设定.
        // (告诉内核使用的semun的哪个字段)
        struct semid_ds sd;
        su.buf = &sd;
        //
        if (semctl(semid, 0, IPC_STAT, su) == -1)
            err_exit("sem_getmode error");
        printf("current permissions is: %o
    ", su.buf->sem_perm.mode);
        return 0;
    }
    int sem_setmode(int semid, char *mode)
    {
        union semun su;
        // 注意: 下面这两行语句一定要设定.
        // (告诉内核使用的semun的哪个字段)
        struct semid_ds sd;
        su.buf = &sd;
        //
        sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);
    
        if (semctl(semid, 0, IPC_SET, su) == -1)
            err_exit("sem_setmode error");
        return 0;
    }

    semop

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

       用来操纵一个信号量集, 以实现P,V操作

    参数:

       semid:是该信号量的标识码,也就是semget函数的返回值

       sops:是个指向一个结构数组(如果信号量集中只有一个信号量的话, 只有一个结构体也可)的指针

       nsops:所设置的信号量个数(如果nsops>1话, 需要将sops[第二个参数]设置成为一个结构数组, 具体参考Man-Page给出的示例代码), 第三个参数其实也指出第二个参数所表示对象的个数;

    //sembuf结构体
    struct sembuf
    {
        unsigned short sem_num;  /*semaphore number:信号量的编号(从0开始)*/
        short          sem_op;   /* semaphore operation(+1, 0, -1) */
        short          sem_flg;  /* operation flags: 常用取值为SEM_UNDO(解释见下) */
    };

       sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是V操作,发出信号量已经变得可用, 该参数还可以等于0, 表示进程将阻塞直到信号量的值等于0;

       sem_flg有三个取值: SEM_UNDO(在进程结束时, 将该进程对信号量的操作复原[即:取消该进程对信号量所有的操作], 推荐使用), IPC_NOWAIT(非阻塞)或0(默认操作, 并不撤销操作);

    /** 示例: P,V操作封装 
    **可以将sembuf的第三个参数设置为IPC_NOWAIT/0, 以查看程序的状态的变化
    **/
    int sem_P(int semid)
    {
        struct sembuf sops = {0, -1, SEM_UNDO};
        if (semop(semid, &sops, 1) == -1)
            err_exit("sem_P error");
        return 0;
    }
    int sem_V(int semid)
    {
        struct sembuf sops = {0, +1, SEM_UNDO};
        if (semop(semid, &sops, 1) == -1)
            err_exit("sem_V error");
        return 0;
    }

    /** 信号量综合运用示例:
    编译完成之后, 直接运行./semtool, 程序将打印该工具的用法;
    下面的这些函数调用, 只不过是对上面所封装函数的稍稍改动, 理解起来并不困难;
    **/
    //semtool.cpp
    #include "Usage.h"
    
    int main(int argc,char *argv[])
    {
        int opt = getopt(argc, argv, "cdpvs:gfm:");
        if (opt == '?')
            exit(EXIT_FAILURE);
        else if (opt == -1)
        {
            usage();
            exit(EXIT_FAILURE);
        }
    
        key_t key = ftok(".", 's');
        int semid;
        switch (opt)
        {
        case 'c':
            sem_create(key);
            break;
        case 'd':
            semid = sem_open(key);
            sem_delete(semid);
            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 's':
            semid = sem_open(key);
            sem_setval(semid, atoi(optarg));
            sem_getval(semid);
            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]);
            sem_getmode(semid);
            break;
        default:
            break;
        }
    
        return 0;
    }
    //Usage.h
    #ifndef USAGE_H_INCLUDED
    #define USAGE_H_INCLUDED
    
    #include <iostream>
    #include <string>
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <string.h>
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/ipc.h>
    #include <sys/wait.h>
    #include <sys/time.h>
    #include <sys/msg.h>
    #include <sys/shm.h>
    #include <sys/mman.h>
    #include <sys/sem.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <unistd.h>
    #include <grp.h>
    #include <pwd.h>
    #include <time.h>
    #include <errno.h>
    #include <mqueue.h>
    using namespace std;
    inline void err_quit(std::string message);
    inline void err_exit(std::string message);
    
    void usage()
    {
        cerr << "Usage:" << endl;
        cerr << "./semtool -c        #create" << endl;
        cerr << "./semtool -d        #delte" << endl;
        cerr << "./semtool -p        #signal" << endl;
        cerr << "./semtool -v        #wait" << endl;
        cerr << "./semtool -s <val>  #set-value" << endl;
        cerr << "./semtool -g        #get-value" << endl;
        cerr << "./semtool -f        #print-mode" << endl;
        cerr << "./semtool -m <mode> #set-mode" << endl;
    }
    
    int sem_create(key_t key)
    {
        int semid = semget(key, 1, IPC_CREAT|IPC_EXCL|0666);
        if (semid == -1)
            err_exit("sem_create error");
        return semid;
    }
    int sem_open(key_t key)
    {
        int semid = semget(key, 0, 0);
        if (semid == -1)
            err_exit("sem_open error");
        return semid;
    }
    
    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) */
    };
    
    int sem_getmode(int semid)
    {
        union semun su;
    
        // 注意: 下面这两行语句一定要设定.
        // (告诉内核使用的semun的哪个字段)
        struct semid_ds sd;
        su.buf = &sd;
        //
        if (semctl(semid, 0, IPC_STAT, su) == -1)
            err_exit("sem_getmode error");
        printf("current permissions is: %o
    ", su.buf->sem_perm.mode);
        return 0;
    }
    int sem_setmode(int semid, char *mode)
    {
        union semun su;
        // 注意: 下面这两行语句一定要设定.
        // (告诉内核使用的semun的哪个字段)
        struct semid_ds sd;
        su.buf = &sd;
        //
        sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);
    
        if (semctl(semid, 0, IPC_SET, su) == -1)
            err_exit("sem_setmode error");
        return 0;
    }
    int sem_getval(int semid)
    {
        int value = semctl(semid, 0, GETVAL);
        if (value == -1)
            err_exit("sem_getval error");
        cout << "current value: " << value << endl;
        return value;
    }
    int sem_setval(int semid, int value)
    {
        union semun su;
        su.val = value;
        if (semctl(semid, 0, SETVAL, su) == -1)
            err_exit("sem_setval error");
        return 0;
    }
    
    int sem_delete(int semid)
    {
        if (semctl(semid, 0, IPC_RMID) == -1)
            err_exit("sem_delete error");
        return 0;
    }
    
    // 为了能够打印信号量的持续变化, 因此sem_flg我们并没用SEM_UNDO
    // 但是我们推荐使用SEM_UNDO
    int sem_P(int semid)
    {
        struct sembuf sops = {0, -1, 0};
        if (semop(semid, &sops, 1) == -1)
            err_exit("sem_P error");
        return 0;
    }
    int sem_V(int semid)
    {
        struct sembuf sops = {0, +1, 0};
        if (semop(semid, &sops, 1) == -1)
            err_exit("sem_V error");
        return 0;
    }
    
    inline void err_quit(std::string message)
    {
        std::cerr << message << std::endl;
        exit(EXIT_FAILURE);
    }
    inline void err_exit(std::string message)
    {
        perror(message.c_str());
        exit(EXIT_FAILURE);
    }
    
    #endif // USAGE_H_INCLUDED

    附-Makefile

    .PHONY: clean all 
    CC = g++ 
    CPPFLAGS = -Wall -g
    BIN = semtool
    SOURCES = $(BIN.=.cpp)
    all: $(BIN)
    
    %.o: %.c 
        $(CC) $(CPPFLAGS) -c $^ -o $@
    main: main.o
        $(CC) $(CPPFLAGS) $^ -lrt -o $@
    
    clean:
        -rm -rf $(BIN) *.o bin/ obj/ core

  • 相关阅读:
    配置Log4j(非常具体)
    RapeLay(电车之狼R)的结局介绍 (隐藏结局攻略)
    普林斯顿公开课 算法1-11:并查集的应用
    检查Oracle 中死事务的语句
    app被Rejected 的各种原因翻译
    经典语录和思考总结
    Java实现夺冠概率模拟
    Java实现夺冠概率模拟
    Java实现打印回型嵌套
    Java实现打印回型嵌套
  • 原文地址:https://www.cnblogs.com/itrena/p/5926956.html
Copyright © 2020-2023  润新知