You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下用C语言检测文件是否被其他进程映射的方法

Linux下mmap共享Bitmap的进程崩溃后资源回收方案

针对你用mmap实现多进程共享Bitmap、进程崩溃后资源位无法自动清除的问题,核心解决思路是给每个资源位绑定占用进程的PID,通过检测PID存活状态来回收僵尸资源,以下是具体实现方案:

1. 扩展共享内存结构

原Bitmap只记录资源是否被占用,现在需要额外维护一个PID数组,每个元素对应Bitmap的一位,存储占用该资源的进程ID(0表示未占用)。同时加入进程间互斥锁,保证所有操作的原子性:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>

#define RESOURCE_COUNT 100  // 自定义资源总数
#define SHARED_FILE "/tmp/shared_resource_map"  // 共享映射文件路径

typedef struct {
    pthread_mutex_t mutex;                  // 进程间共享互斥锁
    unsigned char bitmap[RESOURCE_COUNT / 8 + 1];  // 资源占用位图
    pid_t resource_owners[RESOURCE_COUNT];  // 每个资源对应的占用进程PID
} SharedResourceMap;

2. 初始化共享映射

创建并映射共享文件,同时初始化互斥锁和共享结构的初始状态:

SharedResourceMap* init_shared_map() {
    int fd = open(SHARED_FILE, O_CREAT | O_RDWR, 0666);
    if (fd == -1) {
        perror("open shared file failed");
        return NULL;
    }

    // 扩展文件到结构体大小
    struct stat st;
    if (fstat(fd, &st) == -1) {
        perror("fstat failed");
        close(fd);
        return NULL;
    }
    if (st.st_size < sizeof(SharedResourceMap)) {
        if (ftruncate(fd, sizeof(SharedResourceMap)) == -1) {
            perror("ftruncate failed");
            close(fd);
            return NULL;
        }
    }

    // 映射到进程内存空间
    SharedResourceMap* map = mmap(NULL, sizeof(SharedResourceMap), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    if (map == MAP_FAILED) {
        perror("mmap failed");
        return NULL;
    }

    // 仅第一次创建时初始化锁和结构
    static int is_initialized = 0;
    if (!is_initialized) {
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);  // 设置为进程间共享
        pthread_mutex_init(&map->mutex, &attr);
        pthread_mutexattr_destroy(&attr);

        // 初始化位图和PID数组为0
        for (int i = 0; i < RESOURCE_COUNT; i++) {
            map->resource_owners[i] = 0;
            int byte_idx = i / 8;
            int bit_idx = i % 8;
            map->bitmap[byte_idx] &= ~(1 << bit_idx);
        }
        is_initialized = 1;
    }

    return map;
}

3. 僵尸资源清理逻辑

通过kill(pid, 0)检测进程存活状态(该调用仅检查PID是否存在,不发送任何信号),如果进程已死则回收对应资源:

void cleanup_dead_resources(SharedResourceMap* map) {
    pthread_mutex_lock(&map->mutex);
    for (int i = 0; i < RESOURCE_COUNT; i++) {
        pid_t pid = map->resource_owners[i];
        if (pid == 0) continue;

        // 检查进程是否存活:返回-1且errno=ESRCH表示进程不存在
        if (kill(pid, 0) == -1) {
            if (errno == ESRCH) {
                int byte_idx = i / 8;
                int bit_idx = i % 8;
                map->bitmap[byte_idx] &= ~(1 << bit_idx);
                map->resource_owners[i] = 0;
                printf("Cleaned resource %d (dead PID: %d)\n", i, pid);
            }
        }
    }
    pthread_mutex_unlock(&map->mutex);
}

4. 资源申请与释放

申请资源前先清理僵尸资源,保证可用资源的准确性;释放资源时同步清理位图和PID记录:

// 申请资源,返回资源索引,失败返回-1
int allocate_resource(SharedResourceMap* map) {
    cleanup_dead_resources(map);

    pthread_mutex_lock(&map->mutex);
    for (int i = 0; i < RESOURCE_COUNT; i++) {
        int byte_idx = i / 8;
        int bit_idx = i % 8;
        if (!(map->bitmap[byte_idx] & (1 << bit_idx))) {
            map->bitmap[byte_idx] |= (1 << bit_idx);
            map->resource_owners[i] = getpid();
            pthread_mutex_unlock(&map->mutex);
            return i;
        }
    }
    pthread_mutex_unlock(&map->mutex);
    return -1;
}

// 释放指定索引的资源
void release_resource(SharedResourceMap* map, int res_idx) {
    if (res_idx < 0 || res_idx >= RESOURCE_COUNT) return;

    pthread_mutex_lock(&map->mutex);
    int byte_idx = res_idx / 8;
    int bit_idx = res_idx % 8;
    map->bitmap[byte_idx] &= ~(1 << bit_idx);
    map->resource_owners[res_idx] = 0;
    pthread_mutex_unlock(&map->mutex);
}

关键注意事项

  • 互斥锁属性:必须设置PTHREAD_PROCESS_SHARED,否则其他进程无法识别该锁,会导致竞态条件。
  • 权限问题:kill(pid, 0)对root进程会返回EPERM(表示进程存活但无权限发送信号),不要误判为进程已死,只在errno == ESRCH时判定进程死亡。
  • 性能优化:如果业务进程频繁申请资源,每次遍历清理会有开销,可以单独启动一个后台监控进程,每隔固定时间调用cleanup_dead_resources,业务进程无需每次清理。
  • 文件权限:共享文件要设置合适的权限(如0666),确保所有需要访问的进程都能读写。

内容的提问来源于stack exchange,提问作者HnlyWk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 17:55:21