信号量 P V测试详解
信号量
当我们编写的程序使用了线程时,不管它是运行在多用户系统上,多进程系统上,还是运行在多用户多进程系统上,我们通常会发现,程序中存在着一部分临界代码,我们需要确保只有一个进程可以进入这个临界代码并拥有对资源独占的访问权
信号量的定义
最简单的信号量是只能取值0和1的变量,即二进制信号量,这也是信号量最常见的一种形式
pv操作的定义非常简单,假设有一个信号变量sv,如下所示:
p(sv) 如果sv的值大于零,就给它减去1,如果它的值等于0,就挂起该进程的执行
v(sv) 如果有其它进程因等待sv而被挂起,就让它恢复运行,如果没有就加1
Linux的信号量机制
信号量函数定义如下:
#include
int semctl(int sem_id,int sem_num,int command,...);
int semget(key_t key,int num_sems,int sem_flags);
int semop(int sem_id,struct sembuf *sem_ops,size_t num_sem_ops);
参数key的作用很像一个文件名
1.semget函数
semget函数的作用是创建一个新信号或取得一个已有信号量的健
int semget(key_t key,int num_sems,int sem_flags);
第一个参数key是整数值,不相关的进程可以通过它访问一个信号量(两个key值设为相同就可以了),程序对所有信号的访问都是间接的,它先提供一个键,再有系统生成一个相应的信号量标识符
num_sems第二个参数指定需要的信号量数目,它几乎取值都是1
sem_flags参数是一组标志,它与open函数的标志非常相似,它低端的9个比特是该信号的权限
semget函数在成功时返回一个正数,也就是其它信号量要用到的信号量标识符,如果失败返回-1
2.semop函数
semop函数用于改变信号量的值,它的定义如下:
int semop(int sem_id,struct sembuf *sem_ops,size_t num_sem_ops);
第一个参数sem_id是由semget返回的信号量标识符,第二个参数sem_ops是指向一个结构体数组的指针,每个数组元素至少包含如下几个成员
struct sembuf {
short sem_num;
short sem_op;
short sem_flg;
}
第一个成员sem_num是信号量编号,除非你需要使用一组信号量,否则它的取值为0,sem_op成员的值是信号量在一次操作需要改变的值,通常只会用到两个值,一个是-1,也就是p操作,它等待信号量变为可用,一个是+1,也就是v操作
最后一个成员sem_flg通常被设为SEM_UNDO
3.semctl函数
semctl函数的值用来直接控制信号量信息,它的定义如下:
int semctl(int sem_id,int sem_num,int command,...)
第一个参数sem_id是由semget返回的信号量标识符,sem_num参数是信号量的编号,它一般设为0,表示这是一个唯一的信号量,command参数是要采取的动作,如果还有第四个参数,它将是一个union semun结构的,它至少包括如下成员如下:
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}
semctl函数中的command参数可以设置许多不同的值,现在介绍连个常用的,如下:
*SETVAL :用来把信号量初始化为一个已知的值,这个值通过union semun中的val成员设置
*IPC_RMID: 用于删除一个已经无需使用的信号量标识符
下面是一个使用信号量的例子:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/sem.h>
//#include "semun.h"
//在新版2.6内核中关于union sem_union 这个联合体已经被注释了,需要自己写这个联合体
union semun {int val; struct semid_ds *buf; unsigned short *array;} sem_union;
static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;
int main(int argc, char *argv[])
{
int i;
int pause_time;
char op_char = 'O';
srand((unsigned int)getpid());
sem_id = semget((key_t)1234,1,0666 | IPC_CREAT);//创建一个新的信号量,IPC_CREAT的作用是:如果信号量不存在,就创建它
if (argc > 1) {
if(!set_semvalue()) {//初始化信号量的值为1
fprintf(stderr,"Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
op_char = 'X';
sleep(2);
}
for(i = 0;i < 10; i++) {
if(!semaphore_p()) exit(EXIT_FAILURE);//p操作把semop成员设为-1就可以了
printf("%c",op_char);fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf("%c",op_char); fflush(stdout);
if(!semaphore_v()) exit(EXIT_FAILURE);//v操作把semop成员设为1
pause_time = rand() % 2;
sleep(pause_time);
}
printf("\n%d - finished\n",getpid());
if(argc > 1) {
sleep(10);
del_semvalue();//回收信号量
}
exit(EXIT_SUCCESS);
}
//把信号量初始化为一个已知值
static int set_semvalue(void)
{
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id,0,SETVAL,sem_union) == -1) return(0);
return(1);
}
//删除一个信号量
static void del_semvalue(void)
{
union semun sem_union;
if(semctl(sem_id,0,IPC_RMID,sem_union) == -1)
fprintf(stderr,"Failed to delete semaphore\n");
}
//设置信号量的值P操作
static int semaphore_p(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id,&sem_b,1) == -1) {
fprintf(stderr,"semaphore_p failed\n");
return 0;
}
return 1;
}
//设置信号量的值v操作
static int semaphore_v(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id,&sem_b,1) == -1) {
fprintf(stderr,"semaphore_v failed\n");
return 0;
}
return 1;
}
运行这个程序如下:
root@www:/opt/chengxu# ./seml 1
XXXXXXXXXXXX^C
root@www:/opt/chengxu# ./seml
OOOOOOOOOOOOOOO^C
共享内存
共享内存是3个ipc机制中的第二个,它允许两个不相关的进程访问同一个逻辑内存,共享内存是在两个进程之间传递数据的一种非常有效的方式,共享内存使用的函数类似于信号量的函数,如下:
#include <sys/shm.h>
void *shmat(int shm_id,const void *shm_addr,int shmflg);
int shmctl(int shm_id,int cmd,struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key,size_t size,int shmflg);
1.shmget函数
我们使用shmget函数来创建共享内存
int shmget(key_t key,size_t size,int shmflg)
与信号量一样,程序需要提供一个参数key,它有效的为共享内存段命名,shmget函数返回一个共享内存标识符,第二个参数size以字节为单位指定需要共享内存容量,第三个参数shmflg包含9个比特的权限标志
2.shmat函数
第一次创建共享内存时,它不能被任何进程访问,要想启动对该共享内存的访问,必须将其连接到一个进程的地址空间中,这项工作由shmat函数来完成,如下:
void *shmat(int shm_id,const void *shm_addr,int shmflg)
第一个参数shm_id是由shmget返回的共享内存标识符
第二个参数shm_addr指定的是共享内存连接到当前进程中的地址位置,它通常是一个空指针,表示让系统来选择共享内存出现的地址
第三个参数shmflg是一组为标志
如果shmat调用成功,它返回一个指向共享内存第一个字节的指针,如果失败返回-1;
3.shmdt
shmdt函数的作用是将共享内存从当前进程中分离,它的参数是shmat返回的地址指针,成功时返回0,失败返回-1
4.shmctl
它的定义如下所示:
int shmctl(int shm_id,int cmd,struct shmid_ds *buf)
shmid_ds结构体至少包含以下成员
struct shmid_ds {
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
第一个参数shm_id是shmget返回的共享内存标识符
第二个参数command是要采取的动作,它可以取3个值
IPC_STAT 把shmid_ds结构体中的数据设置为共享内存的当前关联
IPC_SET 如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID 删除共享内存段
第三个参数buf是一个空指针,它指向包含共享内存模式和访问权限的结构
下面是一个共享内存例子:
(1)我们首先创建一个头文件,如下:
#define TEXT_SZ 2048
struct shared_use_st {
int written_by_you;
char some_text[TEXT_SZ];
};
(2)第一个程序shm1.c是消费者程序,如下:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shm_com.h"
int main()
{
int running = 1;
void *shared_memory = (void *)0;//指向共享内存第一个字节指针
struct shared_use_st *shared_stuff;
int shmid;//存放共享内存标识符
srand((unsigned int) getpid());
//创建共享内存
shmid = shmget((key_t)1234 ,sizeof(struct shared_use_st),0666 | IPC_CREAT);
if(shmid == -1) {
fprintf(stderr,"shmget failed\n" );
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid,(void *)0,0);//把共享内存连接到进程地址空间
if(shared_memory == (void *)-1) {
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %x\n",(int)shared_memory);
shared_stuff = (struct shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0;
while(running) {//打印消息
if(shared_stuff->written_by_you) {
//从共享内存中读出数据
printf("You wrote: %s",shared_stuff->some_text);
sleep(rand() % 4);
shared_stuff->written_by_you = 0;
if(strncmp(shared_stuff->some_text,"end",3) ==0) {
running = 0;
}
}
}
if(shmdt(shared_memory) == -1) {//把共享内存从进程地址空间分离
fprintf(stderr,"shmdt failed\n");
exit(EXIT_FAILURE);
}
if (shmctl(shmid,IPC_RMID,0) == -1) {//删除共享内存
fprintf(stderr,"shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
msgflg);
msgtype,int msgflg);
msgid_ds结构体至少包含以下成员
struct msgid_ds {
uid_t msg_perm.uid;
uid_t msg_perm.gid;
mode_t msg_perm.mode;
}
第一个参数msgid是msgget返回的共享内存标识符
第二个参数command是要采取的动作,它可以取3个值
IPC_STAT 把shmid_ds结构体中的数据设置为共享内存的当前关联
IPC_SET 如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID 删除共享内存段
成功时返回0,失败时返回-1
下面是消息队列的例子:
(1)下面是接收者程序msg1.c的代码:
#include
<stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/msg.h>
struct my_msg_st { //准备接收数据结构体
long int my_msg_type;
char some_text[BUFSIZ];
};
int main()
{
int running = 1;
int msgid;
struct my_msg_st
some_data;
long int msg_to_receive =
0;
msgid =
msgget((key_t)1234,0666 | IPC_CREAT);//创建消息队列
if(msgid == -1) {
fprintf(stderr,"msgget
failed with error: %d\n",errno);
exit(EXIT_FAILURE);
}
while(running) {
if(msgrcv(msgid,(void
*)&some_data,BUFSIZ,msg_to_receive,0) == -1)
{//从队列中获取消息
fprintf(stderr,"msgrcv
failed with error: %d\n",errno);
exit(EXIT_FAILURE);
}
printf("You
wrote: %s",some_data.some_text);//打印获取消息
if(strncmp(some_data.some_text,"end",3)
== 0) {
running
= 0;
}
}
if(msgctl(msgid,IPC_RMID,0) ==
-1) {
fprintf(stderr,"msgctl(IPC_RMID)
failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
(2)下面是发送者程序msg2.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st {
long int my_msg_type;
char some_text[MAX_TEXT];
};
int main()
{
int running = 1;
struct my_msg_st
some_data;
int msgid;
char buffer[BUFSIZ];
msgid =
msgget((key_t)1234,0666 | IPC_CREAT);//创建或访问消息队列
if(msgid == -1) {
fprintf(stderr,"msgget
failed with error: %d\n",errno);
exit(EXIT_FAILURE);
}
while(running) {
printf("Enter
some text: ");
fgets(buffer,BUFSIZ,stdin);
some_data.my_msg_type
= 1;
strcpy(some_data.some_text,buffer);
if(msgsnd(msgid,(void
*)&some_data,MAX_TEXT,0) == -1) {//把数据写入队列
fprintf(stderr,"msgsnd
failed\n");
exit(EXIT_FAILURE);
}
if
(strncmp(buffer,"end",3) == 0) {
running
= 0;
}
}
exit(EXIT_SUCCESS);
}
运行这两个程序如下所示:
root@www:/opt/chengxu# gcc
msg1.c -o msg1
root@www:/opt/chengxu#
gcc msg2.c -o msg2
root@www:/opt/chengxu#
./msg2
Enter some text: hello
Enter some text: How are you today?
Enter some text: end
root@www:/opt/chengxu#
./msg1
You wrote: hello
You wrote: How are you today?
You wrote: end
root@www:/opt/chengxu#
....
信号量 P V测试详解的更多相关文章
- web 压力测试工具ab压力测试详解
Web性能压力测试工具之ApacheBench(ab)详解 原文:http://www.ha97.com/4617.html PS:网站性能压力测试是性能调优过程中必不可少的一环.只有让服务器处在高压 ...
- redis压力测试详解
redis做压测可以用自带的redis-benchmark工具,使用简单,效果也比较不错. linux下一般无需下载,windows下redis-benchmark压力测试工具下载地址:http:// ...
- 大数据笔记(十九)——数据采集引擎Sqoop和Flume安装测试详解
一.Sqoop数据采集引擎 采集关系型数据库中的数据 用在离线计算的应用中 强调:批量 (1)数据交换引擎: RDBMS <---> Sqoop <---> HDFS.HBas ...
- Ubuntu14.04搭建JSP与Servlet开发环境及其测试详解
一,搭建JDK开发环境 1,在Java官网下载Jdk软件包,我的系统是64位Ubuntu14.04,所以选择jdk-8u25-linux-x64.tar.gz. 2,解压Jdk软件包 tar xvzf ...
- 5G/NR OTA (Over The Air) 测试详解
原文链接:http://www.sharetechnote.com/html/5G/5G_OTA.html 1 什么是OTA (Over The Air) OTA代表Over The Air.为了使用 ...
- 【HTB系列】靶机Frolic的渗透测试详解
出品|MS08067实验室(www.ms08067.com) 本文作者:大方子(Ms08067实验室核心成员) Hack The Box是一个CTF挑战靶机平台,在线渗透测试平台.它能帮助你提升渗透测 ...
- 【HTB系列】靶机Access的渗透测试详解
出品|MS08067实验室(www.ms08067.com) 本文作者:大方子(Ms08067实验室核心成员) Hack The Box是一个CTF挑战靶机平台,在线渗透测试平台.它能帮助你提升渗透测 ...
- 【HTB系列】靶机Vault的渗透测试详解
出品|MS08067实验室(www.ms08067.com) 本文作者:大方子(Ms08067实验室核心成员) Kali: 10.10.14.213 靶机地址:10.10.10.109 先用nmap探 ...
- 【HTB系列】靶机Teacher的渗透测试详解
出品|MS08067实验室(www.ms08067.com) 本文作者:大方子(Ms08067实验室核心成员) Kali: 10.10.14.50 靶机地址:10.10.10.153 先用nmap 对 ...
随机推荐
- nova network-vif-plugged 事件分析1
在创建虚机过程中,nova-compute会调用wait_for_instance_event函数(nova/compute/manage.py)进行network-vif-plugged的事件等待, ...
- java—实现一个监听器HttpServletRequest的创建销毁、在线人数 (56)
在JavaWeb中的监听器分类 在Javaweb中存在三个被监听对象: HttpServletRequest HttpSessoin ServletContext 监听者 被监听者 监听到事件对象 H ...
- Java多线程原理及Thread类的使用
一.进程与线程的区别 1.进程是应用程序在内存总分配的空间.(正在运行中的程序) 2.线程是进程中负责程序执行的执行单元.执行路径. 3.一个进程中至少有一个线程在负责进程的运行. 4.一个进程中有多 ...
- Python装饰器(函数)
闭包 1.作用域L_E_G_B(局部.内嵌.全局...): x=10#全局 def f(): a=5 #嵌套作用域 def inner(): count = 7 #局部变量 print a retur ...
- Lucene.Net+盘古分词器(详细介绍)
本章阅读概要1.Lucenne.Net简介2.介绍盘古分词器3.Lucene.Net实例分析4.结束语(Demo下载)Lucene.Net简介 Lucene.net是Lucene的.net移植版本,是 ...
- scrapy 中用selector来提取数据的用法
一. 基本概念 1. Selector是一个可独立使用的模块,我们可以用Selector类来构建一个选择器对象,然后调用它的相关方法如xpaht(), css()等来提取数据,如下 from sc ...
- 【转载】Java 9 新特性——模块化
来自 <http://www.jianshu.com/p/053a5ca89bbb#> 前言 年,我们将迎来 Java 语言的 22 岁生日,22岁,对于一个人而言,正是开始大展鸿图的年纪 ...
- jQuery css()与class()的用法
一.css()用法: 1.设置css // css(name, value) // 修改单个样式 // name:样式名 value:样式值 $("li") .css(&quo ...
- [转载+整理]Nginx Location匹配规则
目录 规则语法 location 分类 匹配顺序: 扩展 location / {}和 location =/ {}的区别 测试 规则语法 语法 匹配规则 空 普通匹配(遵循最大前缀匹配规则, 优先度 ...
- python之守护进程
主进程创建子进程,然后将该进程设置成守护自己的进程,守护进程就好比崇祯皇帝身边的老太监,崇祯皇帝已死老太监就跟着殉葬了. 关于守护进程需要强调两点: 其一:守护进程会在主进程代码执行结束后就终止 其二 ...