ITPub博客

首页 > 应用开发 > IT综合 > System V共享内存全解析:从创建到映射的完整实践

System V共享内存全解析:从创建到映射的完整实践

翻译 IT综合 作者:bhluo 时间:2026-02-06 11:46:53 0 删除 编辑

# System V共享内存全解析:从创建到映射的完整实践


在Linux进程通信机制中,共享内存是最快的数据交换方式。System V共享内存作为经典的IPC机制,允许多个进程直接访问同一块物理内存区域。本文将详细解析从shmget到内存映射的完整流程。


## 共享内存的基本原理


共享内存的核心思想是:多个进程通过映射同一块物理内存区域来实现数据共享。这种方式避免了数据在用户空间和内核空间之间的复制,从而获得极高的通信效率。


```c

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/types.h>


// 共享内存的基本操作流程

void shared_memory_workflow() {

    // 1. 生成唯一的key

    // 2. 创建/获取共享内存段

    // 3. 映射到进程地址空间

    // 4. 使用共享内存

    // 5. 解除映射

    // 6. 删除共享内存段(可选)

}

```


## 键值生成与共享内存创建


### ftok函数:生成System V IPC键值


```c

#include <stdio.h>

#include <stdlib.h>


key_t generate_shared_memory_key(const char *pathname, int proj_id) {

    // pathname: 存在的文件路径(通常使用程序文件自身)

    // proj_id: 项目标识符(0-255)

    

    key_t key = ftok(pathname, proj_id);

    if (key == -1) {

        perror("ftok failed");

        return -1;

    }

    

    printf("Generated key: 0x%08x\n", key);

    printf("From path: %s, proj_id: %d\n", pathname, proj_id);

    return key;

}


// 使用示例

void demonstrate_ftok() {

    // 使用当前程序文件生成key

    key_t key1 = generate_shared_memory_key("/proc/self/exe", 'A');

    

    // 使用特定文件生成key

    key_t key2 = generate_shared_memory_key("/tmp/shared_mem.lock", 123);

    

    // 注意:相同的pathname和proj_id组合总是生成相同的key

}

```


### shmget:创建或获取共享内存段


```c

// 共享内存创建函数

int create_shared_memory_segment(key_t key, size_t size, int flags) {

    // size: 共享内存段大小(字节)

    // flags: 权限标志和创建选项

    

    int shmid = shmget(key, size, flags);

    if (shmid == -1) {

        perror("shmget failed");

        return -1;

    }

    

    printf("Shared memory segment created/retrieved:\n");

    printf("  SHMID: %d\n", shmid);

    printf("  Key: 0x%08x\n", key);

    printf("  Size: %zu bytes\n", size);

    

    return shmid;

}


// 不同的创建方式

void different_creation_methods() {

    key_t key = ftok("/tmp", 'S');

    

    // 方法1:创建新的共享内存段

    int shmid1 = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0666);

    // IPC_CREAT: 如果不存在则创建

    // IPC_EXCL: 如果已存在则失败

    // 0666: 读写权限

    

    // 方法2:获取已存在的共享内存段

    int shmid2 = shmget(key, 0, 0);  // size为0表示不创建新段

    

    // 方法3:创建私有共享内存(只在进程族内共享)

    int shmid3 = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);

    

    // 方法4:创建大页共享内存(需要系统支持)

    // int shmid4 = shmget(key, 2*1024*1024, IPC_CREAT | SHM_HUGETLB | 0666);

}

```


## 共享内存的映射与使用


### shmat:将共享内存映射到进程空间


