《Linux系統程式設計訓練營》8_Linux 終端與程式

發表於2023-09-25

詳解控制檯與終端

有趣的問題:Linux 的終端,控制檯,TTY, PTY 究竟是什麼?它們與程式有什麼關係?

歷史回顧:控制檯

  • 控制檯是一個直接控制裝置的皮膚(屬於裝置的一部分)
  • 計算機裝置的控制檯:按鍵 & 指示燈 (鍵盤 & 顯示器)
  • 早期的電子計算機必然有一個控制檯

image.png

歷史回顧:終端 (Terminal)

  • 終端是一臺獨立於計算機的機器,是能夠用來和計算機進行互動的裝置

image.png

TTY -- 即:TeleType Writer 電傳打字機,一種終端裝置

歷史發展程式。。。

  • 電傳打字機已經淘汰
  • 計算機上的輸入裝置和顯示裝置從主機獨立出來
  • 控制檯與終端的物理表現形式逐漸趨近
  • 計算機開始支援多工處理
  • 。。。

image.png

終端與程式

  • TTY 演變為 Linux 中的抽象概念,對於程式而言,TTY是一種輸入輸出裝置

image.png

虛擬終端與偽終端

各種終端型別

型別說明
虛擬終端(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(從), ...

核心終端模擬器

image.png

偽終端模型

image.png

偽終端(gnome-terminal)

image.png

偽終端
tiansong@tiansong:~$ pstree -A -p -s $$
systemd(1)---systemd(968)---gnome-terminal(1885)---bash(1973)---pstree(2052)
虛擬終端
ubuntu 由 GUI 切換到 CLI : CTRL + ALT +F3

tiansong@tiansong:~$ pstree -A -p -s $$
systemd(1)---login(2064)---bash(2118)---pstree(2125)

ubuntu 由 CLI 切換到 GUI : CTRL + ALT +F1

偽終端程式設計原理

image.png

站在 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.out
tiansong@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.out
tiansong@tiansong:~/Desktop/linux$ ./slave.out /dev/pts/0  // 新終端執行
Read: from slave: D.T.Software

終端必然與程式關聯才有意義!那麼,程式之間除了父子關係,是否還有其它關係?

相關文章