Linux-信号量与P,V操作

内容

  • 使用信号量实现进程互斥
  • 使用信号量及PV实现子进程读写同步

机理

Linux信号量集

Linux信号量作为IPC机制的一种,与其他通信方式类似,Linux也是通过kern_ipc_perm结构中的key来唯一标志一个信号量集,并通过该结构设置并检查访问权限。针对信号量集,系统维护一个由信号量集组成的数组,数组中的每个单元指向一个信号量集。

PV原语

PV操作是典型的同步机制之一。用一个信号量与一个消息联系起来,当信号量的值为0时,表示期望的消息尚未产生;当信号量的值非0时,表示期望的消息已经存在。用PV操作实现进程同步时,调用P操作测试消息是否到达,调用V操作发送消息。

调用函数说明

创建一个新的信号量集或获取一个已存在的信号量集

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

参数说明:

key

  • 使用IPC_PRIVATE,由系统产生key值并返回标识符,或者返回key值已存在的信号集的标识符。
  • 用户指定一个非0整数型值,对信号量集的打开或存取依赖于semflg参数的取值。

nsems指定打开或者新创建的信号量集将包含的信号量的数目。

semflg当key不为IPC_PRIVATE时使用

  • 若只设置semflg的IPC_CREAT位,则创建一个信号量集,如果该信号量集已经存在,则返回其标识符。
  • semflg的IPC_CREAT|IPC_EXCL位,则创建一个新的信号量集,如果该key值的信号量已经存在,则返回错误信息。
  • 只设置IPC_EXCL而不设置IPC_CREAT位没有任何意义

返回值:正确返回信号量集的标识符,错误时返回-1。

如,创建一个只包含一个信号量的信号量集:

  • semid = semget(IPC_PRIVATE,1,IPC_CREAT|0666);

对信号量的PV操作

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

参数说明:

  • semid是信号量集的标识符,由semget()得到
  • sops指向一个sembuf结构数组,该数组的每一个元素对以一次信号量操作。
struct sembuf
{
unsigned short sem_num; /*semaphore index in array*/
short sem_op; /*semaphore operation*/
short sem_flg; /*operation flags*/
};
/*
sem_num标明它是信号量集的第几个元素,从0开始
sem_op指定信号量采取的操作
<0相当于P操作,占有资源
>0相当于V操作,释放资源
=0进程睡眠直到信号量的值为0
sem_flg指明操作的执行模式,两个标志位。一个是IPC_NOWAIT,指明以非阻塞方式操作信号量。一个是SEM_UNDO,指明内核为信号量操作保留恢复值。
*/

返回值:正确返回0,错误时返回-1

信号量集的控制函数

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

参数说明:

  • semid是信号量集的标识符,由semget()得到
  • semnum指定semid信号量集的第几个信号量,在撤销信号量集时,此参数可缺省。
  • cmd指定操作类型。
取值 含义
GETVAL 返回semnum指定的信号量的semval域值
SETVAL 指定semval域值为arg.val
GETPID 返回semnum指定信号量sempid
GETNCNT 返回semncnt
GETZCNT 返回semzcnt
GETALL 返回所有信号量的值,结果保存到arg.array中
SETALL 通过arg.array更新所有信号量的值
IPC_STAT 获取信号量集的arg.array,存入arg.buf
IPC_SET 将arg.buf数据结构的sem_perm.uid,sem_perm.gid,sem_perm.mode赋给sem_array,此操作仅限root、sem_perm.cuid或sem_perm.uid
IPC_RMID 删除指定信号量集。此操作仅限root、sem_perm.cuid或sem_perm.uid
IPC_INFO 获取信号量集的相关信息存放于arg.buf中
  • arg为5中数据类型的共同体类型semnu,该类型在include/linux/sem.h中定义
union senum
{
int val; /*value for setval*/
struct semi_ds *buf; /*buffer for IPC_STAT & IPC_SET*/
unsigned short *array; /*array for GETALL & SETALL*/
struct seminfo *_buf; /*buffer for IPC_INFO*/
void *_pad
};

返回值:正确时根据cmd的的不同返回值或0,错误时返回-1。

撤销信号量集

semctl(semid ,IPC_RMID ,0)

思路

  • 定义信号量标志符:int semid;
  • 定义信号量数据结构
    • 定义PV操作所用的数据结构:struct sembuf P,V;
    • 定义给信号量赋初值的参数数据结构:union semun arg;
  • 申请一个信号量的信号量集
  • 对每一个信号量semid赋初值
  • 定义信号量的P操作
  • 定义信号量的V操作
  • 执行PV操作
  • 撤销信号量

