Linux 信号量详解一

     信号量主要用于进程间(不是线程)的互斥,通过sem_p()函数加锁使用资源,sem_v函数解锁释放资源,在加锁期间,CPU从硬件级别关闭中断,防止pv操作被打断。
semget函数
int semget(key_t key, int nsems, int semflg);
--功能:用来创建和访问一个信号量集
--参数
    key:信号集的key值
    nsems:信号集中信号量的个数
    semflg:由九个权限标志构成,他们的用法和创建文件时使用的mode模式标志是一样的
--返回值:成功返回一个非负整数,即该信号集的标识码,失败返回-1,并且更新errno
shmctl函数
int semctl(int semid, int semnum, int cmd, ...);
--功能:用于控制信号量集
--参数
    semid:由semget返回的信号集标识码
    semnum:信号集中信号量的序号(信号量的序号从0开始,和数组类似)
    cmd:将要采取的动作(有5个可取值)
    最后一个参数根据命令不同而不同
--返回值:成功返回0;失败返回-1并且更新errno
semop函数
int semop(int semid, struct sembuf *sops, unsigned nsops);
--功能:
--参数
    semid:semget函数的返回值
    sops:是个指向一个结构体的指针
    nsops:信号量的个数
--返回值:成功返回0,失败返回-1,并且更新errno
semop函数续
--sembuf结构体
struct sembuf
{
    unsigned short sem_num; /* semaphore number */
    short sem_op; /* semaphore operation */
    short sem_flg; /* operation flags */
};
sem_num是信号量的编号
sem_op是信号量一次PV操作是加减的数值,一般只会用到两个值,一个是"-1",也就是P操作,等待信号量变得可用;另一个是"+1",也就是V操作,发出信号量已经变得可用。
sem_flg的两个取值IPC_NOWAIT或SEM_UNDO,SEM_UNDO是进程完成P操作后直接被终止了,那么系统会自动执行V操作,恢复成默认值
//信号量API
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.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)
};

/**
 * sem_setval - 设置信号量的资源值
 * semid:由semget返回的信号集标识码
 * val:资源值
 * 成功返回0,失败返回-1
 * */
int sem_setval(int semid, int val)
{
    int ret = 0;
    //union semun必须自定义
    union semun su;
    su.val = val;
    //semctl()第二个参数是序号,这里取第一个信号量
    ret = semctl(semid, 0, SETVAL, su);
    if (ret == -1)
    {
        perror("semctl() err");
    }
    return ret;
}

/**
 * sem_setval - 获取信号量的资源值
 * semid:由semget返回的信号集标识码
 * 成功返回可用资源值,失败返回-1
 * */
int sem_getval(int semid)
{
    int ret = 0;
    //semctl()第二个参数是序号,这里取第一个信号量
    //当使用GETVAL命令时,调用中的最后一个参数被忽略
    //成功返回该信号量的可用资源值
    ret = semctl(semid, 0, GETVAL, 0);
    if (ret == -1)
    {
        perror("semctl() err");
    }
    printf("getval=%d\n", ret);
    return ret;
}

/**
 * sem_p - 信号量P操作
 * semid:由semget返回的信号集标识码
 * 成功返回0,失败返回-1
 * */
int sem_p(int semid)
{
    int ret=0;
    //通过struct sembuf结构体的sem_op属性设置P操作
    //sem_flg默认设置为0
    struct sembuf sbuf={0,-1,0};
    ret=semop(semid,&sbuf,1);
    if(ret==-1)
        perror("semop() err");
    return ret;
}

/**
 * sem_v - 信号量V操作
 * semid:由semget返回的信号集标识码
 * 成功返回0,失败返回-1
 * */
int sem_v(int semid)
{
    int ret=0;
    //通过struct sembuf结构体的sem_op属性设置P操作
    //sem_flg默认设置为0
    struct sembuf sbuf={0,1,0};
    ret=semop(semid,&sbuf,1);
    if(ret==-1)
        perror("semop() err");
    return ret;
}

int main()
{
    //创建或者访问信号量集
    int semid = 0;
    //第二个参数创建几个信号量
    semid = semget(0x1234, 1, 0666 | IPC_CREAT | IPC_EXCL);
    if (semid == -1)
    {
        if (errno == EEXIST)
        {
            printf("该信号量集已经存在!\n");
            semid = semget(0x1234, 1, 0666);
        } else
        {
            perror("semget() err");
            return -1;
        }
    }
    //设置第0个信号量的s(可用资源)的值为1
    sem_setval(semid, 1);
    sem_getval(semid);
    sem_p(semid);
    printf("dddddd\n");
    sem_v(semid);
    return 0;
}

 

posted on 2016-12-26 22:50  寒魔影  阅读(598)  评论(0编辑  收藏  举报

导航