01 原理

开辟一块共享内存,使得相关进程均可访问同一块区域,再将互斥锁定义在该区域(即共享内存)上,使得相关进程可以使用该锁。

02 进程间的互斥锁和线程间互斥锁的区别

函数pthread_mutex_init(互斥锁地址, 属性对象地址)在定义一把线程锁的时候第二个参数通常传为NULL,这样该锁默认只能被统一进程下的线程持有。

如果要将其定义为进程之间可以持有的互斥锁,则需要传入属性对象地址。

// 锁
pthread_mutex_t lock;
// 状态对象
pthread_mutexattr_t lock_attr;

// 初始化锁状态,设置状态状态为——进程共享
pthread_mutexattr_init(&lock_attr);
pthread_mutexattr_setpshared(&lock_attr, PTHREAD_PROCESS_SHARED);
// 用锁状态来初始化锁
pthread_mutex_init(&lock, &lock_attr);

// 使用时不牵扯状态对象,但状态对象在锁销毁时也要销毁
pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);

// 销毁锁和锁状态
pthread_mutex_destroy(&lock);
pthread_mutexattr_destroy(&lock_attr);

现在我们需要将其定义在共享内存上,这样该锁就可以被其他进程访问,否则不能达到效果,原因请参考Linux虚拟地址

03 将锁定义在共享内存上

共享内存的开辟参考Linux共享内存

04 Show me the code

父进程每次将共享内存上的变量加1,子进程每次将其加2,用定义在共享内存上的互斥锁维护该变量的累加操作原子性。

去掉两进程加锁、开锁的过程则结果出错(N越大越容易暴露问题)。

#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>


/**
 *  返回一片共享内存标识符,用于后续获取该共享内存,以及销毁该共享内存
 *  INDEX_OF_KEY —— 自定义的该共享内存序号
 *  LENGTH —— 共享内存大小
 */
const int create_flag(const int INDEX_OF_KEY, const unsigned int LENGTH) {
    // 生成key
    const char* FILE_PATH = "./";
    key_t key = ftok(FILE_PATH, INDEX_OF_KEY);

    // 创建共享内存空间
    const int FLAG = shmget(key, LENGTH, IPC_CREAT | 0666);

    return FLAG;
}


// 定义进程锁结构体
typedef struct MUTEX_PACKAGE {
    // 锁以及状态
    pthread_mutex_t lock;
    pthread_mutexattr_t lock_attr;
    // 在共享内存中的标识符
    int FLAG;
} mutex_package_t;


// 初始化进程锁结构体
const int init(void* pthis) {
    mutex_package_t* mp = (mutex_package_t*)pthis;
    // 初始化锁状态,设置状态状态为——进程共享
    pthread_mutexattr_init(&(mp->lock_attr));
    pthread_mutexattr_setpshared(&(mp->lock_attr), PTHREAD_PROCESS_SHARED);
    // 用锁状态来初始化锁
    pthread_mutex_init(&(mp->lock), &(mp->lock_attr));

    return 0;
}


// 在共享内存上定义进程锁结构体并且返回其位置
mutex_package_t* create_mutex_package(const int INDEX) {
    const int FLAG = create_flag(INDEX, sizeof(mutex_package_t));
    mutex_package_t* mp = (mutex_package_t*)shmat(FLAG, NULL, SHM_R | SHM_W);
    mp->FLAG = FLAG;

    assert(init(mp) == 0);

    return mp;
}


// 销毁进程锁结构体,利用其FLAG变量索引到其占用的共享内存并销毁
const int destory_mutex_package(mutex_package_t* mp) {
    // 销毁锁和锁状态
    pthread_mutex_destroy(&(mp->lock));
    pthread_mutexattr_destroy(&(mp->lock_attr));

    // 释放共享内存
    assert(shmctl(mp->FLAG, IPC_RMID, NULL) == 0);

    return 0;
}


int main() {
    // 创建自定义进程锁
    mutex_package_t* mp = create_mutex_package(111);

    // 获取一片共享内存空间
    const int FLAG = create_flag(222, sizeof(int));
    volatile int* x = (int*)shmat(FLAG, NULL, 0);

    // 创建新进程
    int id = fork();
    assert(id >= 0);

    // 设置循环次数
    const int N = 1000000;

    // 父进程每次加1,子进程每次加2
    int i;
    for (i = 0; i < N; ++i) {

        if (id > 0) {  // 父进程
            // 加锁
            pthread_mutex_lock(&(mp->lock));
            int temp = *x;
            *x = temp+1;
            // 解锁
            pthread_mutex_unlock(&(mp->lock));

        } else {  // 子进程
            // 加锁
            pthread_mutex_lock(&(mp->lock));
            int temp = *x;
            *x = temp+2;
            // 解锁
            pthread_mutex_unlock(&(mp->lock));
        }

    }

    // 等待循环完毕
    sleep(1);

    // 打印
    printf("pid= %d, x_address= %x, x= %d\n", getpid(), x, *x);

    // 等待打印完毕
    sleep(1);

    // 销毁进程锁,释放申请的共享内存
    if (id > 0) {  // 父进程
        destory_mutex_package(mp);
        mp = NULL;
        shmctl(FLAG, IPC_RMID, NULL);
        x = NULL;
        printf("父进程释放资源完毕\n");
    }

    return 0;
}

05 结果

pid= 64735, x_address= c1f1000, x= 3000000
pid= 64737, x_address= c1f1000, x= 3000000
父进程释放资源完毕

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