```c

#include <sys/shm.h>


// 内存映射函数

void* map_shared_memory(int shmid, int flags) {

    // flags参数:

    // 0 - 默认,可读可写

    // SHM_RDONLY - 只读映射

    // SHM_RND - 对齐地址

    // SHM_REMAP - 重新映射(Linux特有)

    

    void* addr = shmat(shmid, NULL, flags);

    if (addr == (void*)-1) {

        perror("shmat failed");

        return NULL;

    }

    

    printf("Shared memory mapped at address: %p\n", addr);

    return addr;

}


// 指定映射地址

void* map_at_specific_address(int shmid, void* desired_addr) {

    // 尝试在指定地址映射

    void* addr = shmat(shmid, desired_addr, 0);

    

    if (addr == (void*)-1) {

        perror("shmat with specific address failed");

        return NULL;

    }

    

    printf("Mapped at desired address: %p (requested: %p)\n", 

           addr, desired_addr);

    return addr;

}

```


### 完整的映射示例


```c

// 生产者进程:创建并写入共享内存

void shared_memory_producer() {

    // 1. 生成key

    key_t key = ftok("/tmp/shared_mem_demo", 'P');

    

    // 2. 创建共享内存段(1MB大小)

    int shmid = shmget(key, 1024*1024, IPC_CREAT | IPC_EXCL | 0666);

    if (shmid == -1) {

        if (errno == EEXIST) {

            // 已存在,获取现有段

            shmid = shmget(key, 0, 0);

        } else {

            perror("shmget failed");

            return;

        }

    }

    

    // 3. 映射到进程地址空间

    void* shared_mem = shmat(shmid, NULL, 0);

    if (shared_mem == (void*)-1) {

        perror("shmat failed");

        return;

    }

    

    // 4. 使用共享内存

    printf("Producer: Shared memory mapped at %p\n", shared_mem);

    

    // 写入数据结构

    typedef struct {

        int version;

        int data_count;

        char data[1024];

    } shared_data_t;

    

    shared_data_t* data = (shared_data_t*)shared_mem;

    data->version = 1;

    data->data_count = 100;

    strcpy(data->data, "Hello from producer");

    

    printf("Producer: Wrote version=%d, count=%d\n", 

           data->version, data->data_count);

    

    // 5. 保持映射,让消费者可以读取

    printf("Producer: Press Enter to exit and clean up...\n");

    getchar();

    

    // 6. 解除映射

    if (shmdt(shared_mem) == -1) {

        perror("shmdt failed");

    }

    

    // 7. 删除共享内存段

    if (shmctl(shmid, IPC_RMID, NULL) == -1) {

        perror("shmctl IPC_RMID failed");

    } else {

        printf("Producer: Shared memory segment removed\n");

    }

}

```


## 共享内存同步机制


### 使用信号量同步共享内存访问


```c

#include <sys/sem.h>


// 共享内存同步结构

typedef struct {

    int shmid;      // 共享内存ID

    int semid;      // 信号量ID

    void* addr;     // 映射地址

    size_t size;    // 大小

} synchronized_shm_t;


// 初始化同步的共享内存

int init_synchronized_shm(synchronized_shm_t* shm, key_t shm_key, 

                         size_t size, int sem_count) {

    memset(shm, 0, sizeof(*shm));

    shm->size = size;

    

    // 创建共享内存

    shm->shmid = shmget(shm_key, size, IPC_CREAT | IPC_EXCL | 0666);

    if (shm->shmid == -1 && errno == EEXIST) {

        shm->shmid = shmget(shm_key, 0, 0);

    }

    if (shm->shmid == -1) {

        perror("Failed to create/get shared memory");

        return -1;

    }

    

    // 创建信号量(用于同步)

    key_t sem_key = shm_key + 1;

    shm->semid = semget(sem_key, sem_count, IPC_CREAT | IPC_EXCL | 0666);

    if (shm->semid == -1 && errno == EEXIST) {

        shm->semid = semget(sem_key, sem_count, 0);

    }

    if (shm->semid == -1) {

        perror("Failed to create/get semaphore");

        // 清理共享内存

        shmctl(shm->shmid, IPC_RMID, NULL);

        return -1;

    }

    

    // 初始化信号量

    union semun arg;

    for (int i = 0; i < sem_count; i++) {

        arg.val = (i == 0) ? 1 : 0;  // 第一个信号量作为互斥锁

        semctl(shm->semid, i, SETVAL, arg);

    }

    

    // 映射共享内存

    shm->addr = shmat(shm->shmid, NULL, 0);

    if (shm->addr == (void*)-1) {

        perror("shmat failed");

        shmctl(shm->shmid, IPC_RMID, NULL);

        semctl(shm->semid, 0, IPC_RMID);

        return -1;

    }

    

    printf("Initialized synchronized shared memory:\n");

    printf("  SHMID: %d\n", shm->shmid);

    printf("  SEMID: %d\n", shm->semid);

    printf("  Address: %p\n", shm->addr);

    printf("  Size: %zu bytes\n", shm->size);

    

    return 0;

}


// 带锁的共享内存访问

void write_to_synchronized_shm(synchronized_shm_t* shm, const void* data, 

                              size_t offset, size_t size) {

    // P操作:等待信号量

    struct sembuf op = {0, -1, SEM_UNDO};

    semop(shm->semid, &op, 1);

    

    // 写入数据

    if (offset + size <= shm->size) {

        memcpy((char*)shm->addr + offset, data, size);

    } else {

        fprintf(stderr, "Write exceeds shared memory boundary\n");

    }

    

    // V操作:释放信号量

    op.sem_op = 1;

    semop(shm->semid, &op, 1);

}

```


