Linux下进程间通信 之 共享内存同步方法(互斥锁,信号量 和 信号)
2022/7/28 5:22:46
本文主要是介绍Linux下进程间通信 之 共享内存同步方法(互斥锁,信号量 和 信号),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情。由于多
个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等 。
信号量(semaphore),它是不同进程间或一个给定进程内部不同线程间同步的机制。信号量包
括:posix有名信号量、 posix基于内存的信号量(无名信号量) 和 System V信号量(IPC对象)。
方法一:利用POSIX有名信号量实现共享内存的同步
有名信号量既可用于线程间的同步,又可用于进程间的同步。
两个进程,对同一个共享内存读写,可利用有名信号量来进行同步。一个进程写,另一个进
程读,利用两个有名信号量“semr”, “semw”。semr信号量控制能否读,初始化为0。 semw信号量
控制能否写,初始为1。
以下这两段代码中,读程序刚开始会一直阻塞到写程序往内存中写入数据后,给读信号量
“semr”加1后才会继续运行。而写程序,由于刚开始的写信号量初始化为1,此时写信号量“semw”
在执行“sem_wait”后会减1。因此会直接执行写操作后,给读信号量“semr”加1,出发读程序继续往
下进行。
读共享内存的程序示例代码如下:
// 创建一个名为"mysem_r"的读信号量(有名信号量) semr = sem_open("mysem_r", O_CREAT | O_RDWR , 0666, 0); if (semr == SEM_FAILED) { printf("errno=%d\n", errno); return -1; } // 创建一个名为"mysem_w"的写信号量(有名信号量) semw = sem_open("mysem_w", O_CREAT | O_RDWR, 0666, 1); if (semw == SEM_FAILED) { printf("errno=%d\n", errno); return -1; } // 创建共享内存区域 if ((shmid = shmget(key, MAXSIZE, 0666 | IPC_CREAT)) == -1) { perror("semget"); exit(-1); } // 获取共享内存区域地址 if ((shmadd = (char *)shmat(shmid, NULL, 0)) == (char *)(-1)) { perror("shmat"); exit(-1); } // 读取内存区数据 while (1) { sem_wait(semr);// 阻塞等待读信号量的值为1后,减1返回 printf("%s\n", shmadd); sem_post(semw); // 读完之后设置写信号量,加1返回 }
写共享内存的程序示例代码如下:
。。。。。。 // 以上同读的程序一致 while (1) { sem_wait(semw); printf(">"); fgets(shmadd, MAXSIZE, stdin); sem_post(semr); }
方法二:利用POSIX无名信号量实现共享内存的同步
POSIX无名信号量是基于内存的信号量,可以用于线程间同步也可以用于进程间同步。若实
现进程间同步,需要在共享内存中来创建无名信号量。因此,共享内存需要定义以下的结构体。
typedef struct { sem_t semr; sem_t semw; char buf[MAXSIZE]; }SHM;
即在互相访问共享内存中数据的时候,查询信号量的状态来进行同步。这种方法并不太方便
,使用方式与下面介绍的利用互斥锁的机制差不多。
读、写程序流程如下图所示:
方法三:利用System V的信号灯实现共享内存的同步
System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号
灯。而Posix信号灯指的是单个计数信号灯。
System V 信号灯由内核维护,主要函数semget,semop,semctl 。
一个进程写,另一个进程读,信号灯集中有两个信号灯,下标0代表能否读,初始化为0。 下
标1代表能否写,初始为1。
程序流程如下:
方法四:利用信号实现共享内存的同步
该方法的局限在于必须获取对方的进程号,虽然可以通过其他方式获取,但相比有名信号量,还是不那么方便。 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。利用信号也可以实现共享内存的同步。
思路:
reader和writer通过信号通信必须获取对方的进程号,可利用共享内存保存双方的进程号。
reader和writer运行的顺序不确定,可约定先运行的进程创建共享内存并初始化。
利用pause, kill, signal等函数可以实现该程序(流程和前边类似)。
方法五:利用互斥锁实现共享内存的同步
以下为转载:基于互斥锁同步机制的Linux共享内存简单实例_szkbsgy的专栏-CSDN博客_linux共享内存同步机制
这种方法与方法二差不多,需要在访问共享内存中数据的时候,查询互斥锁的状态来进行同步。
sm_common.h文件
#ifndef __SM_COMMON_H__ #define __SM_COMMON_H__ #include <pthread.h> #define SM_BUF_SIZE 1024 #define SM_ID 0x1122 // 定义的共享内存区数据结构 // 内部携带一把互斥锁 struct sm_msg { int flag; pthread_mutex_t sm_mutex; // 定义互斥锁 char buf[SM_BUF_SIZE]; }; #endif
sm_server.c文件
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/shm.h> #include <pthread.h></span> #include "sm_common.h" int main(void) { int shm_id = -1; int ret = -1; int key = -1; int running = 1; struct sm_msg *msg = NULL; void *shared_memory = NULL; // 创建互斥锁 pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); // 创建一片共享内存,使用同一个key: SM_ID //key = ftok( "./sm_common.h", 1 ); //printf("key: %d\n", key); shm_id = shmget((key_t)SM_ID, sizeof(struct sm_msg), 0666|IPC_CREAT); if(shm_id < 0) { perror("fail to shmget"); exit(1); } #if 1 shared_memory = shmat(shm_id, NULL, 0); if (shared_memory == NULL) { perror("Failed to shmat"); exit(1); } msg = (struct sm_msg *)shared_memory; msg->flag = 0;// 使用该标记位同步读写 // 创建并初始化互斥锁 pthread_mutex_init(&msg->sm_mutex, &attr); while (running) { pthread_mutex_lock(&msg->sm_mutex);// 阻塞,等待锁释放 if (msg->flag == 1) { printf("Read message: %s\n", msg->buf); msg->flag = 0; pthread_mutex_unlock(&msg->sm_mutex); if (strncmp(msg->buf, "exit", 4) == 0) { running = 0; } } else { printf("No available data to read, sleep...\n"); pthread_mutex_unlock(&msg->sm_mutex); sleep(2); } ret = shmdt(shared_memory); if (ret < 0) { perror("Failed to shmdt"); exit(1); } if(shmctl(shm_id, IPC_RMID, 0) < 0) { perror("failed to shmctl"); exit(1); } #endif return 0; }
sm_client.c文件
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/shm.h> #include <pthread.h> #include "sm_common.h" int main(void) { int shm_id = -1; int ret = -1; int key = -1; int running = 1; struct sm_msg *msg = NULL; void *shared_memory = NULL; // 创建一片共享内存,使用同一个key: SM_ID // key = ftok( "./sm_common.h", 1 ); //printf("key: %d\n", key); shm_id = shmget((key_t)SM_ID, sizeof(struct sm_msg), 0666|IPC_CREAT); if(shm_id < 0) { perror("fail to shmget"); exit(1); } shared_memory = shmat(shm_id, NULL, 0); if (shared_memory == NULL) { perror("Failed to shmat"); exit(1); } msg = (struct sm_msg *)shared_memory; char buf[32]; while (running) { printf("wait lock\n"); pthread_mutex_lock(&msg->sm_mutex);// 阻塞等待内部的互斥锁 printf("get lock\n"); if(msg->flag == 1) { printf("Wait for other process's reading\n"); pthread_mutex_unlock(&msg->sm_mutex); sleep(2); } else { printf("Please input data\n"); fgets(buf, 32, stdin); printf("Write msg: %s\n", buf); strncpy(msg->buf, buf, sizeof(buf)); msg->flag = 1; if (strncmp(msg->buf, "exit", 4) == 0) { running = 0; } pthread_mutex_unlock(&msg->sm_mutex); } } ret = shmdt(shared_memory); if (ret < 0) { perror("Failed to shmdt"); exit(1); } #if 0 //Do this in server. if(shmctl(shm_id, IPC_RMID, 0) < 0) { perror("failed to shmctl"); exit(1); } #endif return 0; }
使用方法:
- 打开终端1,运行服务端:./sm_server
- 打开终端2,运行客户端:./sm_client
- 在终端2输入字符串,在终端1可看到相应输出
原文:https://blog.csdn.net/lpwsw/article/details/121945155
这篇关于Linux下进程间通信 之 共享内存同步方法(互斥锁,信号量 和 信号)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-18git仓库有更新,jenkins 自动触发拉代码怎么配置的?-icode9专业技术文章分享
- 2024-12-18Jenkins webhook 方式怎么配置指定的分支?-icode9专业技术文章分享
- 2024-12-13Linux C++项目实战入门教程
- 2024-12-13Linux C++编程项目实战入门教程
- 2024-12-11Linux部署Scrapy教程:新手入门指南
- 2024-12-11怎么将在本地创建的 Maven 仓库迁移到 Linux 服务器上?-icode9专业技术文章分享
- 2024-12-10Linux常用命令
- 2024-12-06谁看谁服! Linux 创始人对于进程和线程的理解是…
- 2024-12-04操作系统教程:新手入门及初级技巧详解
- 2024-12-04操作系统入门:新手必学指南