进程间通信(IPC)的方法:共享内存

      共享内存(shared memory)是可用IPC技术中最快的一种。一旦内存被映射到共享内存区域的进程的地址空间中,在进程之间传递数据时就不会发生内核(kernel)参与。然而,在共享内存区域存储和提取数据时,进程之间需要某些形式的同步(例如互斥锁、条件变量、读写锁、记录锁、信号量)。
      注:不允许共享对象文件名中任何位置出现"/"(不包括前导"/")
      关键步骤:
      (1).使用shm_open函数创建共享内存对象;
      (2).使用mmap函数将共享内存区域映射到RAM,使其在内存中拥有可读写的地址.

      共享内存总结:
      (1).异步;
      (2).物理内存数据大小限制;
      (3).多个进程可以同时访问但需要同步管理;
      (4).随机访问。

      注:以上内容主要来自网络整理。

      测试代码如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
#include <thread>

const char* SHARED_OBJ_NAME = "shm_test"; // disallows '/' from anywhere in the filename(not counting leading '/'): shm_test will be generated in /dev/shm/ directory

// shared data struct
typedef struct message {
    int pid;
    int counter;
} message;

bool write_message(int pid, int value)
{
    auto fd = shm_open(SHARED_OBJ_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); // create and open a new shared memory object, return a file descriptor
	if (fd == -1) {
		fprintf(stderr, "fail to shm_open: %s\n", strerror(errno));
		exit(1);
	}

	auto ret = ftruncate(fd, sizeof(message)); // truncate a file to a specified length
	if (ret == -1) {
		fprintf(stderr, "fail to ftruncate: %s\n", strerror(errno));
		exit(1);
	}

    message* msg_ptr = (message*)mmap(nullptr, sizeof(message), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // create a new mapping in the virtual address space of the calling process
	if (msg_ptr == MAP_FAILED) {
		fprintf(stderr, "fail to mmap: %s\n", strerror(errno));
		exit(1);
	}
    
    fprintf(stdout, "Process %d: Increase the counter\n", pid);
    msg_ptr->pid = pid;
    msg_ptr->counter = value;

    ret = munmap(msg_ptr, sizeof(message)); // delete the mappings for the specified address range
	if (ret == -1) {
		fprintf(stderr, "fail to munmap: %s\n", strerror(errno));
		exit(1);
	}

    // remember to close to not hit an error of opening too many files
    close(fd);
    return true;
}

bool read_message(int curr_pid, int& curr_value)
{
    int fd = shm_open(SHARED_OBJ_NAME, O_RDWR, S_IRUSR | S_IWUSR);
	if (fd == -1) {
		fprintf(stderr, "fail to shm_open: %s\n", strerror(errno));
		exit(1);
	}

    auto ret = ftruncate(fd, sizeof(message));
	if (ret == -1) {
		fprintf(stderr, "fail to ftruncate: %s\n", strerror(errno));
		exit(1);
	}

    message* msg_ptr = (message*)mmap(nullptr, sizeof(message), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (msg_ptr == MAP_FAILED) {
		fprintf(stderr, "fail to mmap: %s\n", strerror(errno));
		exit(1);
	}

    if (msg_ptr->pid == curr_pid) {
        fprintf(stderr, "Process %d: No new msg available\n", curr_pid);
		munmap(msg_ptr, sizeof(message));
		close(fd);
		shm_unlink(SHARED_OBJ_NAME);
        return false;
    } else {
        fprintf(stdout, "Process %d: Receive %d from PID %d\n", curr_pid, msg_ptr->counter, msg_ptr->pid);
        curr_value = msg_ptr->counter;
        munmap(msg_ptr, sizeof(message));
    }

    close(fd);
	shm_unlink(SHARED_OBJ_NAME); // remove an object previously created by shm_open
    return true;
}

int main()
{
	// reference: https://biendltb.github.io/tech/inter-process-communication-ipc-in-cpp/
    write_message(-1, 0); // Init the initial value

    // create a child process by calling folk, 
    pid_t pid = fork();
    if (pid < 0) {
        fprintf(stderr, "fail to fork\n");
        return -1;
    }

    if (pid != 0) { // parent process
        for (int i = 0; i < 5; i++) {
            int value;
            // only write message if reading sucessfully
            if (read_message(pid, value))
                write_message(pid, ++value);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }

		int status;
        auto pid2 = wait(&status); // system call suspends execution of the calling thread until one of its children terminates
        fprintf(stdout, "process ID of the terminated child: %d\n", pid2);
        if (WIFEXITED(status)) { // returns true if the child terminated normally
            fprintf(stdout, "child process ended with: exit(%d)\n", WEXITSTATUS(status));
        }
        if (WIFSIGNALED(status)) { // returns true if the child process was terminated by a signal
            fprintf(stderr, "child process ended with: kill -%d\n", WTERMSIG(status));
        }
    }

    if (pid == 0) { // child process
        for (int j = 0; j < 5; j++) {
            int value;
            if (read_message(pid, value))
                write_message(pid, ++value);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }

		exit(0);
    }

    fprintf(stdout, "====== test finish ======\n");
    return 0;
}

      编译脚本build.sh内容如下:

#! /bin/bash

if [ -d build ]; then
    echo "build directory already exists, it does not need to be created again"
else
    mkdir -p build
fi

cd build
cmake ..
make

rc=$?
if [[ ${rc} != 0 ]];then
    echo "#### ERROR: please check ####"
    exit ${rc}
fi

echo "==== build finish ===="

      CMakeLists.txt内容如下:

cmake_minimum_required(VERSION 3.22)
project(samples_multi_process)

set(CMAKE_BUILD_TYPE Release) # only works under linux
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O2 -std=c++17")

file(GLOB samples ${PROJECT_SOURCE_DIR}/test_*.cpp)
#message(STATUS "samples: ${samples}")

foreach(sample ${samples})
    string(REGEX MATCH "[^/]+$" name ${sample})
    string(REPLACE ".cpp" "" exec_name ${name})
    #message(STATUS "exec name: ${exec_name}")

    add_executable(${exec_name} ${sample})
    target_link_libraries(${exec_name} rt)
endforeach()

      执行结果如下所示:

      GitHubhttps://github.com/fengbingchun/Linux_Code_Test

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值