基本概念
终端和控制台都不是个人电脑的概念,而是多人共用的小中大型计算机上的概念。终端为主机提供了人机接口,一台主机可以连很多物理终端,每个人都通过终端使用主机的资源。终端有字符终端(RS-232串口)和图形终端两种。控制台是另一种人机接口, 不通过终端与主机相连,而是通过显示卡-显示器和键盘接口分别与主机相连,这是人控制主机的第一人机接口。
现在的个人计算机一般只有一个控制台(键盘加上显示器),没有终端,当然愿意的话,可以在串口上连一两台物理终端。但是Linux按POSIX标准把个人计算机当成小型机来用,在控制台上通过getty软件虚拟了六个字符终端(或者叫虚拟终端tty1-tty6,数量可以在/etc/inittab里自己调整)和一个图型终端。在虚拟图形终端中又可以通过软件(如screen,tmux)再虚拟无限多个伪终端。但这全是虚拟的,虽然用起来一样,但实际上没有物理实体。所以在个人计算机上,只有一个实际的控制台,没有终端,所有终端都是在控制台上用软件模拟的。虚拟终端不是实际的物理终端设备,之所以称为虚拟是因为可以在一个物理图形终端(键盘加上显示器)上同时运行若干个虚拟终端。虚拟终端也称为虚拟控制台(virtual console)。
Linux操作系统在启动的时候支持6个虚拟终端,内核支持64个,用户用Alt+F1 ~ F6进行切换,Alt+F7将切换到图形界面环境。一旦进入了图形桌面环境,可以通过Ctrl+Alt+Fx切回到字符界面的虚拟终端。
控制台
/dev/console 是系统控制台,是与操作系统交互的设备。系统所产生的信息会发送到该设备上。PC的显示器和键盘其实就是控制台。目前只有在单用户模式下,才允许用户登录控制台。控制台有缓冲的概念,为内核提供打印输出。内核把要打印的内容装入缓冲区,然后由控制台来决定打印到哪里。在Linux系统中,我们一般将系统控制台设备设定为其中一个虚拟终端(一般是当前的virtual terminal,也就是/dev/tty0),这样,我们可以在某一个virtual terminal上看到系统的日志。在嵌入式的环境中,我们一般会将系统控制台设备设定为串口终端或者usb串口终端。可以通过kernel启动的command line来设定系统控制台,例如
root=/dev/mtdblock0 console=ttyS2,115200 mem=64M
虚拟终端
/dev/ttyn 是虚拟终端,他们共享同一个真实的物理控制台。用户可以使用 Alt+Fn 切换控制台,看起来感觉存在多个屏幕,这种虚拟终端对应tty1~n。/dev/tty0 代表当前虚拟终端,不管当前正在使用哪个虚拟终端(注意:这里是虚拟终端,不包括伪终端),系统信息都会发送到/dev/tty0上。只有系统或超级用户root可以向/dev/tty0进行写操作。tty0是系统自动打开的,但不用于用户登录。如果在进程里打开一个这样的文件且该文件不是其他进程的虚拟终端时,那该文件就是这个进程的虚拟终端。进程printf数据会输出到这里。
如果当前在图形界面,按Ctrl+Alt+F3切换到3号虚拟终端,sudo sh -c 'echo 1111 > /dev/tty0' 会在当前虚拟终端输出 1111。如果使用图形界面下面的伪终端, echo 1111 > /dev/tty 也会输出 1111
控制终端
当会话创建的时候,没有控制终端。当会话首进程打开了一个不是其它会话控制终端的终端时,控制终端建立。fork和exec会保留原来的控制终端。如果当前进程有控制终端,那么/dev/tty就是控制终端的设备文件。
对于你登录的shell,/dev/tty就是你使用的控制台,设备号是(5,0)。不过它并不指任何物理意义上的控制台,/dev/tty会映射到当前设备。输出到/dev/tty的内容只会显示在当前工作终端上。
如果在虚拟的字符终端(包含远程登录)下面,那么/dev/tty就是映射到/dev/ttyn,取决于你当前的虚拟终端号。如果是在虚拟的图形终端下面,/dev/tty 映射到的是/dev/pts/n 伪终端上。
可以输入命令tty,将显示当前映射终端如:/dev/tty1或者/dev/pts/0等。也可以使用命令 ps -ax 来查看其他进程与哪个控制终端相连。
/dev/tty文件有一个特殊的用法:
当标准输入stdin和标准输出stdout被重定向时,我们仍然可以通过/dev/tty文件而实现对键盘的读取和对显示器的输出!
char *ctermid(char *s) 函数可以获取控制终端的名称,对于Linux一般就是 /dev/tty
伪终端
伪终端(Pseudo Terminal)是终端的发展,为满足现在需求(比如网络登陆、xwindow窗口的管理)。它是成对出现的逻辑终端设备(即master和slave设备,对master的操作会反映到slave上)。它多用于模拟终端程序,是远程登陆(telnet、ssh、xterm等)后创建的控制台设备。
历史上,有两套伪终端软件接口:
- BSD接口:较简单,master为/dev/pty[p-za-e][0-9a-f];slave为/dev/tty[p-za-e][0-9a-f],它们都是配对的出现的。例如/dev/ptyp3和/dev/ttyp3。但由于在编程时要找到一个合适的终端需要逐个尝试,所以逐渐被放弃。
- Unix 98接口:使用一个/dev/ptmx作为master设备,在每次打开操作时会得到一个master设备fd,并在/dev/pts/目录下得到一个slave设备(如/dev/pts/3),这样就避免了逐个尝试的麻烦。由于可能有好几千个用户登陆,所以/dev/pts/*是动态生成的。第一个用户登陆,设备文件为/dev/pts/0,第二个为/dev/pts/1,以此类推。它们并不与实际物理设备直接相关。现在大多数系统是通过此接口实现pty
伪终端的使用方法:
- 进程A打开伪终端主设备 /dev/ptmx (int posix_openpt(int flags);)
- 进程A fork 出一个子进程B
- B进程做如下工作
- 调用 setsid 创建新的session (此时B进程失去控制终端)
- 通过ptsname获取进程A打开的伪终端主设备所对应的伪终端从设备名称 /dev/pts/n
- 打开伪终端从设备 /dev/pts/n ,使之成为会话B的控制终端)
- 把/dev/pts/n作为标准输入输出错误
- exe执行面向伪终端从设备的程序
进程A和进程B此时可以通过伪终端主从设备通信。
配置tty
# stty
列出系统支持的所有终端类型
# toe -a
当前存在的不同 tty 驱动的列表, 显示驱动的名子, 缺省的节点名子, 驱动的主编号, 次编号范围, 以及 tty 驱动的类型
# cat /proc/tty/drivers
当前bash关联到了哪个tty
# tty
tty被哪些进程打开
# lsof /dev/pts/1
$ ls /dev/tty* -al
crw-rw-rw- 1 root tty 5, 0 Nov 28 17:45 /dev/tty
crw--w---- 1 root tty 4, 0 Nov 28 17:19 /dev/tty0
crw--w---- 1 root tty 4, 1 Nov 27 17:03 /dev/tty1
crw--w---- 1 root tty 4, 10 Nov 27 17:03 /dev/tty10
crw--w---- 1 root tty 4, 11 Nov 27 17:03 /dev/tty11
crw--w---- 1 root tty 4, 12 Nov 27 17:03 /dev/tty12
crw--w---- 1 root tty 4, 13 Nov 27 17:03 /dev/tty13
crw--w---- 1 root tty 4, 14 Nov 27 17:03 /dev/tty14
crw--w---- 1 root tty 4, 15 Nov 27 17:03 /dev/tty15
crw--w---- 1 root tty 4, 16 Nov 27 17:03 /dev/tty16
crw--w---- 1 root tty 4, 17 Nov 27 17:03 /dev/tty17
crw--w---- 1 root tty 4, 18 Nov 27 17:03 /dev/tty18
crw--w---- 1 root tty 4, 19 Nov 27 17:03 /dev/tty19
crw--w---- 1 root tty 4, 2 Nov 27 17:03 /dev/tty2
crw--w---- 1 root tty 4, 20 Nov 27 17:03 /dev/tty20
crw--w---- 1 root tty 4, 21 Nov 27 17:03 /dev/tty21
crw--w---- 1 root tty 4, 22 Nov 27 17:03 /dev/tty22
crw--w---- 1 root tty 4, 23 Nov 27 17:03 /dev/tty23
crw--w---- 1 root tty 4, 24 Nov 27 17:03 /dev/tty24
crw--w---- 1 root tty 4, 25 Nov 27 17:03 /dev/tty25
crw--w---- 1 root tty 4, 26 Nov 27 17:03 /dev/tty26
crw--w---- 1 root tty 4, 27 Nov 27 17:03 /dev/tty27
crw--w---- 1 root tty 4, 28 Nov 27 17:03 /dev/tty28
crw--w---- 1 root tty 4, 29 Nov 27 17:03 /dev/tty29
crw--w---- 1 root tty 4, 3 Nov 28 17:14 /dev/tty3
crw--w---- 1 root tty 4, 30 Nov 27 17:03 /dev/tty30
crw--w---- 1 root tty 4, 31 Nov 27 17:03 /dev/tty31
crw--w---- 1 root tty 4, 32 Nov 27 17:03 /dev/tty32
crw--w---- 1 root tty 4, 33 Nov 27 17:03 /dev/tty33
crw--w---- 1 root tty 4, 34 Nov 27 17:03 /dev/tty34
crw--w---- 1 root tty 4, 35 Nov 27 17:03 /dev/tty35
crw--w---- 1 root tty 4, 36 Nov 27 17:03 /dev/tty36
crw--w---- 1 root tty 4, 37 Nov 27 17:03 /dev/tty37
crw--w---- 1 root tty 4, 38 Nov 27 17:03 /dev/tty38
crw--w---- 1 root tty 4, 39 Nov 27 17:03 /dev/tty39
crw--w---- 1 root tty 4, 4 Nov 27 17:03 /dev/tty4
crw--w---- 1 root tty 4, 40 Nov 27 17:03 /dev/tty40
crw--w---- 1 root tty 4, 41 Nov 27 17:03 /dev/tty41
crw--w---- 1 root tty 4, 42 Nov 27 17:03 /dev/tty42
crw--w---- 1 root tty 4, 43 Nov 27 17:03 /dev/tty43
crw--w---- 1 root tty 4, 44 Nov 27 17:03 /dev/tty44
crw--w---- 1 root tty 4, 45 Nov 27 17:03 /dev/tty45
crw--w---- 1 root tty 4, 46 Nov 27 17:03 /dev/tty46
crw--w---- 1 root tty 4, 47 Nov 27 17:03 /dev/tty47
crw--w---- 1 root tty 4, 48 Nov 27 17:03 /dev/tty48
crw--w---- 1 root tty 4, 49 Nov 27 17:03 /dev/tty49
crw--w---- 1 root tty 4, 5 Nov 27 17:03 /dev/tty5
crw--w---- 1 root tty 4, 50 Nov 27 17:03 /dev/tty50
crw--w---- 1 root tty 4, 51 Nov 27 17:03 /dev/tty51
crw--w---- 1 root tty 4, 52 Nov 27 17:03 /dev/tty52
crw--w---- 1 root tty 4, 53 Nov 27 17:03 /dev/tty53
crw--w---- 1 root tty 4, 54 Nov 27 17:03 /dev/tty54
crw--w---- 1 root tty 4, 55 Nov 27 17:03 /dev/tty55
crw--w---- 1 root tty 4, 56 Nov 27 17:03 /dev/tty56
crw--w---- 1 root tty 4, 57 Nov 27 17:03 /dev/tty57
crw--w---- 1 root tty 4, 58 Nov 27 17:03 /dev/tty58
crw--w---- 1 root tty 4, 59 Nov 27 17:03 /dev/tty59
crw--w---- 1 root tty 4, 6 Nov 27 17:03 /dev/tty6
crw--w---- 1 root tty 4, 60 Nov 27 17:03 /dev/tty60
crw--w---- 1 root tty 4, 61 Nov 27 17:03 /dev/tty61
crw--w---- 1 root tty 4, 62 Nov 27 17:03 /dev/tty62
crw--w---- 1 root tty 4, 63 Nov 27 17:03 /dev/tty63
crw--w---- 1 root tty 4, 7 Nov 27 17:03 /dev/tty7
crw--w---- 1 root tty 4, 8 Nov 27 17:03 /dev/tty8
crw--w---- 1 root tty 4, 9 Nov 27 17:03 /dev/tty9
crw------- 1 root root 5, 3 Nov 27 17:03 /dev/ttyprintk
crw-rw---- 1 root dialout 4, 64 Nov 27 17:03 /dev/ttyS0
crw-rw---- 1 root dialout 4, 65 Nov 27 17:03 /dev/ttyS1
crw-rw---- 1 root dialout 4, 74 Nov 27 17:03 /dev/ttyS10
crw-rw---- 1 root dialout 4, 75 Nov 27 17:03 /dev/ttyS11
crw-rw---- 1 root dialout 4, 76 Nov 27 17:03 /dev/ttyS12
crw-rw---- 1 root dialout 4, 77 Nov 27 17:03 /dev/ttyS13
crw-rw---- 1 root dialout 4, 78 Nov 27 17:03 /dev/ttyS14
crw-rw---- 1 root dialout 4, 79 Nov 27 17:03 /dev/ttyS15
crw-rw---- 1 root dialout 4, 80 Nov 27 17:03 /dev/ttyS16
crw-rw---- 1 root dialout 4, 81 Nov 27 17:03 /dev/ttyS17
crw-rw---- 1 root dialout 4, 82 Nov 27 17:03 /dev/ttyS18
crw-rw---- 1 root dialout 4, 83 Nov 27 17:03 /dev/ttyS19
crw-rw---- 1 root dialout 4, 66 Nov 27 17:03 /dev/ttyS2
crw-rw---- 1 root dialout 4, 84 Nov 27 17:03 /dev/ttyS20
crw-rw---- 1 root dialout 4, 85 Nov 27 17:03 /dev/ttyS21
crw-rw---- 1 root dialout 4, 86 Nov 27 17:03 /dev/ttyS22
crw-rw---- 1 root dialout 4, 87 Nov 27 17:03 /dev/ttyS23
crw-rw---- 1 root dialout 4, 88 Nov 27 17:03 /dev/ttyS24
crw-rw---- 1 root dialout 4, 89 Nov 27 17:03 /dev/ttyS25
crw-rw---- 1 root dialout 4, 90 Nov 27 17:03 /dev/ttyS26
crw-rw---- 1 root dialout 4, 91 Nov 27 17:03 /dev/ttyS27
crw-rw---- 1 root dialout 4, 92 Nov 27 17:03 /dev/ttyS28
crw-rw---- 1 root dialout 4, 93 Nov 27 17:03 /dev/ttyS29
crw-rw---- 1 root dialout 4, 67 Nov 27 17:03 /dev/ttyS3
crw-rw---- 1 root dialout 4, 94 Nov 27 17:03 /dev/ttyS30
crw-rw---- 1 root dialout 4, 95 Nov 27 17:03 /dev/ttyS31
crw-rw---- 1 root dialout 4, 68 Nov 27 17:03 /dev/ttyS4
crw-rw---- 1 root dialout 4, 69 Nov 27 17:03 /dev/ttyS5
crw-rw---- 1 root dialout 4, 70 Nov 27 17:03 /dev/ttyS6
crw-rw---- 1 root dialout 4, 71 Nov 27 17:03 /dev/ttyS7
crw-rw---- 1 root dialout 4, 72 Nov 27 17:03 /dev/ttyS8
crw-rw---- 1 root dialout 4, 73 Nov 27 17:03 /dev/ttyS9
$ ls /dev/ptmx -al
crw-rw-rw- 1 root tty 5, 2 Nov 29 11:35 /dev/ptmx
$ ls /dev/pts/* -al
crw--w---- 1 root tty 136, 0 Nov 28 17:03 /dev/pts/0
crw--w---- 1 root tty 136, 1 Nov 29 11:35 /dev/pts/1
crw--w---- 1 root tty 136, 10 Nov 28 11:07 /dev/pts/10
crw--w---- 1 root tty 136, 12 Nov 27 17:04 /dev/pts/12
crw--w---- 1 root tty 136, 13 Nov 27 17:04 /dev/pts/13
crw--w---- 1 root tty 136, 14 Nov 27 17:04 /dev/pts/14
crw--w---- 1 root tty 136, 15 Nov 29 11:33 /dev/pts/15
crw--w---- 1 root tty 136, 16 Nov 27 17:04 /dev/pts/16
crw--w---- 1 root tty 136, 17 Nov 27 17:04 /dev/pts/17
crw--w---- 1 root tty 136, 18 Nov 27 17:04 /dev/pts/18
crw--w---- 1 root tty 136, 2 Nov 28 16:58 /dev/pts/2
crw--w---- 1 root tty 136, 3 Nov 28 19:09 /dev/pts/3
crw--w---- 1 root tty 136, 4 Nov 28 17:03 /dev/pts/4
crw--w---- 1 root tty 136, 6 Nov 29 11:33 /dev/pts/6
crw--w---- 1 root tty 136, 7 Nov 29 11:21 /dev/pts/7
crw--w---- 1 root tty 136, 8 Nov 28 17:24 /dev/pts/8
crw--w---- 1 root tty 136, 9 Nov 27 17:04 /dev/pts/9
c--------- 1 root root 5, 2 Nov 27 17:03 /dev/pts/ptmx