信号量:用于进程间同步控制
信号量就是具有原子性的计数器,相当于一把锁,进程要访问临资源时,必须向信号量获取锁,才能访问这个临界资源,同时其他进程因为无法“获得锁”而不能访问该临界资源,只有以获得锁的进程访问完了临界资源,将锁还给信号量后,其他进程才能“获得锁”去执行临界区代码。从而实现了进程间的同步控制。
对信号量只有两种操作:等待(P)、发送信号(V)
假设有一个信号量变量sv:
P(sv):如果sv的值大于0,就给它减一;如果它的值为0,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而被挂起,就给它加1
即:当临界区资源可用时,信号量变量sv的值时true,然后P(sv)操作将它减1使他变为false,表示临界区域正在被使用;当进程离开临界区时,使用V(sv)操作将它加1,使临界区变为可用。
这里有一些概念:
临界资源:不同进程可以看到的那份共同资源
临界区:多个进程访问临界资源的代码
原子性:一件事情只能有做了和没做两种状态
对成组的信号量值操作函数:#include<sys/sem.h>
①创建一个新信号量或取得一个已有信号量的键:
int semget(key_t key, int num_sems, int sem_flags); //返回值信号标识符sem_id。
key是整数值,不相关进程可以通过它访问同一个信号量
num_sems指定要创建/获取的信号量集中信号的个数,如果是获取已经存在的信号量,则将它设为0。
②改变信号量的值:P V 操作由此函数完成
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops); //sem_ops是一个指向结构数组的指针,结构类型 如下:
struct sembuf{
short sem_num; //信号量编号,除非是一组信号量,否则一般为0表示这是第一个也是唯一的一个信号量
short sem_op; //信号量在一次操作中需要改变的数值:p(-1) /v(+1)
short sem_flg; //使操作系统跟踪当前进程对这个信号量的修改情况,通常被设为SEM_UNDO
}
③控制信号量的信息:
int semctl(int sem_id,int sem_num,int command,…); //
command是将要采取的动作。如果有第四个参数,它就是一个union semun结构:
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
}
command常取的值为:
SETVAL:把信号量初始化为一个已知的值,由第四个参数中的val设置。
IPC_RMID:删除一个已经无需继续使用的信号量标识符。
共享内存:最快的IPC
共享内存允许两个或多个进程共享给定的存储区,这段存储区可以被两个或两个以上的进程映射至自身的地址空间中。一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过简单的内存读取读出,从而实现进程间的通讯。
共享内存是最快的IPC,因为进程是直接对内存进行读写的,不需要任何数据的拷贝(像管道实际上一次消息的传递需要两次拷贝:写入管道和从管道中读出)。
A、B两个进程通过内核对象将各自的虚拟指针ptr都指向物理空间中的共享内存进行操作。
共享内存并未提供同步机制,即在第一个进程介乎对共享内存的写操作之前,其他进程也可能会对共享内存进行操作。所以通常需要使用进程的同步机制来同步对共享内存的访问。如:信号量
共享内存有多种实现方式:mmap系统调用,POSIX共享内存,以及System V共享内存。这里只介绍System V共享内存API。
共享内存的使用:#include<sys/shm.h>
①创建共享内存:
int shmget(key_t key, size_t size, int shmflg); //成功返回共享内存的标识符;失败:返回-1
key:为共享内存段命名。
size:指定共享内存需要的内存容量,如果是获取已存在的共享内存,设为0.
shmflg:对共享内存的访问权限
②把共享内存连接到当前进程的地址空间:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
//成功返回指向共享内存第一个字节的指针,失败返回-1.
shm_id:共享内存标识符
shm_addr:共享内存连接到当前进程中的地址位置,通常为空,由系统选择
shmflg:标志位。SHM_RND(与shm_addr一起用,控制共享内存连接的地址)、SHM_RDONLY(使得连续的内存只读)
③把共享内存从当前进程分离:进程不能再使用这块共享内存
int shmdt(const void *ahmaddr); //成功返回0,失败返回-1
④控制共享内存:
int shmctl(int shm_id, int command, shruct shmid_ds *buf);
shm_id:共享内存标识符
command:要采取的动作:
IPC_STAT —》把shmid_ds中的数据设为共享内存的当前关联值
IPC_SET —》如果有权限,把共享内存的当前关联值设为shmid_ds中给出的值
IPC_RMID —》删除共享内存
buf:指向包含共享内存模式和访问权限的结构
struct shmid_ds
{
uid_t shm_perm.uid; //
uid_t shm_perm.gid; //
mode_t shm_perm.mode;
}
下面的程序实例时由信号进行同步控制的共享内存进行进程间通讯
shm_com.h
/*
* shm_com.h
*/
#include<sys/sem.h>
#define TEXT_SZ 2048
struct memory
{
char text[TEXT_SZ];
};
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
sever.c
/*
*向共享内存中写入数据
*使用信号量做同步控制
*/
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include"shm_com.h"
#define BUFFSIZE 1024
static int semid; //信号量标识符
//信号量的p操作
int p()
{
struct sembuf sem_p;
sem_p.sem_num = 0; //信号量在信号集中的编号
sem_p.sem_op = -1; //定义操作:-1
sem_p.sem_flg = SEM_UNDO;
//调用信号操作函数semop对信号值进行操作
if(semop(semid,&sem_p,1) == -1)
{
printf("P operation failed\n");
return 0;
}
return 1;
}
//信号量的v操作
int v()
{
struct sembuf sem_p;
sem_p.sem_num = 0; //信号在信号集中的编号
sem_p.sem_op = 1; //定义操作:1
//调用信号操作函数semop对信号值进行操作
if(semop(semid,&sem_p,1) == -1)
{
printf("V operation failed\n");
return 0;
}
return 1;
}
int main()
{
//创建信号量
semid = semget((key_t)1234,1,0666|IPC_CREAT);
if(semid == -1)
{
printf("creat shm faill\n");
exit(1);
}
//初始化信号量的值
union semun sem_u;
sem_u.val = 1; //设置变量值
semctl(semid,0,SETVAL,sem_u); //设置第0个信号
struct memory *shared_memory; //共享内存的地址,初始化为0
char buff[BUFFSIZE];
int shmid; //共享内存标识符
//创建共享内存
shmid = shmget((key_t)6789,sizeof(struct memory),0666|IPC_CREAT);
if(shmid == -1)
{
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
}
//把共享内存链接到当前进程的地址空间
shared_memory = (struct memory*)shmat(shmid,0,0);
if(shared_memory == (struct memory *)-1)
{
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
}
//对共享内存进行写操作
//先进行P操作,获取信号量
while(1)
{
if(!p())
{
exit(EXIT_FAILURE);
}
//向共享内存中写入数据
printf("Enter some text:");
fgets(buff,BUFFSIZE,stdin);
strncpy(shared_memory->text,buff,TEXT_SZ);
//写入完成,进行V操作,释放信号量
if(!v())
{
exit(EXIT_FAILURE);
}
sleep(3);
//输入了end,结束循环
if(strncmp(buff,"end",3) == 0)
{
break;
}
}
//把共享内存从进程中分离
if(shmdt(shared_memory) == -1)
{
fprintf(stderr,"shmdt failed\n");
exit(EXIT_FAILURE);
}
sleep(2);
exit(EXIT_SUCCESS);
}
client.c
/*
* 从共享内存中读取数据
* 使用信号量进行同步控制
*/
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include"shm_com.h"
static int semid;//信号量标识符
int p()
{
struct sembuf sem_p;
sem_p.sem_num = 0;
sem_p.sem_op = -1;
sem_p.sem_flg = SEM_UNDO;
if(semop(semid,&sem_p,1) == -1)
{
fprintf(stderr,"p operation failed\n");
return 0;
}
return 1;
}
int v()
{
struct sembuf sem_p;
sem_p.sem_num = 0;
sem_p.sem_op = 1;
if(semop(semid,&sem_p,1) == -1)
{
printf("v operation failed\n");
return 0;
}
return 1;
}
int main()
{
//创建信号量
semid = semget((key_t)1234,0,0666);
if(semid == -1)
{
printf("creat shm failed\n");
exit(1);
}
struct memory *shared_memory; //共享内存的地址,初始化为0
int shmid; //共享内存标识符
//创建共享内存
shmid = shmget((key_t)6789,0,0666);
if(shmid == -1)
{
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
}
//把共享内存链接到当前进程的地址空
shared_memory = (struct memory*)shmat(shmid,0,0);
if(shared_memory == (struct memory *)-1)
{
fprintf(stderr,"shmat failed\n");
exit(EXIT_FAILURE);
}
//读取共享内存中的数据
while(1)
{
if(!p())
{
exit(EXIT_FAILURE);
}
//从共享内存中读取数据
printf("you wrote:%s",shared_memory->text);
//读取完数据,v操作使得共享内存端可写
//这里是写一句,读一句
if(!v())
{
exit(EXIT_FAILURE);
}
sleep(6);
//输入了end,退出循环
if(strncmp(shared_memory->text,"end",3) == 0)
{
break;
}
}
//把共享内存从进程中分离
if(shmdt(shared_memory) == -1)
{
printf("shmdt is fail\n");
}
//删除共享内存 否则户一直留存在系统中
if(shmctl(shmid,IPC_RMID,NULL) == -1)
{
fprintf(stderr,"shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
//删除信号量
if(semctl(semid,0,IPC_RMID,0) == -1)
{
fprintf(stderr,"semctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}