ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux下进程间通信 之 共享内存同步方法(互斥锁,信号量 和 信号)

2022-07-27 15:03:05  阅读:188  来源: 互联网

标签:信号量 include 间通信 互斥 mutex sm msg 共享内存


同步(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. 打开终端1,运行服务端:./sm_server
  2. 打开终端2,运行客户端:./sm_client
  3. 在终端2输入字符串,在终端1可看到相应输出

原文:https://blog.csdn.net/lpwsw/article/details/121945155

标签:信号量,include,间通信,互斥,mutex,sm,msg,共享内存
来源: https://www.cnblogs.com/xiaohai123/p/16524862.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有