共享内存与信号量
一. 共享内存
最为高效的进程间通信方式
进程直接读写内存,不需要任何数据的拷贝
•为了在多个进程间交换信息,内核专门留出了一块内存区
•由需要访问的进程将其映射到自己私有地址空间
•进程直接读写这一内存区而不需要进行数据的拷贝,提高了效率
多个进程共享一段内存,需要依靠某种同步机制,如互斥锁和信号量等

l共享内存编程步骤:
1. 创建共享内存
•函数shmget()
•从内存中获得一段共享内存区域
2. 映射共享内存
•把这段创建的共享内存映射到具体的进程空间中
•函数shmat()
3. 使用这段共享内存
•可以使用不带缓冲的I/O读写命令对其进行操作
4. 撤销映射操作: 函数shmdt()
5. 删除共享内存: 函数shctl()





程序代码如下:
/* * 共享内存测试程序(创建,写入数据) shamA.c * Created on: Oct 11, 2016 * Author: zhangming */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/shm.h> #include <sys/ipc.h> #include <signal.h> char *ptrStart; int shmid; void deal(int s){ if(s == SIGINT){ //4.卸载共享内存shmdt shmdt(ptrStart); //5.删除共享内存shctl shmctl(shmid,IPC_RMID,NULL); exit(0); } } main(){ signal(SIGINT,deal); //1.创建共享内存shmget key_t key = ftok(".",255); if(key==-1) { printf("ftok error:%m\n"),exit(-1); } shmid = shmget(key,4,IPC_CREAT|IPC_EXCL|0666); if(shmid==-1) { printf("get error:%m\n"),exit(-1); } //2.挂载共享内存shmat ptrStart = shmat(shmid,0,0); if(ptrStart == NULL) { printf("at error:%m\n"),exit(-1); } //3.访问共享内存 int i = 0; while(1){ *ptrStart = 'a'+i; if(*ptrStart == 'z'){ break; } sleep(1); i++; } }
/* * 共享内存测试程序(访问,读出数据) shamB.c * Created on: Oct 11, 2016 * Author: zhangming */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/shm.h> #include <sys/ipc.h> char *ptrStart; int shmid; void deal(int s){ if(s==SIGINT){ //4.卸载共享内存shmdt shmdt(ptrStart); exit(0); } } main(){ signal(SIGINT,deal); //1.创建共享内存shmget key_t key=ftok(".",255); if(key==-1) { printf("ftok error:%m\n"),exit(-1); } shmid=shmget(key,4,0); if(shmid==-1) { printf("get error:%m\n"),exit(-1); } //2.挂载共享内存shmat ptrStart = shmat(shmid,0,0); if(ptrStart == NULL){ printf("at error:%m\n"),exit(-1); } //3.访问共享内存 while(1){ sleep(1); fprintf(stdout,"%c ",*ptrStart); fflush(stdout); if(*ptrStart == 'z'){ //只读取一次 printf("\n"); break; } } }
运行结果截图:


二.信号量
信号量: 解决进程之间的同步与互斥的IPC机制
多个进程同时运行,之间存在关联
•同步关系
•互斥关系
互斥与同步关系存在的根源在于临界资源
•临界资源是在同一个时刻只允许有限个(通常只有一个)进程可以访问(读)或修改(写)的资源
–硬件资源(处理器、内存、存储器以及其他外围设备等)
–软件资源(共享代码段,共享结构和变量等)
•临界区,临界区本身也会成为临界资源
一个称为信号量的变量
•信号量对应于某一种资源,取一个非负的整型值
•信号量值指的是当前可用的该资源的数量,若它等于0则意味着目前没有可用的资源
在该信号量下等待资源的进程等待队列
对信号量进行的两个原子操作(PV操作)
•P操作
•V操作
最简单的信号量是只能取0 和1 两种值,叫做二维信号量
编程步骤:
创建信号量或获得在系统已存在的信号量
•调用semget()函数
•不同进程使用同一个信号量键值来获得同一个信号量
初始化信号量
•使用semctl()函数的SETVAL操作
•当使用二维信号量时,通常将信号量初始化为1
进行信号量的PV操作
•调用semop()函数
•实现进程之间的同步和互斥的核心部分
如果不需要信号量,则从系统中删除它
•使用semclt()函数的IPC_RMID操作
•在程序中不应该出现对已被删除的信号量的操作