实例

  • 利用信号量实现进程互斥
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sem.h>
/*
父子进程共享一个临界资源(这里就是stdout),
每个进程循环进入临界区3次
父进程进入后显示parent in
父进程出去后显示parent out
子进程进入后显示chld in
子进程出去后显示chld out
*/
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
int mutexid; //互斥信号量 int main()
{
int chld,i,j; struct sembuf P,V;
union semun arg;
/*****申请只有一个信号量的信号量集*****/
mutexid=semget(IPC_PRIVATE,1,0666|IPC_CREAT);
/*****分别对每个信号量赋初值*****/
arg.val=1;
if(semctl(mutexid,0,SETVAL,arg)==-1)perror("semctl setval error");
/*****定义PV操作*****/
P.sem_num=0;
P.sem_op=-1;
P.sem_flg=SEM_UNDO;
V.sem_num=0;
V.sem_op=1;
V.sem_flg=SEM_UNDO; while((chld=fork())==-1);
if(chld>0)
{/*****父进程块*****/
i=1;
while(i<=3)
{
sleep(1);
semop(mutexid,&P,1); /*占有临界资源*/
printf("parent in\n");
sleep(1);
printf("parent out\n");
semop(mutexid,&V,1); /*释放临界资源*/
i++;
}
wait(0);
/*****撤销信号量集*****/
semctl(mutexid,IPC_RMID,0);
exit(0);
}
else
{/*****子进程块*****/
j=1;
while(j<=3)
{
sleep(1);
semop(mutexid,&P,1);
printf("chld in\n");
sleep(1);
printf("chld out\n");
semop(mutexid,&V,1);
j++;
}
exit(0);
}
}

运行结果

  • 利用信号量实现进程同步
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/shm.h>
/*
父子进程共用一个存储区,
子进程写入信息,父进程
读取信息。
*/
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
}; int emptyid;
int fullid; //同步信号量 main()
{
int chld; struct sembuf P,V;
union semun arg; int shmid;
char *viraddr;
char buffer[BUFSIZ];
/*****申请只有一个信号量的信号量集*****/
emptyid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
fullid=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
/*****分别对每个信号量赋初值*****/
arg.val=1;
if(semctl(emptyid,0,SETVAL,arg)==-1)perror("semctl setval error");
arg.val=0;
if(semctl(fullid,0,SETVAL,arg)==-1)perror("semctl setval error");
/*****定义PV操作*****/
P.sem_num=0;
P.sem_op=-1;
P.sem_flg=SEM_UNDO;
V.sem_num=0;
V.sem_op=1;
V.sem_flg=SEM_UNDO;
/*****申请共享内存空间*****/
shmid=shmget(IPC_PRIVATE,BUFSIZ,0666|IPC_CREAT);
viraddr=(char*)shmat(shmid,0,0); while((chld=fork())==-1);
if(chld>0)
{
while(1)
{
semop(fullid,&P,1);
printf("Your message is:\n%s",viraddr);
semop(emptyid,&V,1);
if(strncmp(viraddr,"end",3)==0)break;
}
wait(0);
shmdt(viraddr);
/*****撤销信号量集、释放共享内存*****/
shmctl(shmid,IPC_RMID,0);
semctl(emptyid,IPC_RMID,0);
semctl(fullid,IPC_RMID,0);
printf("Parent exit!\n");
exit(0);
}
else
{
while(1)
{
semop(emptyid,&P,1);
puts("Enter your text:");
fgets(buffer,BUFSIZ,stdin);
strcpy(viraddr,buffer);
semop(fullid,&V,1);
if(strncmp(viraddr,"end",3)==0)
{
sleep(1);
break;
}
}
printf("Child exit!\n");
exit(0);
}
}

运行结果

