进程间通信-信号量

1、信号量

信号量本质上是一个计数器(不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),它和管道不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。

 

临界资源:具有排他性的资源

临界区:访问临界资源的代码段

2、信号量的工作原理

由于信号量只能进行两种操作,等待和发送信号,即P, V操作:

P操作:上锁,如果计数器的值大于0,就减1;如果它的值为0,就挂起该进程的执行

V操作:解锁,如果有其他进程因等待而被挂起,就让它恢复运行,如果没有进程因等待而挂起,计算器加1.

 

在信号量进行PV操作时都是原子操作(因为它需要保护临界资源)

原子操作:单指令的操作称为原子的,单条指令的执行是不会被打断的。

 

3、进程同步:

进程同步也就是进程之间直接的制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系来源于他们之间的合作。

 

4、信号量常用函数:

原型:

int semget(key_t key,int nsems,int flags)

作用:创建信号量

参数:key表示信号集的名字,一般由ftok函数产生。

nsems表示信号集中信号量的个数。

flags标识信号量集合的权限,如0644,和以下参数一起使用。

IPC_CREAT表示key不存在就创建

IPC_EXCL表示key存在就返回失败

IPC_NOWAIT表示如果需要等待,则直接返回错误。

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

原型:

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

作用:删除或初始化信号量值

参数:semid为semget返回的信号量标识

semnum为信号量集中的第几个信号量

cmd为要采取的操作:

一般使用以下两种:GETVAL获取信号量的值;SETVAL设置信号量的值。

第四个参数为联合体union semun,用来设置信号量的值

cmd操作为GETVAL时:semctl第二个参数为信号量集的编号,若执行成功,semctl返回当前信号量的值,失败返回-1;

cmd操作为SETVAL时,第四个参数要设置val,成功返回0,失败返回-1;

原型:

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

作用:修改集合中一个或多个信号量的值

参数:semid信号量标识

sops是一个sembuf结构体指针

nsops标识信号量个数

struct sembuf{  
    short sem_num;   //除非使用一组信号量,否则它为0  
    short sem_op;   //信号量在一次操作中需要改变的数据通常是两个数                                         
                    //一个是-1,即P(等待)操作,  
                    //一个是+1,即V(发送信号)操作。  
    short sem_flg; //通常为SEM_UNDO,使操作系统跟踪信号量,  
                  //并在进程没有释放该信号量而终止时,操作系统释放信号量  
};  
#include <stdio.h>
#include <stdlib.h>
#include <sys//ipc.h>
#include <sys/sem.h>
union semun{
    int val;
};
void P(int id)
{
    //设置信号量的值,P操作时sem_op为-1
    struct sembuf sb;
    sb.sem_num = 0;
    sb.sem_op = -1;
    sb.sem_flg = 0;
    semop(id, &sb, 1);
}
void V(int id)
{
    //设置信号量的值,V操作时sem_op为1
    struct sembuf sb;
    sb.sem_num = 0;
    sb.sem_op = 1;
    sb.sem_flg = 0;
    semop(id, &sb, 1);
}
int main()
{
    //创建信号量
    int id = semget(123,1,IPC_CREAT|0644);
    if(id == -1)
    {
        perror("id");
        exit(1);
    }
    printf("semget success!\n");
    union semun su;
    su.val = 1;
    //初始化信号量
    semctl(id, 0, SETVAL, su);
    pid_t pid = fork();
    if(pid == 0)
    {
        //获取信号量的标识
        int semid = semget(123, 0, 0);
        while(1)
        {
            //P操作,上锁
            P(semid);
            printf("A\n");
            sleep(1);
            printf("A\n");
            sleep(1);
            //V操作,解锁
            V(semid);
        }
    }
    else if(pid > 0)
    {
        int semid = semget(123, 0, 0);
        while(1)
        {
            P(semid);
            printf("B\n");
            sleep(1);
            printf("B\n");
            sleep(1);
            V(semid);
        }
    }
}

结果:

[root@livedvd tmp]# ./sem_test 
semget success!
B
B
A
A
B
B
A
A
B
B
A
A

可以看到,打印结果时,AA,BB是成对出现的,如果没有加信号量,AB出现的顺序是乱序的。

 

参考:

https://blog.csdn.net/skyroben/article/details/72513985

https://blog.csdn.net/enjoymyselflzz/article/details/81603577

https://blog.csdn.net/l__xing/article/details/80217842

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值