每个进程可以有控制终端,通常从交互式shell启动的程序,它的标准输出和标准错误输出到控制终端,它的标准输入从控制终端读取输入。
控制终端收到CTRL+C等按键时,会发送信号给控制终端前的端进程组(foreground process group)。
注意是发给group,因为前端进程可能有多个,比如 echo 123 | hexdump
再终端启动一个交互式shell
$ sh -i
它会打开/dev/tty这个虚拟的字符设备(表示当前进程的控制终端)。
修改自己的组id为自己的pid。然后调用TIOCSPGRP ioctl把自己的组设置成前端进程组。
strace记录如下:
getpid() = 1090
getppid() = 1087
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_LARGEFILE) = 3
fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10
close(3) = 0
ioctl(10, TIOCGPGRP, [1087]) = 0
getpgrp() = 1087
setpgid(0, 1090) = 0
ioctl(10, TIOCSPGRP, [1090]) = 0
进程的标准输入输出可以关闭、重定向等,但是这不影响进程的控制终端。进程仍然可以获取并打开控制终端。
通过ctermid() 接口,可以获得进程控制终端的路径。这个接口通常是返回/dev/tty.
另外ttyname()接口,可以返回一个终端fd的路径。
printf("stdout tty: %s\n", ttyname(1));
只要open一个终端,不指定O_NOCTTY选项即可。
要关闭一个进程的控制终端,可以
1 打开控制终端,然后调用 TIOCNOTTY ioctl
int fd = open(ctermid(NULL), O_RDWR);
ioctl(fd, TIOCNOTTY);
2 调用setsid(),这个接口会创建一个新的session,并且没有控制终端。
注意 process group leader不能调用setsid,会返回-1。因为setsid会创建新session,且把调用者变成新leader,且新process group id等于调用者的pid。因此如果本身就是leader。那么一个进程组中的进程可能跨越多个session。