Linux初学第十二天<进程间通信五、信号量>

一、信号量概述

       信号量(Semaphore)与已经学过的的IPC结构不同,它是一个计数器,信号量用于实现进程间的互斥与同步,而不是用于存储进程间的通信数据。举例子来理解这句话:
       把一个临界资源(也可以认为是共享内存)比作一个上了锁的房间,某人比作一个进程,而开锁的钥匙就是信号量。某人有钥匙的话,就能开锁进入房间里,做它需要做的事情,如果这时候有第二个人想进入房间里的话,是进不去的,因为它没有钥匙(信号量)。直到进去的那个人除了房间把钥匙放在某个地方,然后第二人拿到了钥匙,才能渠道房间里去。这钥匙起到的作用就是信号量

二、相关API

        全都放在了<sys/sem.h> 头文件中

1.int semget(key_t key,int num_sems,int sem_flags)

        创建或获取一个信号量组
参数说明:
              1. key 索引值,可使用ftok函数来获取;
              2. num_sems 信号量的个数;
              3. sem_flags 传递IPC_CREAT 加上权限可以创建信号量集;
返回值: 成功返回信号集的ID;失败返回 -1
用法:

key_t key;
key=ftok(".",1);
int semid=semget(key,1,IPC_CREAT|0666);//创建一个信号量

2.int semop(int semid,struct sembuf semoparray[],size_t semops)

        操作信号量组函数。有两种操作,p操作:即上面例子提到的拿钥匙,v操作:上面例子提到的放钥匙。
参数说明:
              1. semid 被操作目标信号量的ID号;
              2. semoparray[] 结构体数组,它有三个成员:
                            (1)sem_num:需要操作第几个信号量;
                            (2)sem_op:信号量的值,-1为减1
                            (3)sem_flg:一般赋值SEM_UNDO;
              3. semops :semoparray[] 数组的个数。
返回值: 成功返回0;失败返回 -1

p操作用法:

struct sembuf *semoparry;
semoparry=(struct sembuf *)malloc(sizeof(struct sembuf));
semoparry->sem_num=0;
semoparry->sem_op=-1;
semoparry->sem_flg=SEM_UNDO;
semop(semid,semoparry,1);

v操作用法:

struct sembuf *semoparry;
semoparry=(struct sembuf *)malloc(sizeof(struct sembuf));
semoparry->sem_num=0;
semoparry->sem_op=1;
semoparry->sem_flg=SEM_UNDO;
semop(semid,semoparry,1);

2.int semctl(int semid,int sem_num,int cmd,…)

        控制信号量函数。用来控制信号,比如初始化,移除信号量等。
参数说明:
              1. semid 被操作目标信号量的ID号;
              2. sem_num 信号量的个数,即需要操作第几个信号量;
              3. cmd 操作指令,它有几个宏,我们这里用SETVAL(操作信号量的值)
              4. 其他 这个参数,是配合SETVAL指令来使用,它是个联合体而且需要定义这个联合体:

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 */                                      
 };

返回值: 成功返回0;失败返回 -1
用法:

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*/                                       
 };
union semun value;
value.val=1;
semctl(semid,0,SETVAL,value);

三、小实验

        认识了这三个API之后,我们可以做个小实验,来看看信号量是怎么实现管控进程。
代码:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
//int semget(key_t key, int nsems, int semflg);
//定义联合体
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) */
};

//pGetKey p操作
void pGetKey(int semid)
{
        int semop_s;
        struct sembuf *sops;
        sops=(struct sembuf *)malloc(sizeof(struct sembuf));
        sops->sem_num=0;
        sops->sem_op=-1;
        sops->sem_flg=SEM_UNDO;
        semop_s=semop(semid,sops,1);
        if(semop_s==0){
                printf("I get key!\n");
        }else{
                printf("I no key!\n");
        }
}

//vGetKey v操作
void vGetKey(int semid)
{
        int semop_s;
        struct sembuf *sops;
        sops=(struct sembuf *)malloc(sizeof(struct sembuf));
        sops->sem_num=0;
        sops->sem_op=1;
        sops->sem_flg=SEM_UNDO;
        semop_s=semop(semid,sops,1);

        if(semop_s==0){
                printf("I no key!\n");
        }else{
                printf("I have key!\n");
        }

}
int main(int argc,char **argv)
{
        key_t key;
        key=ftok(".",12);
        int semid;
		int pid;

        semid=semget(key,1,IPC_CREAT|0666);//创建信号量

        union semun Value;
        Value.val=0;

        semctl(semid,0,SETVAL,Value);//初始化信号量

        pid=fork();//创建子进程
        if(pid>0){
                pGetKey(semid);//父进程p操作
                printf("This is father!\n");
                vGetKey(semid);//父进程v操作
        }else if(pid==0){
                printf("This is child!\n");
                vGetKey(semid);//子进程v操作  
        }else{
                printf("fork error!\n");
        }
        return 0;
}

        因为 semctl 时,把信号量的值设置为 0;所以父进程p操作(拿钥匙)时会阻塞,当子进程v操作后,重新放出了钥匙。父进程才能执行打印。所以这个运行结果就是子进程先执行,然后父进程才执行。
运行结果:
在这里插入图片描述

四、进程间通信总结

       进程间通信(IPC)学到现在,就可结束了了。回顾一下进程间通信都学了写啥:
        一、通道和命名通道FIFO: 是个半双工的通信方式,读写不可以同时进行,必须读完后关闭通道,然后才可以写入。而FIFO相当于一个文件,可以用普通的文件操作来操作FIFO。
        二、消息队列: 消息队列是个链表,发消息就是往消息链表里添加一个节点(消息的结构体),而且两个不相关的进程都可以操作消息队列。
       三、共享内存: 区别于两个进程之间的另外一块内存空间,而且可以连接到进程的空间当中,可以使用普通的指针操作来访问共享内存。
        四、信号: Linux操作系统中已经定义了64个信号,我们可以通过这些信号来控制进程(入门版)或传递消息(高级版);
        五:信号量: 区别于前四个IPC结构,不是一种进程通信方式,我们可以理解为它起到一个进程调度的作用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值