## 共享内存控制与监控


### shmctl:共享内存控制操作


```c

#include <sys/shm.h>


// 获取共享内存信息

void print_shared_memory_info(int shmid) {

    struct shmid_ds buf;

    

    if (shmctl(shmid, IPC_STAT, &buf) == -1) {

        perror("shmctl IPC_STAT failed");

        return;

    }

    

    printf("\n=== Shared Memory Information ===\n");

    printf("Segment ID: %d\n", shmid);

    printf("Key: 0x%08x\n", buf.shm_perm.__key);

    printf("Size: %lu bytes\n", buf.shm_segsz);

    printf("Attached processes: %lu\n", buf.shm_nattch);

    printf("Creator PID: %d\n", buf.shm_cpid);

    printf("Last attach PID: %d\n", buf.shm_lpid);

    printf("Last attach time: %ld\n", buf.shm_atime);

    printf("Last detach time: %ld\n", buf.shm_dtime);

    printf("Last change time: %ld\n", buf.shm_ctime);

    printf("Permissions: %o (owner: %d/%d)\n", 

           buf.shm_perm.mode,

           buf.shm_perm.uid, buf.shm_perm.gid);

    

    // 检查是否被标记为删除

    if (buf.shm_perm.mode & SHM_DEST) {

        printf("Warning: Segment marked for deletion\n");

    }

}


// 修改共享内存属性

int modify_shared_memory_properties(int shmid, uid_t uid, mode_t mode) {

    struct shmid_ds buf;

    

    // 首先获取当前状态

    if (shmctl(shmid, IPC_STAT, &buf) == -1) {

        perror("shmctl IPC_STAT failed");

        return -1;

    }

    <"dsn.h4k7.org.cn"><"abb.h4k7.org.cn"><"avg.h4k7.org.cn">

    // 修改权限

    buf.shm_perm.uid = uid;

    buf.shm_perm.mode = mode;

    

    // 应用修改

    if (shmctl(shmid, IPC_SET, &buf) == -1) {

        perror("shmctl IPC_SET failed");

        return -1;

    }

    

    printf("Shared memory properties updated\n");

    return 0;

}


// 锁定/解锁共享内存(防止交换)

int lock_shared_memory(int shmid, int lock) {

    // lock: 1-锁定, 0-解锁

    if (shmctl(shmid, lock ? SHM_LOCK : SHM_UNLOCK, NULL) == -1) {

        perror(lock ? "SHM_LOCK failed" : "SHM_UNLOCK failed");

        return -1;

    }

    

    printf("Shared memory %s\n", lock ? "locked" : "unlocked");

    return 0;

}

```


## 高级特性:共享内存与内存映射


### 与传统mmap的比较


