信号量可以说是进程间通信的基本方式,常用来保护某个变量或者代码段在多个进程间的访问,以保证同步。信号量和其他方式相比稍微有些麻烦,这里咱们把对信号量操作的方法进行简单封装。
信号量的使用有几个基本的函数:
int semget(key_t key, int nsems, int semflg);//创建或者获取一个信号量
int semctl(int semid, int semnum, int cmd, ...);//控制操作信号量
int semop(int semid, struct sembuf *sops, size_t nsops);//信号量的PV操作
sem.h
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
union semun{
int val;
struct semid_ds *buff;
unsigned short *array;
};
int init_sem(int,int);
int del_sem(int);
int sem_p(int);
int sem_v(int);
sem.c
#include<stdio.h>
#include "sem.h"
int init_sem(int sem_id,int sem_value){
union semun smun;
smun.val=sem_value;
if(semctl(sem_id,0,SETVAL,smun)==-1){//初始化信号量
printf("init sem failed!\n");
return -1;
}
return 0;
}
int del_sem(int sem_id){
union semun smun;
if(semctl(sem_id,0,IPC_RMID,smun)==-1){
printf("error occured in del!\n");
return -1;
}
return 0;
}
int sem_p(int sem_id){
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){
perror("p opreation!\n");
return -1;
}
return 0;
}
int sem_v(int sem_id){
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){
perror("v opreation!\n");
return -1;
}
return 0;
}
一下是一个在父子进程间用信号量的例子:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<wait.h>
#include "sem.h"
void main(){
pid_t pid;
int sem_id;
static int num=10;
sem_id=semget((key_t)1234,1,0666|IPC_CREAT);
if(sem_id==-1){
printf("create sem failed!\n");
exit(0);
}
if(init_sem(sem_id,1)==-1){
printf("init sem failed!\n");
exit(0);
}
pid=fork();
if(pid<0){
perror("error in create fork!\n");
exit(0);
}
if(pid==0){
printf("i am child, i am decreasing........\n");
sem_p(sem_id);
while(1){
if(num==0)break;
printf("child working num = %d \n",num);
num--;
sleep(1);
}
sem_v(sem_id);
wait(NULL);
}
if(pid>0){
sleep(2);
printf("i am father,i am increasing.......\n");
sem_p(sem_id);
printf("num = %d\n",num);
while(1){
if(num==1)break;
printf("father working num = %d\n",num--);
sleep(1);
}
sem_v(sem_id);
del_sem(sem_id);
exit(0);
}
}
运行结果:(为加信号量时,看起来很乱!)
添加信号量后:
下面在给出一个不同进程间使用信号量的案例:
sem_p.c//生产者
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include "sem.h"
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/stat.h>
#include<string.h>
#include<sys/ipc.h>
#define SIZE 1024
void main(){
char buff[SIZE];
int *shmaddr;
int shm_id;
int sem_id;
int shm_num=0;
if((shm_id=shmget((key_t)1024,1024,S_IRUSR|S_IWUSR|IPC_CREAT))==-1){
printf("create shmemory operation!\n");
exit(0);
}
shmaddr=shmat(shm_id,0,0);
if(shmaddr ==(void *)-1){
printf("shmaddr error!\n");
exit(0);
}
if((sem_id=semget((key_t)1002,1,0666|IPC_CREAT))==-1){
perror("create sem operation!");
exit(0);
}
if(init_sem(sem_id,1)==-1){
perror("init sem operation !\n");
exit(0);
}
memset(shmaddr,'\0',1024);
*shmaddr=shm_num;
printf("now has %d production ,i am begining.....\n",*shmaddr);
sem_p(sem_id);
while(1){
if((*shmaddr)==8)break;
(*shmaddr)+=1;
printf("produce a production; num = %d\n",*shmaddr);
sleep(1);
}
sem_v(sem_id);
exit(0);
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include "sem.h"
#define SIZE 1024
void main(){
int shm_id;
int sem_id;
int *shmaddr;
if((shm_id=shmget((key_t)1024,1024,0666|IPC_CREAT))==-1){
perror("create shm error!");
exit(0);
}
shmaddr=shmat(shm_id,0,0);
if((sem_id=semget((key_t)1002,1,0666|IPC_CREAT))==-1){
perror("create shmemory operation!");
exit(0);
}
printf("i am customer,i am waiting producer.......\n");
sleep(1);
sem_p(sem_id);
while(1){
if((*shmaddr)==0)break;
if((*shmaddr)==8)
printf("producer has stoped,i begin consuming.....\n");
printf("i custome a production! num = %d\n",*shmaddr);
(*shmaddr)-=1;
sleep(1);
}
sem_v(sem_id);
exit(0);
}
运行结果:
两个进程同时运行,在producer生产到8个之前,consumer一直等待中。。。。
之后生产者结束生产,消费者获得锁从而开始消费。。。。
2.共享存储区
共享内存顾名思义就是把一块内存区域作为共享的区域而允许多个进程共享访问。这样的一块区域可以被所共享的任意一个进程读写,任意一个进程的更改均会影响存储区的内容。
其有点如下:
(1)为了在多个进程间交换信息,内核专门留出了一块内存区
(2)由需要访问的进程将其映射到自己私有地址空间
(3)进程直接读写这一内存区而不需要进行数据的拷贝,提高了效率
使用共享内存有点类似于信号量,其基本方法介绍如下:
int shmget(key_t key, size_t size, int shmflg); //获取一块和key值相关的共享内存标识符shmid
void *shmat(int shmid, const void *shmaddr, int shmflg);//获取一个指针,指向共享内存区域
int shmdt(const void *shmaddr);//把共享内存从当前进程分离
int shmctl(int shmid, int cmd, struct shmid_ds *buf);//控制共享内存
实例代码:
write.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define SIZE 1024
#define PRTE S_IRUSR|S_IWUSR|IPC_CREAT
void main(){
char buff[SIZE];
char *shmaddr;
int shmid;
if((shmid=shmget((key_t)777,1024,PRTE))==-1){
printf("create memory failed!\n");
exit(0);
}
shmaddr = shmat(shmid,0,0);
memset(shmaddr,'\0',1024);
if(shmaddr==(void *)-1){
printf("shmat error!\n");
}
printf("i am sending message.........\n");
while(1){
printf("write:\n");
scanf("%s",buff);
strncpy(shmaddr,buff,strlen(buff));
sleep(1);
if(strncmp(buff,"quit",4)==0)break;
}
exit(0);
}
read.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/shm.h>
#include<wait.h>
#define SIZE 1024
#define PITE S_IRUSR|S_IWUSR|IPC_CREAT
void main(){
char buff[SIZE];
int shmid;
char *shmaddr;
if((shmid=shmget((key_t)777,1024,PITE))==-1){
printf("create memory failed!\n");
exit(0);
}
shmaddr=shmat(shmid,0,0);
printf("i am waiting for receiving message........\n");
while(1){
sleep(1);
while(strlen(shmaddr)==0)sleep(1);
strcpy(buff,shmaddr);
printf("read: %s \n",buff);
memset(shmaddr,'\0',SIZE);
if(strncmp(buff,"quit",4)==0)break;
}
exit(EXIT_SUCCESS);
}
这里共享存储区实现了共享操作,但是并没有实现同步,若好几个进程同时访问问题就可实现,不过这点我们可以结合上边的信号量来解决,大家可以试试;
以上所写所有错误还望大家积极批评指正!