博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
转:Linux--进程间通信(信号量,共享内存)
阅读量:5278 次
发布时间:2019-06-14

本文共 6376 字,大约阅读时间需要 21 分钟。

源地址:

 

一. 信号量  

l信号量: 解决进程之间的同步与互斥的IPC机制

 

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

 

 eg. 通过对信号量PV操作,消除父子进程间的竞争条件,使得其调用顺序可控。
1 union semun {
2 int val; 3 struct semid_ds *buf; 4 unsigned short *array; 5 }; 6 7 // 将信号量sem_id设置为init_value 8 int init_sem(int sem_id,int init_value) {
9 union semun sem_union; 10 sem_union.val=init_value; 11 if (semctl(sem_id,0,SETVAL,sem_union)==-1) {
12 perror("Sem init"); 13 exit(1); 14 } 15 return 0; 16 } 17 // 删除sem_id信号量 18 int del_sem(int sem_id) {
19 union semun sem_union; 20 if (semctl(sem_id,0,IPC_RMID,sem_union)==-1) {
21 perror("Sem delete"); 22 exit(1); 23 } 24 return 0; 25 } 26 // 对sem_id执行p操作 27 int sem_p(int sem_id) {
28 struct sembuf sem_buf; 29 sem_buf.sem_num=0;//信号量编号 30 sem_buf.sem_op=-1;//P操作 31 sem_buf.sem_flg=SEM_UNDO;//系统退出前未释放信号量,系统自动释放 32 if (semop(sem_id,&sem_buf,1)==-1) {
33 perror("Sem P operation"); 34 exit(1); 35 } 36 return 0; 37 } 38 // 对sem_id执行V操作 39 int sem_v(int sem_id) {
40 struct sembuf sem_buf; 41 sem_buf.sem_num=0; 42 sem_buf.sem_op=1;//V操作 43 sem_buf.sem_flg=SEM_UNDO; 44 if (semop(sem_id,&sem_buf,1)==-1) {
45 perror("Sem V operation"); 46 exit(1); 47 } 48 return 0; 49 }
1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include "sem_com.c" 9 10 #define DELAY_TIME 3 11 12 int main() { 13 pid_t pid; 14 // int sem_id; 15 // key_t sem_key; 16 17 // sem_key=ftok(".",'a'); 18 // 以0666且create mode创建一个信号量,返回给sem_id 19 // sem_id=semget(sem_key,1,0666|IPC_CREAT); 20 // 将sem_id设为1 21 // init_sem(sem_id,1); 22 23 if ((pid=fork())<0) { 24 perror("Fork error!\n"); 25 exit(1); 26 } else if (pid==0) { 27 // sem_p(sem_id); // P操作 28 printf("Child running...\n"); 29 sleep(DELAY_TIME); 30 printf("Child %d,returned value:%d.\n",getpid(),pid); 31 // sem_v(sem_id); // V操作 32 exit(0); 33 } else { 34 // sem_p(sem_id); // P操作 35 printf("Parent running!\n"); 36 sleep(DELAY_TIME); 37 printf("Parent %d,returned value:%d.\n",getpid(),pid); 38 // sem_v(sem_id); // V操作 39 // waitpid(pid,0,0); 40 // del_sem(sem_id); 41 exit(0); 42 } 43 44 }
在以上程序注释//未去掉时,即没用信号量机制时,其结果为:

显然,此处存在竞争条件。

 

在以上程序注释//去掉后,即使用信号量机制,其结果为:

由于父子进程采用同一信号量且均执行各自PV操作,故必先等一个进程的V操作后,另一个进程才能工作。

 

 

二. 共享内存

 

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

 

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

 