```c

#include <sys/mman.h>

#include <fcntl.h>


// 使用mmap创建共享内存(POSIX方式)

void* create_shared_memory_mmap(const char* name, size_t size) {

    // 创建共享内存文件描述符

    int fd = shm_open(name, O_CREAT | O_RDWR, 0666);

    if (fd == -1) {

        perror("shm_open failed");

        return NULL;

    }

    

    // 设置文件大小

    if (ftruncate(fd, size) == -1) {

        perror("ftruncate failed");

        close(fd);

        return NULL;

    }

    

    // 内存映射

    void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, 

                     MAP_SHARED, fd, 0);

    close(fd);

    

    if (addr == MAP_FAILED) {

        perror("mmap failed");

        return NULL;

    }

    

    printf("POSIX shared memory created:\n");

    printf("  Name: %s\n", name);

    printf("  Size: %zu bytes\n", size);

    printf("  Address: %p\n", addr);

    

    return addr;

}


// 比较System V和POSIX共享内存

void compare_shared_memory_methods() {

    printf("\n=== System V vs POSIX Shared Memory ===\n");

    printf("\nSystem V (shmget):\n");

    printf("  - 使用key标识(ftok生成)\n");

    printf("  - 独立于文件系统\n");

    printf("  - 需要显式删除(shmctl IPC_RMID)\n");

    printf("  - 支持大页内存(SHM_HUGETLB)\n");

    

    printf("\nPOSIX (shm_open + mmap):\n");

    printf("  - 使用名称标识(/dev/shm/)\n");

    printf("  - 基于文件系统\n");

    printf("  - 自动清理(可配置)\n");

    printf("  - 更符合现代POSIX标准\n");

    printf("  - 支持匿名映射\n");

}

```


### 大页共享内存配置


```c

// 大页共享内存示例(需要系统支持)

#ifdef SHM_HUGETLB

void hugepage_shared_memory_demo() {

    key_t key = ftok("/tmp", 'H');

    

    // 尝试创建2MB大页共享内存

    int shmid = shmget(key, 2*1024*1024, 

                      IPC_CREAT | SHM_HUGETLB | 0666);

    

    if (shmid == -1) {

        if (errno == ENOSYS) {

            printf("Hugepages not supported by system\n");

        } else if (errno == ENOMEM) {

            printf("Not enough hugepages available\n");

        } else {

            perror("shmget with hugepages failed");

        }

        return;

    }

    

    void* addr = shmat(shmid, NULL, 0);

    if (addr == (void*)-1) {

        perror("shmat failed");

        return;

    }

    

    printf("Hugepage shared memory created:\n");

    printf("  SHMID: %d\n", shmid);

    printf("  Size: 2MB\n");

    printf("  Address: %p\n", addr);

    

    // 使用大页内存...

    

    shmdt(addr);

    shmctl(shmid, IPC_RMID, NULL);

}

#endif

```


## 实际应用:进程间数据交换


### 生产者-消费者模型实现


