在 Linux 内核命名空间之间使用 POSIX 信号量

问题描述

我正在使用 Linux 命名空间开发 C 应用程序,出现的一件事是需要使用信号量(或类似的东西)从父级向子命名空间发出信号。这是我目前正在尝试做的:

#define _GNU_SOURCE
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <semaphore.h> 

//A stack for the container
#define STACK_SIZE (1024 * 1024)
static char stack[STACK_SIZE];

//The semaphore
sem_t semaphore;

int child(void* arg){
    int semval;

    //Print the semaphore state as read from the new namespace
    for(int i=0;i<6;i++){
        sem_getvalue(&semaphore,&semval);
        printf("Semaphore state: %d.\n",semval);
        sleep(1);
    }

    return 1;
}

int main(){
    //Init a shared POSIX semaphore with the value zero
    sem_init(&semaphore,1,0); 

    //Create the child namespace
    pid_t pid = clone(child,stack+STACK_SIZE,CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD,NULL);

    //Wait,then post the semaphore
    sleep(3);
    printf("Posting semaphore\n");
    sem_post(&semaphore);

    //Wait for it to return
    waitpid(pid,NULL,0);

    return 0;
}

据我所知,这应该在一个新的命名空间中启动 child,在那里它会多次吐出信号量的值。我们应该能够看到父命名空间中的父进程何时发布它,但是,我们没有:

Semaphore state: 0.
Semaphore state: 0.
Semaphore state: 0.
Posting semaphore
Semaphore state: 0.
Semaphore state: 0.
Semaphore state: 0.

信号量被初始化为共享的,从克隆调用删除 CLONE_NEWIPC 也不能解决这个问题(据我所知,它只处理隔离 SysV IPC,而不是这个 POSIX 信号量)。>

一个怪癖是,如果我们将信号量初始化为不同的值(例如 sem_init(&semaphore,3);,子命名空间将读取该初始值:

Semaphore state: 3.
Semaphore state: 3.
Semaphore state: 3.
Posting semaphore
Semaphore state: 3.
Semaphore state: 3.
Semaphore state: 3.

因此,它似乎并非完全无法访问信号量 - 只是无法看到它何时发布。你该怎么做?是否需要设置一些特殊的共享内存技巧才能使其在命名空间之间工作,或者我只是在这里做错了什么?

解决方法

sem_* 假设信号量内存在进程/线程之间共享

当您clone/fork时,您会得到不同的副本。

要修复,请将 CLONE_VM 添加到您的标志参数 [首选]。

或者,在执行 mmap 之前确保信号量在共享内存中(例如 shmgetclone 等)。