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

使用伪终端运行bash报ioctl错误与无作业控制问题求助

问题背景

为给后续项目搭建技术基础,开发一款通过伪终端(pty)运行bash的程序,核心逻辑为透传所有接收到的数据。
即便已正确传递termios配置,运行时仍然抛出如下报错:

bash: cannot set terminal process group (8751): Inappropriate ioctl for device
bash: no job control in this shell

以下是问题对应的完整实现代码,代码暂未实现SIGNAL处理逻辑:

#define _XOPEN_SOURCE 700

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/poll.h>
#include <pty.h>

#include "libtmt/tmt.h"
#include <ncurses.h>

typedef struct App {
    int pty_fd;
    pid_t pid;
} App;

App *App_new(char **argv) {
    App *self = malloc(sizeof(App));
    int slave_pty_fd, master_pty_fd;

    struct termios terminal_settings;
    tcgetattr(STDERR_FILENO, &terminal_settings);
    
    struct winsize terminal_size = {
        .ws_row = 24,
        .ws_col = 80,
    };
    int ret = openpty(&master_pty_fd, &slave_pty_fd, NULL, &terminal_settings, &terminal_size);
    if (ret < 0) {
        perror("openpty");
        exit(1);
    }
    self->pty_fd = master_pty_fd;

    self->pid = fork();
    if (self->pid < 0) {
        perror("fork");
        exit(1);
    }
    if (self->pid == 0) {
        close(master_pty_fd);
        if (dup2(slave_pty_fd, STDIN_FILENO) != STDIN_FILENO) {
            perror("dup2");
            exit(1);
        }
        if (dup2(slave_pty_fd, STDOUT_FILENO) != STDOUT_FILENO) {
            perror("dup2");
            exit(1);
        }
        if (dup2(slave_pty_fd, STDERR_FILENO) != STDERR_FILENO) {
            perror("dup2");
            exit(1);
        }

        if (execvp(argv[0], argv) < 0) {
            perror("execvp");
            exit(1);
        }
    }

    close(slave_pty_fd);

    return self;
}

int main() {
    int ret;

    char *argv[] = {
        "/bin/bash",
        NULL,
    };

    App *app = App_new(argv);

    while (TRUE) {
        struct pollfd fds[2] = {
            { .fd = app->pty_fd, .events = POLLIN },
            { .fd = STDIN_FILENO, .events = POLLIN },
        };
        ret = poll(fds, 2, -1);
        if (ret < 0) {
            perror("poll");
            exit(1);
        }

        if (fds[0].revents & POLLIN) {
            char buf[1024];
            ret = read(app->pty_fd, buf, sizeof(buf));
            if (ret < 0) {
                perror("read");
                exit(1);
            }
            if (ret == 0) {
                printf("0 read but poll said it was okay\n");
                exit(0);
            }
            write(STDOUT_FILENO, buf, ret);
        }
        if (fds[1].revents & POLLIN) {
            char buf[1024];
            ret = read(STDIN_FILENO, buf, sizeof(buf));
            if (ret < 0) {
                perror("read");
                exit(1);
            }
            if (ret == 0) {
                printf("0 read but poll said it was okay\n");
                exit(0);
            }
            write(app->pty_fd, buf, ret);
        }
    }
}
报错原因

报错的核心原因是子进程(运行bash的进程)没有将伪终端从设备设置为自己的控制终端,也没有创建独立的会话,导致bash调用tcsetpgrp设置终端进程组时权限不足,无法开启作业控制。
现有子进程逻辑仅完成了fd重定向,缺少两个关键步骤:

  • 未调用setsid()创建新会话,子进程仍然依附在父进程的会话中,没有独立的进程组权限
  • 未显式将伪终端从设备设置为子进程的控制终端,bash无法对该fd执行终端相关的ioctl操作
修复方案

修改子进程分支的逻辑,在重定向fd前后补充会话创建、控制终端设置的逻辑,修改后的子进程代码如下:

if (self->pid == 0) {
    close(master_pty_fd);
    // 创建新会话,成为会话首进程
    setsid();
    
    if (dup2(slave_pty_fd, STDIN_FILENO) != STDIN_FILENO) {
        perror("dup2");
        exit(1);
    }
    if (dup2(slave_pty_fd, STDOUT_FILENO) != STDOUT_FILENO) {
        perror("dup2");
        exit(1);
    }
    if (dup2(slave_pty_fd, STDERR_FILENO) != STDERR_FILENO) {
        perror("dup2");
        exit(1);
    }
    // 将伪终端从设备设置为当前进程的控制终端
    ioctl(slave_pty_fd, TIOCSCTTY, 0);
    // 重定向完成后关闭多余的从设备fd
    close(slave_pty_fd);

    if (execvp(argv[0], argv) < 0) {
        perror("execvp");
        exit(1);
    }
}

补充说明:

  • 使用TIOCSCTTY宏需要提前引入头文件<sys/ioctl.h>
  • 部分系统上openpty返回的从设备fd不会自动成为子进程控制终端,必须显式调用对应ioctl完成设置
  • 后续补充信号处理逻辑时,还需要将父进程的标准输入设置为raw模式,避免宿主终端拦截信号、做行缓冲,导致输入输出异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:36:18