linux应用 进程间通信之共享内存(POSIX)

1、前言

1.1 定义

POSIX共享内存是一种在UNIX和类UNIX系统上可用的进程间通信机制。它允许多个进程共享同一块内存区域,从而可以在这块共享内存上进行读写操作。

1.2 应用场景

POSIX共享内存适用于需要高效地进行大量数据交换的场景,比如多个进程需要共享大型数据集合或缓存。它可以提供比其他进程间通信方式更快的数据传输速度

1.3 优缺点

1.3.1 优点
  • 高效性:共享内存允许多个进程直接访问同一块内存,因此数据传输速度更快。
  • 灵活性:由于共享内存是直接映射到进程的地址空间,因此对数据的访问更加灵活。
1.3.2 缺点
  • 同步问题:需要额外的同步机制来确保多个进程对共享内存的访问是安全的。
  • 通信复杂性:共享内存通常需要与其他进程间通信方式(如信号量、互斥量等)结合使用。

2、常用接口

2.1 shm_open函数

创建或打开一个POSIX共享内存对象。

int shm_open(const char *name, int oflag, mode_t mode);

参数

  • name:共享内存对象的名称,以斜杠开头,类似于文件路径。
  • oflag:打开标志,可以使用 O_CREAT 表示创建共享内存对象,O_RDWR 表示读写模式等。
  • mode:创建共享内存对象时的权限模式。

返回值

  • 成功:返回一个非负整数,表示共享内存对象的文件描述符。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.2 ftruncate函数

设置共享内存对象的大小。

int ftruncate(int fd, off_t length);

参数

  • fd:共享内存对象的文件描述符。
  • length:要设置的共享内存大小。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.3 mmap函数

将共享内存对象映射到进程的地址空间。

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数

  • addr:指定映射的地址,通常为0表示由系统选择合适的地址。
  • length:映射的长度。
  • prot:映射的保护模式,如 PROT_READ 和 PROT_WRITE。
  • flags:映射的标志,如 MAP_SHARED 表示共享映射。
  • fd:共享内存对象的文件描述符。
  • offset:共享内存对象的偏移量。

返回值

  • 成功:返回映射区的起始地址。
  • 失败:返回 MAP_FAILED,并设置 errno 以指示错误原因。

2.4 munmap函数

解除共享内存的映射。

int munmap(void *addr, size_t length);

参数

  • addr:要解除映射的地址。
  • length:映射的长度。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.5 shm_unlink函数

删除共享内存对象。

int shm_unlink(const char *name);

参数

  • name:共享内存对象的名称。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

3、编程测试

3.1 共享内存简单测试

测试代码如下,代码编译需要加-lrt -pthread

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// 打印时分秒的宏        
#define PRINT_MIN_SEC do { \
            time_t t = time(NULL); \
            struct tm *tm_ptr = localtime(&t); \
            printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\
        } while (0);printf



#define SHM_NAME "/example_shm"
#define SHM_SIZE 1024

void parent_process();
void child_process();

int main() 
{
    pid_t pid = fork();
    if (pid == 0) 
    {
        child_process();
    } 
    else if (pid > 0) 
    {
        parent_process();
    } 
    else 
    {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    return 0;
}

void parent_process() 
{
    PRINT_MIN_SEC("Parent start!\n");
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) 
    {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    ftruncate(shm_fd, SHM_SIZE);

    char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    bzero(ptr, SHM_SIZE);
    strcpy(ptr, "Parent Write Data");

    sleep(2); // 等待子进程读取共享内存中的数据

    PRINT_MIN_SEC("Parent read from shared memory: %s\n", ptr);
    munmap(ptr, SHM_SIZE);
    close(shm_fd);
    shm_unlink(SHM_NAME);
}

void child_process() 
{
    PRINT_MIN_SEC("Child start!\n");
    
    sleep(1); // 等待父进程创建共享内存并写入数据到共享内存
    
    int shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);
    if (shm_fd == -1) 
    {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) 
    {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    PRINT_MIN_SEC("Child read from shared memory: %s\n", ptr);
    bzero(ptr, SHM_SIZE);
    strcpy(ptr, "Child Write Data");

    munmap(ptr, SHM_SIZE);
    close(shm_fd);
}

通过父子间进程读写共享内存的时间不一致来实现进程通信,测试结果如下:

3.2 共享内存配合信号量使用测试

测试代码如下,代码编译需要加-lrt -pthread

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


// 打印时分秒的宏        
#define PRINT_MIN_SEC do { \
            time_t t = time(NULL); \
            struct tm *tm_ptr = localtime(&t); \
            printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\
        } while (0);printf



#define SHM_NAME "/example_shm"
#define SHM_SIZE 1024


void parent_process();
void child_process();

// 打印当前信号量的值
void printfValue(sem_t *sem)
{
    // 打印当前值
    int value;
    sem_getvalue(sem, &value);
    PRINT_MIN_SEC("Current value of semaphore: %d\n", value);
}


int main() 
{
    sem_t *mutex = sem_open("/mysemaphore", O_CREAT, 0644, 0);  
    
    pid_t pid = fork();
    if (pid == 0) 
    {
        child_process(mutex);
    } 
    else if (pid > 0) 
    {
        parent_process(mutex);
    } 
    else 
    {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    return 0;
}

void parent_process(sem_t * mutex) 
{
    int SendNum = 0;
    char SendData[32] = {0};
    PRINT_MIN_SEC("Parent start!\n");
    printfValue(mutex);
    
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) 
    {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    ftruncate(shm_fd, SHM_SIZE);

    char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        sprintf(SendData, "Parent Write Data-%d\n", SendNum);
        SendNum++;
        strcpy(ptr, SendData);
        sem_post(mutex);
        sleep(2);
    }


    PRINT_MIN_SEC("Parent read from shared memory: %s\n", ptr);

    munmap(ptr, SHM_SIZE);
    close(shm_fd);
    shm_unlink(SHM_NAME);
}

void child_process(sem_t * mutex) 
{
    PRINT_MIN_SEC("Child start!\n");
    printfValue(mutex);
    sleep(1); // 等待父进程创建共享内存并写入数据到共享内存
    
    int shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);
    if (shm_fd == -1) 
    {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) 
    {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        sem_wait(mutex);
        PRINT_MIN_SEC("Child read from shared memory: %s\n", ptr);
    }

    munmap(ptr, SHM_SIZE);
    close(shm_fd);
}

父进程通过信号量,每1秒写一次数据,写入完毕后执行post,子进程一直阻塞等待,获取到信号量后从共享内存读取数据,测试结果如下:

linux应用 进程间通信之共享内存(POSIX)_第1张图片

4、总结

本文阐述了进程间通信之共享内存(POSIX)的定义、应用场景、优缺点等,列举了编程中使用的接口,编写了测试用例测试相关功能。

你可能感兴趣的:(linux应用,linux,运维,服务器)