【问题标题】:Detaching terminal of a parent process分离父进程的终端
【发布时间】:2015-01-16 13:47:21
【问题描述】:

我正在 Linux 下试验命名空间,因此我正在编写一个小型 C 程序来隔离使用 debootstrap 创建的 Debian Wheezy 环境。

我可以成功启动 sysv-init 并获得登录提示,但是当我关闭隔离环境时,无论是关闭系统还是杀死 -9 init,终端都处于似乎没有连接控制终端的状态贝壳。具体来说,如果我启动 sudo,它会抱怨没有终端。

我将 sudo 中的故障点缩小到以下语句:

open("/dev/tty", O_RDWR|O_NOCTTY);

出现错误 ENXIO(即“没有这样的设备或地址”)。

我试图理解为什么会发生这种情况,我感觉这与 init 中的 setsid() 系统调用有关,但我无法重现确切的场景,因此我无法提供适当的测试用例。

对我来说真正奇怪的是,不仅 init(一个分叉进程,因此是 shell 的子进程)从当前终端分离,而且直到 GUI 终端的所有进程层次结构都从 tty 分离同样,我似乎无法弄清楚它是如何发生的。

另外,不同命令之间还有一些不一致的地方:

➜  namespaces  tty
/dev/pts/19
➜  namespaces  sudo -s
sudo: no tty present and no askpass program specified
➜  namespaces  ls -l /proc/$$/fd
total 0
lrwx------ 1 paris paris 64 gen 15 23:24 0 -> /dev/pts/19
lrwx------ 1 paris paris 64 gen 15 23:24 1 -> /dev/pts/19
lrwx------ 1 paris paris 64 gen 15 23:24 10 -> /dev/pts/19
lrwx------ 1 paris paris 64 gen 15 23:24 2 -> /dev/pts/19
➜  namespaces  

非常感谢任何有关这种情况的线索。

编辑:查看“/dev/tty”的内核源代码我认为问题与内核端char设备的引用计数有关。实际上,为了将“/dev/{console|tty0|tty1}”输出重定向到当前 pty,我将 shell 的控制终端绑定到容器的已安装 dev 中的那些设备文件。

EDIT:好像是在Linux内核的“tty_open_current_tty()”函数中这一步报错:

static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
     struct tty_struct *tty;
     int retval;

     if (device != MKDEV(TTYAUX_MAJOR, 0))
         return NULL;

     tty = get_current_tty();
     if (!tty)
         return ERR_PTR(-ENXIO);
     ...
}

编辑:问题似乎与“窃取”控制 tty 的概念有关。这可以在CAP_SYS_ADMIN 功能下完成,并使用TIOCSCTTY 作为命令和1 作为参数在tty 文件描述符上调用ioctl()(请参阅tty_ioctl(4))。我将尝试编写一个测试用例来确认这一点并报告回来。

【问题讨论】:

    标签: linux process tty linux-namespaces


    【解决方案1】:

    我认为您对setsid() 的预感可能很接近。在setsid() 调用附近是否有fork() 调用?因为将进程转变为守护进程的常用技术是:

    • fork()
    • setsid()
    • 再次fork() 以确保一切都已分离

    编辑

    相关函数的源代码(init_main):http://svn.savannah.nongnu.org/viewvc/sysvinit/trunk/src/init.c?root=sysvinit&view=markup

    它具有重复多次的一般forksetsidfork 模式。这将确保与 tty 分离。

    【讨论】:

    • 是的,看起来代码流是:init_mainfork()setsid()spawn()fork()setsid()fork()...很多分叉和setsid,很可能确保与tty分离。
    • 是的,但我不明白为什么父级也被分离了。
    【解决方案2】:

    好的,我可以成功追查到问题了。

    请参阅this gist 了解工作代码示例(将/dev/pts/17 替换为tty 命令的输出)。

    问题与sysvinitspawn()函数中的以下步骤有关:

    (void)ioctl(f, TIOCSCTTY, 1);
    

    ioctl() 实际上是在窃取控制终端 /dev/console,在我的例子中,它是当前进程的 pty 的绑定挂载。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-19
      • 2015-10-08
      • 1970-01-01
      • 1970-01-01
      • 2015-04-12
      • 1970-01-01
      • 2012-05-05
      • 1970-01-01
      相关资源
      最近更新 更多