信号量 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 对 ...
随机推荐
- ElasticSearch安装拼音插件 elasticsearch-analysis-pinyin
elasticsearch-analysis-pinyin 是 ElasticSearch的拼音插件.强大的功能支持拼音等的搜索 1.下载源代码 源码地址https://github.com/medc ...
- 搭建一个ES6开发环境
一.首先先建立一个项目工程目录,并在目录下建立两个文件夹:src和dist src:书写ES6代码的文件夹,写的js程序都放在这里. dist:利用Babel编译成的ES5代码的文件夹,在HTML页面 ...
- 解决 MySQL 1045错误的三种方法 (转)
连接MySQL数据库时难免会遇到1045错误,主要是因为用户输入的用户名或密码错误被拒绝访问,如果不想重装,需要找回密码或者重置密码. 问题描述: 1045-Access denied for use ...
- SpringMvc HttpMessageConverter之@ResponseBody
我们先看HttpMessageConverter的示意图,从图片可以看出它是多么的重要.在一条必经之路截道了的感觉. 先上我的测试例子: jsp页面: <%@ page language=&qu ...
- zookeeper 高可用集群搭建
前言 记录Zookeeper集群搭建的过程! 什么是 Zookeeper ? ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hado ...
- 前后端分离——token超时刷新策略
前言 记录一下前后端分离下————token超时刷新策略! 需求场景 昨天发了一篇记录 前后端分离应用——用户信息传递 中介绍了token认证机制,跟几位群友讨论了下,有些同学有这么一个疑惑:toke ...
- Tomcat启动和请求处理解析
tomcat是我们经常使用的组件,但是内部是如何运行的呢,我们去一探究竟. 1.tomcat架构 tomcat的整体架构图如下: Tomcat中只有一个Server,一个Server可以有多个Serv ...
- KMP算法再解 (看毛片算法真是人如其名,哦不,法如其名。)
KMP算法主要解决字符串匹配问题,其中失配数组next很关键: 看毛片算法真是人如其名,哦不,法如其名. 看了这篇博客,转载过来看一波: 原博客地址:https://blog.csdn.net/sta ...
- 架构师养成记--22.客户端与服务器端保持连接的解决方案,netty的ReadTimeoutHandler
概述 保持客户端与服务器端连接的方案常用的有3种 1.长连接,也就是客户端与服务器端一直保持连接,适用于客户端比较少的情况. 2.定时段连接,比如在某一天的凌晨建立连接,适用于对实时性要求不高的情况. ...
- Java多线程——同步容器类
1.同步容器类 同步容器类包括Vector和Hashtable,是早期JDK的一部分,这些类实现的方法是:将它们的状态封装起来,并对每个共有的方法进行同步,使得每个线程只有一个线程能访问它们. 1.1 ...