```c

// 共享内存环形缓冲区

typedef struct {

    int head;          // 写入位置

    int tail;          // 读取位置

    int count;         // 当前数据数量

    int capacity;      // 缓冲区容量

    char data[];       // 柔性数组,实际数据

} ring_buffer_t;


// 通过共享内存创建环形缓冲区

ring_buffer_t* create_shared_ring_buffer(key_t key, int capacity) {

    // 计算总大小

    size_t total_size = sizeof(ring_buffer_t) + capacity;

    

    // 创建共享内存

    int shmid = shmget(key, total_size, IPC_CREAT | IPC_EXCL | 0666);

    if (shmid == -1 && errno == EEXIST) {

        shmid = shmget(key, 0, 0);

    }

    if (shmid == -1) {

        perror("shmget failed");

        return NULL;

    }

    

    // 映射到内存

    ring_buffer_t* buffer = (ring_buffer_t*)shmat(shmid, NULL, 0);

    if (buffer == (void*)-1) {

        perror("shmat failed");

        return NULL;

    }

    

    // 如果是新创建的,初始化缓冲区

    struct shmid_ds buf;

    if (shmctl(shmid, IPC_STAT, &buf) == 0 && buf.shm_cpid == getpid()) {

        buffer->head = 0;

        buffer->tail = 0;

        buffer->count = 0;

        buffer->capacity = capacity;

        memset(buffer->data, 0, capacity);

        

        printf("New ring buffer created in shared memory\n");

    } else {

        printf("Attached to existing ring buffer\n");

    }

    

    printf("Ring buffer info:\n");

    printf("  Capacity: %d bytes\n", buffer->capacity);

    printf("  Current count: %d\n", buffer->count);

    printf("  Address: %p\n", buffer);

    

    return buffer;

}


// 向环形缓冲区写入数据

int ring_buffer_write(ring_buffer_t* buffer, const void* data, size_t size) {

    if (size > buffer->capacity - sizeof(ring_buffer_t)) {

        fprintf(stderr, "Data too large for buffer\n");

        return -1;

    }

    

    // 检查是否有足够空间

    if (buffer->count + size > buffer->capacity) {

        fprintf(stderr, "Buffer full\n");

        return -1;

    }

    

    // 写入数据

    int write_pos = buffer->head;

    for (size_t i = 0; i < size; i++) {

        buffer->data[write_pos] = ((char*)data)[i];

        write_pos = (write_pos + 1) % buffer->capacity;

    }

    

    // 更新头部指针

    buffer->head = write_pos;

    buffer->count += size;

    

    return 0;

}


// 从环形缓冲区读取数据

int ring_buffer_read(ring_buffer_t* buffer, void* data, size_t size) {

    if (size > buffer->count) {

        fprintf(stderr, "Not enough data in buffer\n");

        return -1;

    }

    

    // 读取数据

    int read_pos = buffer->tail;

    for (size_t i = 0; i < size; i++) {

        ((char*)data)[i] = buffer->data[read_pos];

        read_pos = (read_pos + 1) % buffer->capacity;

    }

    

    // 更新尾部指针

    buffer->tail = read_pos;

    buffer->count -= size;

    

    return 0;

}

```


## 系统管理工具


```bash

#!/bin/bash

# shared_memory_manager.sh - 共享内存管理工具


# 显示共享内存信息

show_shm_info() {

    echo "=== System V Shared Memory ==="

    ipcs -m

    

    echo -e "\n=== POSIX Shared Memory (/dev/shm/) ==="

    ls -la /dev/shm/ 2>/dev/null || echo "No POSIX shared memory"

}


# 清理共享内存

cleanup_shm() {

    local shmid=$1

    <"avr.h4k7.org.cn"><"evs.h4k7.org.cn"><"vrb.h4k7.org.cn">

    if [ -z "$shmid" ]; then

        echo "Usage: $0 cleanup <shmid>"

        return 1

    fi

    

    # 检查共享内存段是否存在

    if ipcs -m | grep -q " $shmid "; then

        # 获取附加的进程数

        nattch=$(ipcs -m -i $shmid | grep "attached" | awk '{print $NF}')

        

        if [ "$nattch" -eq 0 ]; then

            ipcrm -m $shmid

            echo "Shared memory segment $shmid removed"

        else

            echo "Cannot remove: $nattch processes still attached"

            echo "Attached processes:"

            ipcs -m -i $shmid | grep -A 20 "attached"

        fi

    else

        echo "Shared memory segment $shmid not found"

    fi

}


# 监控共享内存使用

monitor_shm_usage() {

    watch -n 2 'echo "=== Shared Memory Usage ==="; 

                echo -e "SHMID\t\tSize\t\tAttached\tKey";

                ipcs -m | tail -n +4 | awk '\''{printf "%-10s %-12s %-10s %-10s\n", $2, $5, $6, $1}'\'''

}


# 主函数

main() {

    case "$1" in

        show)

            show_shm_info

            ;;

        cleanup)

            cleanup_shm "$2"

            ;;

        monitor)

            monitor_shm_usage

            ;;

        *)

            echo "Usage: $0 {show|cleanup <shmid>|monitor}"

            exit 1

            ;;

    esac

}


main "$@"

```