Linux-信号量与P,V操作的更多相关文章

  1. 信号量及P/V操作

    有一个厕所,允许多个男生同时使用,也允许一个女生使用,但是不允许男女共用(那岂不是乱了套)通过厕所门口有一个三面小牌子来运行.一面是男生在用,第二面是女生在用,第三面是空.运行机制:第一个进入空厕所男 ...

  2. Linux中的System V信号量

    在进程同步,并发运行时,保证按序地访问共享资源是十分重要的.因此引入了临界区的概念,一次只能有一个线程进入临界区完成他的指令.而信号量(semaphore)的作用,类似于一个交通信号灯,它负责进程协作 ...

  3. Linux信号量详解

    1.什么是信号量信号量是一种特殊的变量,访问具有原子性.只允许对它进行两个操作:1)等待信号量当信号量值为0时,程序等待:当信号量值大于0时,信号量减1,程序继续运行.2)发送信号量将信号量值加1. ...

  4. linux信号量(转载)

    本文转载自http://blog.csdn.net/qinxiongxu/article/details/7830537 信号量一.什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只 ...

  5. Linux信号量同步共享内存实验.

    Linux信号量同步共享内存实验. Linux信号量同步共享内存实验. 简述 程序流程 信号量和共享内存的系统函数 信号量系统函数及接口 共享内存系统函数及接口 写程序 读程序 简述 本文主要内容是自 ...

  6. 最全面的linux信号量解析

    信号量 一.什么是信号量 信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程) 所拥有. 信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明 它被占用, ...

  7. Linux 信号量详解一

    信号量主要用于进程间(不是线程)的互斥,通过sem_p()函数加锁使用资源,sem_v函数解锁释放资源,在加锁期间,CPU从硬件级别关闭中断,防止pv操作被打断. semget函数 int semge ...

  8. 操作系统中的P,V操作(转)

    无论是计算机考研.计算机软件水平考试.计算机操作系统期末考试还是其他计算机岗位考试,P.V原语操作都是一个常考点.下面笔者总结了关于P.V操作的一些知识. 信号量是最早出现的用来解决进程同步与互斥问题 ...

  9. 关于P,V操作理解的突破,关于并发设计与并行

    今天又找了一篇博客研究P,V操作.. 发现..它有一个变量没有声明.. 我就换了篇博客..http://c.biancheng.net/cpp/html/2600.html 然后就看懂了.. 关键突破 ...

随机推荐

  1. Linux记录-sftp上传大文件

    1.Alt +P 进入sftp会话 2.pwd显示linux目录 lpwd显示windows目录 3.lcd切换windows目录 cd切换linux目录 4.put上传 5.get下载 6.help ...

  2. python的copy模块理解

    首先直接上结论: —–我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在.所以改变原有被复制对象不会对已经复制出来的新对象产生影响. —–而浅复制并不会产生一个独立的对 ...

  3. jira7.3.6的安装步骤

    准备环境:jira7.3需要jdk1.8 1.下载jira需要的版本 https://www.atlassian.com/software/jira/download 2.上传atlassian-ji ...

  4. .call() 与 .apply() 的用法及区别

    首先说明两个方法的含义: apply:调用一个对象的一个方法,用另一个对象替换当前对象.例如:B.apply(A, arguments);即A对象应用B对象的方法.call:调用一个对象的一个方法,用 ...

  5. 使用python爬虫,批量爬取抖音app视频(requests+Fiddler+appium)

    抖音很火,楼主使用python随机爬取抖音视频,并且无水印下载,人家都说天下没有爬不到的数据,so,楼主决定试试水,纯属技术爱好,分享给大家.. 1.楼主首先使用Fiddler4来抓取手机抖音app这 ...

  6. 【python】面向对象编程之@property、@setter、@getter、@deleter用法

    @property装饰器作用:把一个方法变成属性调用 使用@property可以实现将类方法转换为只读属性,同时可以自定义setter.getter.deleter方法 @property&@ ...

  7. springboot动态多数据源

    参考文章:https://www.cnblogs.com/hehehaha/p/6147096.html 前言 目标是springboot工程支持多个MySQL数据源,在代码层面上,同一个SQL(Ma ...

  8. .Net ABP 框架 service 无法访问

    最近在看ABP框架,按照文档写好service后,怎么也访问不到,后来发现,忘记把service类设置为public的了! 不写public ABP框架就不能将这个service映射为controll ...

  9. python学习之re (?P...)通过关键字获取组以及( P=name)

    和其他的RE表达式一样,但是匹配的子串可以通过group的名字 name来获取.即  result.group('name')  (提示,字符串数字都是常量,所以关键字都可以被视为整型(hash结果) ...

  10. cadcam

    Email:kefu007@vip.qq.com 13D TIMON 2007 英語版2007 23DVIA Composer V6R2013 中文版2013 3ABQUS V6.11 6.11 4A ...