我试图重新编写UNIX命令script
(就像它在OSX上一样)。这是学校帮助学生学习UNIX API的练习的一部分。我们只允许使用系统调用,更具体地说,只允许使用Mac OSX上的MAN(2)页面上的那些(因为那是我们在学校的操作系统)。
我有第一个版本'那种作品。运行ls
等程序会将正确的输出打印到屏幕和输出文件中。
问题场景
我从bash
克隆中运行script
。第一个问题是我收到以下错误:
bash: no job control in this shell
我尝试使用bash
和setpgrp
强制setpgid
进程进入前台,但是没有改变任何内容,所以我认为这不是问题。
我还试图理解为什么真正的script
命令使用cfmakeraw
(至少在Linux上),如here所示,但我没有得到它。 MAN页面不是很有帮助。
script
上的真实dup2
也slave
个STDIN,如here所示,但是当我这样做时,似乎输入无法读取了。
但是,bash仍在运行,我可以在其中执行命令。
但是如果我在其中运行vim
,然后点击Ctrl-Z将vim
放到后台,终端就会搞砸(当我在我的时候不会发生这种情况常规终端)。
所以我想我一定做错了。我很感激任何建议/帮助。
您可以通过执行以下操作进行编译:{{1}}(它建立在OSX 10.9上,希望在Linux上也是如此)
执行:make
不知道在StackOverflow中拥有所有源代码更有意义,因为它会用它来挤占页面。如果需要,我可以用源替换Git链接。
答案 0 :(得分:2)
我不使用OS X,因此我无法直接测试您的代码,但我目前正在编写玩具终端模拟器并遇到类似问题。
关于“bash:此shell中没有作业控制”
为了执行作业控制,shell需要是会话负责人和其终端的控制进程。默认情况下,您的程序会继承您自己的shell的控制终端,该终端运行您的script
程序,并且也是会话负责人。以下是如何在fork
之后使新的从属进程成为会话负责人:
/* we don't need the inherited master fd */
close(master);
/* discard the previous controlling tty */
ioctl(0, TIOCNOTTY, 0);
/* replace existing stdin/out/err with the slave pts */
dup2(slave, 0);
dup2(slave, 1);
dup2(slave, 2);
/* discard the extra file descriptor for the slave pts */
close(slave);
/* make the pts our controlling terminal */
ioctl(0, TIOCSCTTY, 0);
/* make a new session */
setsid()
此时,分叉进程已将stdin / out / err绑定到新pts,pts成为其控制终端,并且该进程是会话负责人。工作控制现在应该有效。
关于原始tty
当您在普通终端内运行程序时,它看起来像这样:
(term emulator, master side) <=> /dev/pts/42 <=> (program, slave side)
如果按^Z
,终端模拟器会将ascii字符0x1A
写入pts。它是一个控制字符,因此它不会被发送到程序,而是内核会向程序发出SIGSTP
并暂停它。将字符转换为其他字符的过程称为“行烹饪”,并具有可针对每个tty进行调整的各种设置。
现在让我们看一下script
的情况:
term emulator <=> /dev/pts/42 <=> script <=> /dev/pts/43 <=> program
使用普通线路设置,当您按^Z
时会发生什么?它将被SIGSTP
转换为/dev/pts/42
,script
将被暂停。但这不是我们想要的,而是我们希望0x1A
生成的^Z
字符按原样/dev/pts/42
生成,然后由script
传递给{ {1}}然后才转换为/dev/pts/43
以暂停程序。
这就是为什么你的终端和脚本之间的pts必须配置为“raw”的原因,这样所有控制字符都会到达SIGSTP
和程序之间的pts,就像你直接使用它一样。