在Linux操作系统启动时,首先加载的进程就是init进程(ID为1),其余进程都是init进程产生的(fork,然后exec金蝉脱壳),因此系统中所有进程都可以看成是init进程的子孙进程。可以通过ps ajx命令查看任意进程的父进程,进行追溯可以发现,任意进程的开头都是init进程,init进程起源于0号进程,0号进程实际不存在。
文件与I/O中讲过,每个进程都可以通过一个特殊的设备文件/dev/tty访问它的控制终端。事实上每个终端设备都对应一个不同的设备文件(伪文件,不占用磁盘空间),/dev/tty提供了一个通用的接口,一个进程要访问它的控制终端既可以通过/dev/tty也可以通过该终端设备所对应的设备文件来访问。ttyname函数可以由文件描述符查出对应的文件名,该文件描述符必须指向一个终端设备而不能是任意文件。
终端的启动过程。简单来说,一个Linux系统启动,大致经历如下的步骤: init --> fork --> exec --> getty --> 用户输入帐号 --> login --> 输入密码 --> exec --> bash(文字终端)。init进程通过fork产生子进程,子进程调用exec函数族成为getty进程,即文字终端登陆界面,登陆成功后再调用exec函数族成为bash终端进程,在该终端中产生的任意子进程的控制终端都是该终端。对于设备终端,init进程fork子进程后,exec提供登陆界面,再exec成为桌面终端,再exec成为设备终端(伪终端)。
计算机的整个架构:计算机硬件→硬件驱动程序→系统调用→应用层,数据流的流向也是如此,对于输入则为硬件流向应用层(用户空间),对于输出则相反。硬件驱动程序负责读写实际的硬件设备(包括各种文件,磁盘),比如从键盘读入字符和把字符输出到显示器,线路规程(line disciline)像一个过滤器(用来过滤键盘输入的内容),对于某些特殊字符并不是让它直接通过,而是做特殊处理,比如在键盘上按下Ctrl-z,对应的字符并不会被用户程序的read读到,而是被线路规程截获,解释成SIGTSTP信号发给前台进程,通常会使该进程停止。线路规程应该过滤哪些字符和做哪些特殊处理是可以配置的。
ttyname函数
由文件描述符查出对应的文件名,该文件描述符必须指向一个终端设备而不能是任意文件。
char *ttyname(int fd); 成功:终端名;失败:NULL,设置errno
//下面我们借助ttyname函数,通过实验看一下各种不同的终端所对应的设备文件名
#include <unistd.h>
#include <stdio.h>
int main(void)
{printf("fd 0: %s\n", ttyname(0));printf("fd 1: %s\n", ttyname(1));printf("fd 2: %s\n", ttyname(2));return 0;
}
[root@localhost 01_session_daemon_test]# ./ttyname
fd 0: /dev/pts/2
fd 1: /dev/pts/2
fd 2: /dev/pts/2 //可以看见标准输入、输出和错误输出对应的终端为pts/2