如何在Docker容器中运行的二进制程序操作GPIO?

后端开发 2026-07-11

我有一个简单的 Dockerfile

# syntax=docker/dockerfile:1

FROM oraclelinux:9
RUN useradd --home-dir /home/my_user --uid 1000 --create-home --shell /bin/bash my_user
USER my_user
WORKDIR /home/my_user
RUN mkdir -p /home/my_user/bin
COPY --chown=my_user --chmod=0755 gpio_test /home/my_user/bin/gpio_test
ENTRYPOINT ["tail", "-f", "/dev/null"]

其中 gpio_test 是一个简单的C 应用程序,作为我们CI/CD流水线的一部分进行编译,用来通过 /dev/port 设备操作GPIO引脚:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

const int DO_PORT = 0xA02;
const int DO7_BIT = 6;

int main(int argc, char** argv) {
    if (argc < 2) {
        printf("missing arg \n");
        return -1;
    }
    int fd = open("/home/my_user/gpio/port", O_RDWR | O_NONBLOCK);
    if (fd < 0) {
        perror("failed to open port");
        return -1;
    }
    char buff = 0;
    lseek(fd, DO_PORT, SEEK_SET);
    read(fd, &buff, 1);
    printf("read byte: %d\n", (int)buff);
    lseek(fd, DO_PORT, SEEK_SET);
    if (strcmp(argv[1], "on") == 0) {
        printf("on\n");
        char val = buff & ~(1 << DO7_BIT);
        write(fd, &val, 1);
    } else if (strcmp(argv[1], "off") == 0) {
        printf("off\n");
        char val = buff | (1 << DO7_BIT);
        write(fd, &val, 1);
    } else {
        printf("unsupported arg\n");
    }
    close(fd);
    return 0;
}

如果我在主机上运行这个应用程序,之前给它原始IO能力:

sudo setcap cap_sys_rawio=ep gpio_test

那么我就可以在不需要 sudo 的情况下操作这些引脚,示例输出:

./gpio_test on
read byte: 64
on

然而,当我使用以下Docker compose文件运行我的容器时:

name: foo
services:
  bar:
    image: nameOfMyDockerImage:1
    container_name: test
    devices:
      - /dev/port:/home/my_user/gpio/port
    cap_add:
      - SYS_RAWIO

接着在运行中的容器内部

sudo docker exec -it test bash

我得到 Operation not permitted 错误:

./bin/gpio_test on
failed to open port: Operation not permitted

有趣的是,如果我在主机上执行 sudo docker cp gpio_test test:/home/my_user/(此前在主机上对它调用了 setcap),那么我就可以在容器内成功运行它

./gpio_test off
read byte: 0
off

我应该怎么做,才能在运行中的Docker容器内,通过执行镜像中的某个二进制文件来操作主机的GPIO?

在主机上,我又创建了一个额外的udev规则,为相关的gpio文件赋予777的权限

ls -lah /dev/port
crwxrwxrwx 1 root kmem 1, 4 Mar 19 08:18 /dev/port

但这并没有帮助。

解决方案

在构建Docker镜像时,同样需要调用 setcap。为了让我它工作,需要满足的条件完整清单如下:

  • /dev/port 文件需要具备读写权限,通过 /etc/udev/rules.d/90-port-chmod.rules 获得:KERNEL=="port", MODE="0777";注意777可能有些夸张
  • docker compose文件需要具备 SYS_RAWIO 能力,设备映射是可以的
  • Dockerfile 需要为目标二进制授予原始IO能力;另外如果该二进制链接到用户环境中指定的某些库 LD_LIBRARY_PATH 那就不再起作用,因此链接器必须进行全局配置
# syntax=docker/dockerfile:1

FROM oraclelinux:9
RUN useradd --home-dir /home/my_user --uid 1000 --create-home --shell /bin/bash my_user
USER my_user
WORKDIR /home/my_user
RUN mkdir -p /home/my_user/bin
COPY --chown=my_user --chmod=0755 gpio_test /home/my_user/bin/gpio_test
USER root
RUN setcap cap_sys_rawio=ep /home/my_user/bin/gpio_test
RUN echo "/home/my_user/lib/" > /etc/ld.so.conf.d/my_user_libs.conf && ldconfig
USER my_user
ENTRYPOINT ["tail", "-f", "/dev/null"]

现在在运行中的容器内调用 gpio_test 二进制文件,可以改变控制GPIO的 /dev/port 的状态。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章