eg. 下面这个例子完成:父进程从stdin读取字符串并保存到共享内存中,子进程从共享内存中读出数据并输出到stdout

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 8 #define BUFFER_SIZE 2048 9 10 int main() { 11 pid_t pid; 12 int shmid; 13 char *shm_addr; 14 char flag[]="Parent"; 15 char buff[BUFFER_SIZE]; 16 // 创建当前进程的私有共享内存 17 if ((shmid=shmget(IPC_PRIVATE,BUFFER_SIZE,0666))<0) { 18 perror("shmget"); 19 exit(1); 20 } else 21 printf("Create shared memory: %d.\n",shmid); 22 23 // ipcs 命令往标准输出写入一些关于活动进程间通信设施的信息 24 // -m 表示共享内存 25 printf("Created shared memory status:\n"); 26 system("ipcs -m"); 27 28 if((pid=fork())<0) { 29 perror("fork"); 30 exit(1); 31 }else if (pid==0) { 32 // 自动分配共享内存映射地址,为可读可写,映射地址返回给shm_addr 33 if ((shm_addr=shmat(shmid,0,0))==(void*)-1) { 34 perror("Child:shmat"); 35 exit(1); 36 }else 37 printf("Child: Attach shared-memory: %p.\n",shm_addr); 38 39 printf("Child Attach shared memory status:\n"); 40 system("ipcs -m"); 41 // 比较shm_addr,flag的长度为strlen(flag)的字符 42 // 当其内容相同时,返回0 43 // 否则返回(str1[n]-str2[n]) 44 while (strncmp(shm_addr,flag,strlen(flag))) { 45 printf("Child: Waiting for data...\n"); 46 sleep(10); 47 } 48 49 strcpy(buff,shm_addr+strlen(flag)); 50 printf("Child: Shared-memory: %s\n",buff); 51 // 删除子进程的共享内存映射地址 52 if (shmdt(shm_addr)<0) { 53 perror("Child:shmdt"); 54 exit(1); 55 }else 56 printf("Child: Deattach shared-memory.\n"); 57 58 printf("Child Deattach shared memory status:\n"); 59 system("ipcs -m"); 60 61 }else{ 62 sleep(1); 63 // 自动分配共享内存映射地址,为可读可写,映射地址返回给shm_addr 64 if ((shm_addr=shmat(shmid,0,0))==(void*)-1) { 65 perror("Parent:shmat"); 66 exit(1); 67 }else 68 printf("Parent: Attach shared-memory: %p.\n",shm_addr); 69 70 printf("Parent Attach shared memory status:\n"); 71 system("ipcs -m"); 72 // shm_addr为flag+stdin 73 sleep(1); 74 printf("\nInput string:\n"); 75 fgets(buff,BUFFER_SIZE-strlen(flag),stdin); 76 strncpy(shm_addr+strlen(flag),buff,strlen(buff)); 77 strncpy(shm_addr,flag,strlen(flag)); 78 // 删除父进程的共享内存映射地址 79 if (shmdt(shm_addr)<0) { 80 perror("Parent:shmdt"); 81 exit(1); 82 }else 83 printf("Parent: Deattach shared-memory.\n"); 84 85 printf("Parent Deattach shared memory status:\n"); 86 system("ipcs -m"); 87 // 保证父进程在删除共享内存前,子进程能读到共享内存的内容 88 waitpid(pid,NULL,0); 89 // 删除共享内存 90 if (shmctl(shmid,IPC_RMID,NULL)==-1) { 91 perror("shmct:IPC_RMID"); 92 exit(1); 93 }else 94 printf("Delete shared-memory.\n"); 95 96 printf("Child Delete shared memory status:\n"); 97 system("ipcs -m"); 98 99 printf("Finished!\n"); 100 } 101 102 exit(0); 103 }

 
分类:

转载于:https://www.cnblogs.com/xuhj001/p/3372894.html

你可能感兴趣的文章
C#关于MSMQ通过HTTP远程发送专有队列消息的问题
查看>>
关于AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案
查看>>
如何在IntelliJ IDEA中快速配置Tomcat
查看>>
在openwrt上编译一个最简单的ipk包
查看>>
Android中如何查看内存(下)
查看>>
rc4加密
查看>>
[渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序实现继承
查看>>
操作系统核心原理-3.进程原理(上):进程概要
查看>>
函数的基础构造
查看>>
【JBPM4】任务节点-任务分配candidate-groups
查看>>
QT生成GUID
查看>>
Vue服务端渲染 VS Vue浏览器端渲染)
查看>>
初识XML Database
查看>>
一种解决新版本API完全兼容老版本API的方法
查看>>
《数据挖掘导论》 - 读书笔记(1) - 概况 | 目录 [2016-8-8]
查看>>
Kattis - String Matching(kmp)
查看>>
Android中活动的最佳实践(如何很快的看懂别人的代码activity)
查看>>
this指针
查看>>
【原】PHPExcel相关
查看>>
Dede没见过的漏洞
查看>>