C语言用信号量进行pv操作

一、获取信号量

用semget()函数获取信号量,其中semget()函数中所需传的参数如下:

semget(key_t key , int nsems, semflg);

参数解释:

1、其中key可以通过ftok() 函数进行获取,ftok函数所需传入的参数为①文件路径②一个八位的整数

2、nsems是所需创建的信号量的个数,如果只是访问已存在的信号量集合,可以将此参数设置为0。

3、semflg是一个标志参数,用于指定创建或访问信号量集合的方式,常用的标志有:

IPC_CREAT:如果指定的键值对应的信号量集合不存在,则创建一个新的信号量集合。

IPC_EXCL:与IPC_CREAT同时使用,如果指定的键值对应的信号量集合已经存在,则返回错误。

0666:用于指定信号量集合的权限,表示允许所有用户读写信号量集合,这里也可以是填其它权限,如0664等,不一定是0666

具体实现:

int sem_creat(key_t key, int nsems)
{
    int semid = semget(key, nsems, 0664|IPC_CREAT);//创建信号量
    
    if(semid < 0){  //通过返回值判断信号量是否创建成功,小于零则创建失败,打印一下错误信息并退出
        perror("semget");
        exit(0);    
    }
    return semid;
}

二、初始化信号量

用semctl来初始化信号量,其中semctl函数所需传入的参数如下:

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

参数解释: 

1、semid:信号量集合的标识符,通过smget函数获取(也就是上一步获取信号量的时候得到的)。

2、semnum:要操作的信号量在集合中的索引(也就是要初始化哪一个信号量),对于一个信号量集合可以有多个信号量,而semnum指定了要操作的具体信号量

3、cmd:指定要执行的命令,可以是以下几种之一:

①、GETVAL:获取指定信号量的当前值。

②、SETVAL:设置指定信号量的值。

③、IPC_RMID:删除信号量集合。

④、IPC_STAT:获取信号量集合的状态信息。

⑤、IPC_SET:设置信号量集合的状态信息。

⑥、GETALL:获取所有信号量的当前值。

⑦、SETALL:设置所有信号量的值。

4、....:表示可变参数,根据不同的命令有不同的使用方式,需要注意的是。semctl函数的第四个参数arg的具体含义取决于使用命令和使用方式(也就是根据第三个参数来的),对于某些命令,arg是一个整数值,而对于其它命令,arg是一个指向union semun结构体的指针。在使用semctl函数时,需要根据具体的需求和命令来确定如何使用这个参数。

具体实现:

void init_sem(int semid,int semnum,int val)//初始化需要传semid和初始化哪个信号量,还有初始化的值
{
    int ret;
    union semun sem;//这个是根据第三个参数来的,semun联合体中有好多种数据类型
    sem.val = val;
    ret = semctl(semid,semnum,SETVAL,sem);//这里用的是SETVAL,所以要传第四个参数,也就是你要初始化的值
    if(ret < 0)//判断是否初始化成功
    {
        perror("semctl");
        exit(0);
    }
}

注意: union semun联合体(也叫共用体)需要我们自己去实现,大概的样子长这样:

union semun {
    int val;                  // 设置信号量的初始值或获取当前值
    struct semid_ds *buf;     // IPC_STAT 或 IPC_SET 时使用
    unsigned short *array;    // GETALL 或 SETALL 时使用
};

三、进行p操作(加锁操作)

用semop函数来进行加锁操作,semop函数传入的参数如下:

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

参数解释: 

1、semid:是一个信号量集标识符,用于指定要操作的信号量集。在使用semget函数创建信号量集后,会返回该标识符。

2、sops:是一个指向sembuf结构体数组的指针,每个sembuf结构体表示对一个信号量的操作(也就是通过结构体来设置信号量当前的状态)。

sembuf结构体的定义如下:

struct sembuf {
    unsigned short sem_num;  // 信号量在信号量集中的索引
    short sem_op;            // 信号量的操作
    short sem_flg;           // 操作标志
};

其中

sem_num是要操作的信号量在信号量集中的索引,从0开始计数

sem_op:是对信号量的操作,可以是正整数、负整数或零。

·  正整数表示将信号量的值增加相应的数量。

·  负整数表示将信号量的值减少相应的数量。

·  零表示等待信号量变为零。

 sem_flg是操作标志,用于指定操作的行为,常见的取值有:

  • SEM_UNDO:如果进程退出时仍然持有该信号量,则会自动释放。
  • IPC_NOWAIT:如果无法立即执行操作,则函数会立即返回,并设置错误码为EAGAIN

3、nsops:表示sembuf结构体数组中元素的数量,即要进行操作的信号量的个数。

具体实现:

void sem_p(int semid,int semnum,size_t nsops)
{
    struct sembuf sops;
    sops.sem_num = semnum;//设置要操作哪个信号量
    sops.sem_op = -1;//设置信号量要执行的操作,-1是上锁的操作,锁-1
    sops.sem_flg = SEM_UNDO;//防止死锁‘
    
    int ret = semop(semid,&sops,nsops);
    if(ret < 0)//判断是否执行成功
    {
        perror("semop-p");
        exit(0);
    }    
}

四、进行v操作(解锁操作) 

进行v操作也是用的semop函数,只是传的参数不同,sops.sem_op = 1。

具体实现:

void sem_v(int semnum,size_t nsops)
{
    struct sembuf sops;
    sops.sem_num = semnum;
    sops.sem_op = +1;//与p操作不同的地方
    sops.sem_flg = SEM_UNDO;
    
    int ret = semop(semid,&sops,nsops);
    if(ret < 0)
    {
        perror("semop-v");
        exit(0);
    }
}

五、释放信号量

释放信号量也可以用semctl()函数,只是传的参数有变化

具体实现:

void sem_destroy(int semid)
{
    int ret = semctl(semid,-1,IPC_RMID);
    if(ret < 0)
    {
        perror("semctl-rmid");
        exit(0);
    }
}

你可能感兴趣的:(c语言,开发语言,linux)