0
点赞
收藏
分享

微信扫一扫

System V 信号量

最后的执着 2022-04-16 阅读 62

System V 信号量

API 函数

创建或打开一个信号量集

#include <sys/types.h>
#include <sys/sem.h>

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

参数 key:由 IPC_PRIVATE 或 ftok() 返回的键

参数 nsems:

  • 创建信号集时:nsems 指定信号量的数量,必须大于0
  • 获取信号集时:nsems 必须小于等于集合的大小

参数 semflg:位掩码和指定文件权限

  • IPC_CREAT:如果 key 不存在则创建新的信号集
  • IPC_EXCL:同时指定了 IPC_CREAT ,如果信号集已存在,则返回EEXIST错误

信号量控制操作

#include <sys/types.h>
#include <sys/sem.h>

int semctl( int semid, int semnum, int cmd, .../* union semun arg */ );

参数 semid:信号量集的标识符

参数 semnum:信号量集中的具体信号量编号

参数 cmd:需执行的操作

  • 常规操作

    • IPC_RMID:删除信号量集

    • IPC_STAT:将 semid_ds 数据结构的副本放入 arg.buf

    • IPC_SET:用 arg.buf 更新 semid_ds 数据结构

  • 信号量值操作

    • GETVAL:返回 semid 信号量集中的第 semnum 个信号量的值
    • SETVAL:设置 semid 信号量集中的第 semnum 个信号量的值
    • GETALL:获取 semid 信号量集中所有信号量的值,放入 arg.array 指向的数组
    • SETALL:使用 arg.array 指向的数组更新 semid 信号量集中的所有信号量的值
  • 获取单个信号量的信息

    • GETPID:返回上一个在该信号量上执行 semop() 的进程的进程ID
    • GETNCNT:返回等待该信号量增长的进程数
    • GETZGNT:返回等待该信号量变为0的进程数

参数 arg:一些特定的操作需要传入第四个参数

信号量操作

#include <sys/types.h>
#include <sys/sem.h>

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

参数 sops :数组

struct sembuf{
	unsigned short sem_num; /*执行操作的信号量编号*/
    short sem_op; /*执行的操作*/
    short sem_flg;
};
  • sem_op 大于 0,信号量加上 sem_op
  • sem_op 等于 0,检查信号量值是否为0,为0就立即结束操作,否则阻塞直到为0
  • sem_op 小于 0,信号量减去 sem_op

参数 nsops :数组的大小

参数 sem_flag:

  • IPC_NOWAIT 防止阻塞,而直接返回错误
  • SEM_UNDO 当进程被终止时,会撤销上一步的信号量操作
struct sembuf sops[3];

/*信号量0 - 1*/
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].se,_flg = 0;

/*信号量1 + 2*/
sops[1].sem_num = 1;
sops[1].sem_op = 2;
sops[1].se,_flg = 0;

sops[3].sem_num = 2;
sops[3].sem_op = 0;
sops[3].se,_flg = IPC_NOWAIT; /*设置非阻塞*/

if( semop( semid, sops, 3 ) == -1 )
{
    if( errno == EAGAIN )
    {
        printf( "信号量2发送了阻塞\n" );
    }
    else
    {
        perror( "其他错误\n" );
    }
}

信号量关联结构体

struct semid_ds{
  	struct ipc_perm sem_perm;
	time_t 		    sem_otime;
    time_t 		    sem_ctime;
    unsigned long   sem_nsems;
};

sem_otime:在创建信号量集时会将这个字段设置为 0,然后在每次成功的 semop() 调用或当信号量值因 SEM_UNDO 操作而发生变更时将这个字段设置为当前时间

sem_ctime:在创建信号量时以及每个成功的 IPC_SET、SETALL 和 SETVAL 操作执行完毕之后将这个字段设置为当前时间

sem_nsems:在创建集合时将这个字段的值初始化为集合中信号量的数量

代码

利用二值信号量在父子进程之间进行同步

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>

#if _SEM_SEMUN_UNDEFINED
union semun
{
 int val;
 struct semid_ds *buf;
 unsigned short *array;
 #if defined(__linux__)
     struct seminfo *__buf;
 #endif
};
#endif

/*创建信号量*/
int createSem( void )
{
    int semid;
    semid = semget( IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | S_IRUSR|S_IWUSR );
    if( semid != -1 )/*成功创建信号集*/
    {
        union semun arg;
        /*信号量值初始化为0*/
        arg.val = 0;
        semctl( semid, 0, SETVAL, arg );
    }
    else /*创建失败*/
    {
        perror("semget failed\n");
        while(1);
    }

    return semid;
}

/*保留信号量*/
int reserveSem( int semId, int semNum )
{
    struct sembuf sops;

    sops.sem_num = semNum;
    sops.sem_op = -1; /*信号量-1*/
    sops.sem_flg = SEM_UNDO;

    /*如果在信号量等于0时减1会导致阻塞*/
    while( semop( semId, &sops, 1 ) == -1 )
    {
        if( errno != EINTR) /*在阻塞时被信号打断了*/
        {
            perror( "EINTR\n" );
            return -1;
        }
    }

    return 0;
}
/*释放信号量*/
int releaseSem( int semId, int semNum )
{
    struct sembuf sops;

    sops.sem_num = semNum;
    sops.sem_op = 1; /*信号量+1*/
    sops.sem_flg = SEM_UNDO;

    return semop( semId, &sops, 1 );
}

int main( void )
{
    int semid;
    semid =  createSem();
    switch( fork() )
    {
        case -1:
            perror("fork failed\n");
            _exit( EXIT_FAILURE );
            break;
        case 0:
            reserveSem( semid, 0 );
            printf("case 0\n");
            _exit( EXIT_SUCCESS );
            break;
        default:
            sleep(5);
            releaseSem( semid, 0 );
            printf("default\n");
            wait(NULL);
            break;
    }
    exit( EXIT_SUCCESS );
}
举报

相关推荐

0 条评论