2021-06-20

信号量是一个计数器,常用于处理进程或线程的同步问题,特别是对临界资源的同步访问。

1.semget创建或打开信号量集,
原型:int semget(key_t key, int nsems, int semflg);
该函数执行成功则返回一个信号量集的标识符,失败返回-1。返回的参数key是由ftok得到的键值;

第二个参数nsems指明要创建的信号量集包含的信号量个数。如果只是打开信号量,把nsems设置为0即可。该参数只在创建信号量集时有效。

第三个参数semflg为操作标识,可取如下值:

0:取信号量集标识符,若不存在则函数会报错

IPC_CREAT:当semflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的信号量集,则新建一个信号量集;如果存在这样的信号量集,返回此信号量集的标识符

IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的信号量集,则新建一个消息队列;如果存在这样的信号量集则报错

上述semflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限

2.信号量的操作
原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
信号量的值与相应资源的使用情况有关,当它的值大于0时,表示当前可用资源的数量,当他的值小于0时,其绝对值表示等待
semid:信号量集标识符
sops:指向进行操作的信号量集结构体数组的首地址,此结构的具体说明如下:
struct sembuf {

short semnum; /*信号量集合中的信号量编号,0代表第1个信号量*/

short val;/*若val>0进行V操作信号量值加val,表示进程释放控制的资源 */

/若val<0进行P操作信号量值减val,若(semval-val)<0(semval为该信号量值),则调用进程阻塞,直到资源可用;若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误/

/若val==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误/

short flag;  /*0 设置信号量的默认操作*/

/IPC_NOWAIT设置信号量操作不等待/

/SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值/

};
nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作

3.semctl (得到一个信号量集标识符或创建一个信号量集对象)
原型:int semctl(int semid, int semnum, int cmd, union semun arg)
semid:信号量集标识符
semnum:信号量集数组上的下标,表示某一个信号量
cmd:
PC_STAT:从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中

IPC_SET:设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值

IPC_RMID:从内核中删除信号量集合

GETALL:从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中

GETNCNT:返回当前等待资源的进程个数

GETPID:返回最后一个执行系统调用semop()进程的PID

GETVAL:返回信号量集合内单个信号量的值

GETZCNT:返回当前等待100%资源利用的进程个数

SETALL与GETALL正好相反

SETVAL:用联合体中val成员的值设置信号量集合中单个信号量的值

arg:

union semun {

short val; /SETVAL用的值/

struct semid_ds* buf; /IPC_STAT、IPC_SET用的semid_ds结构/

unsigned short* array; /SETALL、GETALL用的数组值/

struct seminfo *buf; /为控制IPC_INFO提供的缓存/

} arg;

编程案例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;

};

void pGetKey(int id)
{
struct sembuf set;

    set.sem_num = 0; //0代表第1个信号量
    set.sem_op = -1;  //对应上面的val ,-1表示减少一个信号量(拿锁)
    set.sem_flg=SEM_UNDO;

    semop(id,&set,1);
    printf("getKey\n");

}

void vPutBackKey(int id)
{
struct sembuf set;

    set.sem_num = 0; //0代表第1个信号量
    set.sem_op = 1;    //对应上面的val ,1表示增加一个信号量(放锁)

    set.sem_flg=SEM_UNDO;

    semop(id,&set,1);
    printf("getKey\n");

}

int main(int argc, char const *argv[])
{
key_t key;
key = ftok(".",2);

    int semid =  semget(key,1,IPC_CREAT|0666);  //创建获取信号量
    union semun initsem;
    initsem.val = 0;     //初始化信号量个数为0
    semctl(semid,0,SETVAL,initsem);//初始化信号量,设置SETVAL的值为inistem

    int pid = fork();
    if(pid>0){
            pGetKey(semid);   //获取信号量(拿锁)
            printf("this is fsther\n");
            vPutBackKey(semid);//放回信号量(放锁)
            semctl(semid,0,IPC_RMID);
    }else if(pid == 0){
            printf("this is child\n");
            vPutBackKey(semid); //放回信号量(放锁)
    }else{
            printf("fork fail\n");
    }

    return 0;

}

运行后因为初始化信号量为0 (锁为0),父进程开始得不到信号量(拿不到锁)阻塞,等子进程运行放信号量(放锁)后,父进程才能获得信号量(拿锁),此时父进程才能运行

运行结果为:在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值