详解控制台与终端

乏味的问题:Linux 的终端,控制台,TTY, PTY 到底是什么?它们与过程有什么关系?

历史回顾:控制台

  • 控制台是一个间接管制设施的面板(属于设施的一部分)
  • 计算机设备的控制台:按键 & 指示灯 (键盘 & 显示器)
  • 晚期的电子计算机必然有一个控制台

历史回顾:终端 (Terminal)

  • 终端是一台独立于计算机的机器,是可能用来和计算机进行交互的设施

TTY -- 即:TeleType Writer 电传打字机,一种终端设备

历史倒退过程。。。

  • 电传打字机曾经淘汰
  • 计算机上的输出设施和显示设施从主机独立进去
  • 控制台与终端的物理表现形式逐步趋近
  • 计算机开始反对多任务处理
  • 。。。

终端与过程

  • TTY 演变为 Linux 中的抽象概念,对于过程而言,TTY是一种输入输出设施

虚构终端与伪终端

各种终端类型

类型阐明
虚构终端(Virtual Terminal)将这一套键盘和显示器映射为 6 个终端设备/dev/tty1~tty6 tty0指代以后应用的终端 (CLI虚构出)
串口终端(Serial Port Terminal)将连贯到窗口的外设看看作终端设备/dev/ttyS1,...
终端模拟器(Terminal Emulator)终端模拟程序(狭义) / 内核模仿模块(侠义)Putty, MobaXTERM, 内核模块,伪终端
伪终端(Pseudo Terminal)运行在用户模式的终端模拟程序,分为主设施(pty master)和从设施(pty slave)/dev/ptmx(主), /dev/pts/3(从), ...

内核终端模拟器

伪终端模型

伪终端(gnome-terminal)

伪终端
tiansong@tiansong:~$ pstree -A -p -s $$systemd(1)---systemd(968)---gnome-terminal(1885)---bash(1973)---pstree(2052)
虚构终端
ubuntu 由 GUI 切换到 CLI : CTRL + ALT +F3tiansong@tiansong:~$ pstree -A -p -s $$systemd(1)---login(2064)---bash(2118)---pstree(2125)ubuntu 由 CLI 切换到 GUI : CTRL + ALT +F1

伪终端程序设计原理

站在 shell 角度的总结:1. shell 面对的只有 TTY 设施,而 TTY 是 Linux 中的抽象概念,因而须要一个具体的模块来反对这个形象的概念2. 对于虚构终端,反对这个概念的模块就是终端模拟器(内核模块,运行于内核模式),能够间接应用键盘、显卡驱动进行输入输出3. 对于伪终端,创立主、从设施,其中 shell 过程对接的是从设施,主设施对接用户过程 gnome-terminal (实质是 GUI 应用程序)

伪终端程序设计(master)

  • 创立 PTY 主从设施: master = posix_openpt(O_RDWR);
  • 获取主设施权限:

    • grantpt(master); // 获取设施应用权限
    • unlockpt(master); // 解锁设施,为读写做筹备
  • 读写主设施

    • c = read(master, &rx, 1);
    • len = write(master, txbuf, strlen(txbuf));

伪终端程序设计(slave)

  • 关上 PTY 从设施,slave = open(path_to_slave, O_RDWR);
  • 读写设施:

    • write(slave, "Delphi\r", 7);
    • read(slave, buf, sizeof(buf) - 1);

实战伪终端程序设计

master.c
#define _XOPEN_SOURCE  600#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <string.h>#include <unistd.h>#include <fcntl.h>int main(){    char rx = 0;    char rxbuf[128] = {0};    char txbuf[256] = {0};    int master = 0;    int c = 0;    int i = 0;    master = posix_openpt(O_RDWR);                         // 连贯主设施,等同于 gnome-terminal    if (master > 0) {        grantpt(master);        unlockpt(master);        printf("Slave: %s\n", ptsname(master));        while ((c = read(master, &rx, 1)) == 1) {            if (rx == '\r') {                rxbuf[i] = 0;                sprintf(txbuf, "from slave: %s\r", rxbuf);  // 等同于屏幕输入                write(master, txbuf, strlen(txbuf));        // 等同于键盘输入            } else {                rxbuf[i++] = rx;            }        }    }    else {        printf("create pty error...\n");    }    return 0;}
tiansong@tiansong:~/Desktop/linux$ gcc master.c -o master.outtiansong@tiansong:~/Desktop/linux$ ./master.out   // 不终止Slave: /dev/pts/0
slave.c
#define _XOPEN_SOURCE  600#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <string.h>#include <unistd.h>#include <fcntl.h>int main(int argc, char *argv[]){    int slave = open(argv[1], O_RDWR);  // 等同于 shell    if (slave > 0) {        char buf[256] = {0};        char *data = "D.T.Software\r";        int len = strlen(data);        write(slave, data, len);        sleep(1);        len = read(slave, buf, sizeof(buf) - 1);        buf[(len > 0) ? len : 0] = 0;        printf("Read: %s\n", buf);  // system(...)        close(slave);    }    else {    }    return 0;}
tiansong@tiansong:~/Desktop/linux$ gcc slave.c -o slave.outtiansong@tiansong:~/Desktop/linux$ ./slave.out /dev/pts/0  // 新终端执行Read: from slave: D.T.Software

终端必然与过程关联才有意义!那么,过程之间除了父子关系,是否还有其它关系?