## 性能优化与最佳实践


### 内存对齐与缓存优化


```c

// 优化共享内存数据结构

typedef struct __attribute__((aligned(64))) {

    // 64字节对齐,避免缓存行伪共享

    _Atomic int write_index;

    char padding1[64 - sizeof(_Atomic int)];

    

    _Atomic int read_index;

    char padding2[64 - sizeof(_Atomic int)];

    

    volatile int data_ready;

    char padding3[64 - sizeof(volatile int)];

    

    char data[4096];

} cache_optimized_shm_t;


// 使用示例

void use_optimized_structure(key_t key) {

    int shmid = shmget(key, sizeof(cache_optimized_shm_t), 

                      IPC_CREAT | 0666);

    if (shmid == -1) return;

    

    cache_optimized_shm_t* shm = (cache_optimized_shm_t*)shmat(shmid, NULL, 0);

    if (shm == (void*)-1) return;

    

    // 不同字段位于不同缓存行,减少伪共享

    atomic_store(&shm->write_index, 0);

    atomic_store(&shm->read_index, 0);

    shm->data_ready = 0;

    

    shmdt(shm);

}

```


### 错误处理与资源管理


```c

// 健壮的共享内存处理封装

typedef struct {

    int shmid;

    void* addr;

    int is_owner;

} shm_handle_t;


int create_shm_handle(shm_handle_t* handle, key_t key, 

                     size_t size, int create_new) {

    memset(handle, 0, sizeof(*handle));

    

    int flags = 0666;

    if (create_new) {

        flags |= IPC_CREAT | IPC_EXCL;

        handle->is_owner = 1;

    }

    

    handle->shmid = shmget(key, size, flags);

    if (handle->shmid == -1) {

        perror("shmget failed");

        return -1;

    }

    

    handle->addr = shmat(handle->shmid, NULL, 0);

    if (handle->addr == (void*)-1) {

        perror("shmat failed");

        

        // 如果是创建者,清理共享内存

        if (handle->is_owner) {

            shmctl(handle->shmid, IPC_RMID, NULL);

        }

        

        return -1;

    }

    

    return 0;

}


void destroy_shm_handle(shm_handle_t* handle) {

    if (handle->addr && handle->addr != (void*)-1) {

        shmdt(handle->addr);

    }

    

    if (handle->is_owner && handle->shmid != -1) {

        // 检查是否还有其他进程附加

        struct shmid_ds buf;

        if (shmctl(handle->shmid, IPC_STAT, &buf) == 0) {

            if (buf.shm_nattch == 0) {

                shmctl(handle->shmid, IPC_RMID, NULL);

            } else {

                fprintf(stderr, "Warning: %lu processes still attached\n", 

                        buf.shm_nattch);

            }

        }

    }

    

    memset(handle, 0, sizeof(*handle));

}

```


## 总结与选择指南


System V共享内存是Linux IPC中的重要组成部分,具有以下特点:


**优势**:

- 极高的数据传输性能(无需内核复制)

- 支持大内存区域

- 独立于文件系统

- 成熟的同步机制配合


**劣势**:

- 需要显式同步机制

- 资源管理复杂

- 可能产生内存泄漏

- 现代系统中POSIX替代方案更受欢迎


**适用场景**:

- 需要高性能进程间通信

- 大量数据交换

- 实时系统

- 遗留系统维护


**最佳实践建议**:

1. 始终实现适当的同步机制(信号量等)

2. 使用`IPC_EXCL`标志避免竞争条件

3. 实现完善的错误处理和资源清理

4. 考虑使用大页内存提高性能

5. 监控共享内存使用情况


虽然现代Linux更推荐使用POSIX共享内存,但理解System V共享内存的工作原理仍然重要。它不仅有助于维护遗留系统,还能加深对Linux内存管理和进程通信的理解。在实际项目中,应根据具体需求选择最合适的共享内存实现方式。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3110104/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    933820