信号量 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 对 ...
随机推荐
- Python----一些面试题
1.写出以下结果 print(1<2 and 2==2) print(1<2 and 2==1) print(1>2 and 2==2) 结果: True False False 解 ...
- 抓包工具Fiddler使用教程
一.基本原理 Fiddler 是以代理web服务器的形式工作的,它使用代理地址:127.0.0.1,端口:8888 二.Fiddler抓取https设置 1.启动Fiddler,打开菜单栏中的 Too ...
- Java 文件本地上传、下载和预览的实现
以下方法为通用版本 实测图片和pdf 都没有问题 上传方法需要前端配合post请求 ,下载前端用a标签就可以,预览 前端使用ifrme标签 ,就可以实现基本功能... 1.文件本地上传 publi ...
- 算法导论-MIT笔记
第一部分 Analysis of Algorithms 算法分析是关于计算机程序性能(performance)和资源利用的理论研究 1 What's more important than perfo ...
- TSL协议升级导致的问题:caught when processing request: Received fatal alert: protocol_version
近日,公司升级TSL协议,禁用TSL1.0,导致原本好好的https接口,报以下错误: 2019-03-05 15:43:29 [org.apache.commons.httpclient.HttpM ...
- 机器学习笔记(四)--sklearn数据集
sklearn数据集 (一)机器学习的一般数据集会划分为两个部分 训练数据:用于训练,构建模型. 测试数据:在模型检验时使用,用于评估模型是否有效. 划分数据的API:sklearn.model_se ...
- 44.oracle表空间的使用
要给下属充分的发挥空间,要允许下属犯错,下属犯错自己能顶下来就顶着,不要盯得让下属觉得“这不是我的事,我只是个小打工的”,团建要放在首位.不可在下属面前“装B”,别人也不傻. Oracle数据库开创性 ...
- 1.需要对txt存放的测试数据做去重处理,代码如下
采用集合去重,在新文件里逐行写入,达成目的 old_file = "D:/testdata/memberId.txt" #old result_file = "D:/te ...
- Mac 10.12安装Go2Shell在资源管理器快速打开Shell并跳转到指定位置
下载: (链接: https://pan.baidu.com/s/1qXAUqxY 密码: bf7g)
- (转)Linux下同步工具inotify+rsync使用详解
原文:https://segmentfault.com/a/1190000002427568 1. rsync 1.1 什么是rsync rsync是一个远程数据同步工具,可通过LAN/WAN快速同步 ...