通过对信号量PV操作,消除父子进程间的竞争条件,使得其调用顺序可控,代码如下:
/* * sem_util.h * Created on: Oct 11, 2016 * Author: zhangming */ #ifndef SEM_UTIL_H_ #define SEM_UTIL_H_ #endif /* SEM_UTIL_H_ */ union semun{ int val; struct semid_ds *buf; unsigned short *array; }; void init_sem(int,int); //将信号量sem_id设置为init_value void del_sem(int); //删除sem_id信号量 void sem_p(int); //对sem_id执行p操作 void sem_v(int); //对sem_id执行V操作
/* * 信号量初始化,PV操作封装 * sem_util.c * Created on: Oct 11, 2016 * Author: zhangming */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <sys/sem.h> #include <sys/ipc.h> #include "sem_util.h" // 将信号量sem_id设置为init_value void init_sem(int sem_id,int init_value) { union semun sem_union; sem_union.val = init_value; if(semctl(sem_id,0,SETVAL,sem_union) == -1){ /** *perror(s) 用来将上一个函数发生错误的原因输出到标准设备(stderr) *参数 s 所指的字符串会先打印出,后面再加上错误原因字符串 */ perror("Sem init"); exit(-1); } } // 删除sem_id信号量 void del_sem(int sem_id) { union semun sem_union; if(semctl(sem_id,0,IPC_RMID,sem_union) == -1){ perror("Sem delete"); exit(-1); } } // 对sem_id执行p操作 void sem_p(int sem_id) { /** * 函数原型 int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops) * 第三个参数num_sem_ops为第二个参数的个数(即数组元素的个数) */ struct sembuf sem_buf; sem_buf.sem_num=0; //信号量编号 sem_buf.sem_op=-1; //P操作 sem_buf.sem_flg=SEM_UNDO; //系统退出前未释放信号量,系统自动释放 if(semop(sem_id,&sem_buf,1) == -1){ perror("Sem P operation"); exit(-1); } } // 对sem_id执行V操作 void sem_v(int sem_id) { struct sembuf sem_buf; sem_buf.sem_num=0; //信号量编号 sem_buf.sem_op=1; //V操作 sem_buf.sem_flg=SEM_UNDO; //系统退出前未释放信号量,系统自动释放 if (semop(sem_id,&sem_buf,1)==-1) { perror("Sem V operation"); exit(-1); } }
/* * sem_test.c * Created on: Oct 11, 2016 * Author: zhangming */ #include "sem_util.c" #define DELAY_TIME 15 int main() { pid_t pid; int sem_id; key_t sem_key; sem_key=ftok(".",'a'); //以0666且create mode创建一个信号量,返回给sem_id sem_id=semget(sem_key,1,0666|IPC_CREAT); //将sem_id设为1 init_sem(sem_id,1); if ((pid=fork())<0) { perror("Fork error!\n"); exit(-1); } else if (pid==0) { sem_p(sem_id); //P操作 printf("Child running...\n"); sleep(DELAY_TIME); printf("Child pid:%d,returned pid:%d.\n",getpid(),pid); sem_v(sem_id); //V操作 exit(0); } else { sem_p(sem_id); //P操作 printf("Parent running!\n"); sleep(DELAY_TIME); printf("Parent pid:%d,returned pid:%d.\n",getpid(),pid); sem_v(sem_id); //V操作 waitpid(pid,0,0); del_sem(sem_id); exit(0); } }
运行结果截图:


浙公网安备 33010602011771号