Linux 终端与进程

本文详细解析了Linux中的终端、控制台和TTY的概念,以及它们与进程的交互关系。重点介绍了伪终端和内核终端模拟器,以及如何通过pty实现进程间的通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有趣的问题

Linux 中的 终端,控制台,TTY,PTY 究竟是什么?它们与进程有什么关系?

历史回顾:控制台 (Console)

控制台是一个直接控制设备的面板 (属于设备的一部分)

计算机设备的控制台:按键 & 指示灯 (键盘 & 显示器)

早期的电子计算机必然有一个控制台

历史回顾:终端 (Terminal)

终端是一台独立于计算机的机器,是能够和计算机进行交互的设备

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

以前 TTY 是电传打字机;现在 TTY 已经成为一种标准,其他终端想要和计算机进行交互,就需要遵守 TTY 标准

历史发展进程

电传打字机已经淘汰

计算机上的输入设备和显示设备从主机独立出来

控制台与终端的物理表现形式逐渐趋近

计算机开始支持多任务处理

终端与进程

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

用户进程通过系统调用访问 TTY 设备来获取输入、展示输出;而 TTY 设备运行于内核模式,可以与外设进行交互,而外设为了成功的被进程使用,就需要在驱动层面满足 TTY 标准

各种终端类型

Linux 有两种工作界面模式,一种是图形界面模式,使用的终端是伪终端;另一种是 命令行(文本)模式,使用的终端是虚拟终端

内核终端模拟器

伪终端模型

伪终端 (gnome-terminal)

gnome-terminal 是一个进程,对接 PTY 主设备,可以操控键盘和显卡,bash 也是一个进程,是 shell 的一种类型,对接 PTY 从设备;主设备发送数据后,从设备可以接收到主设备发送的消息,从设备发送数据后,主设备也可以接收到从设备发送的消息,这是一种进程间通信的方法

当我们在命令行中输入 ls 后,bash 去执行 ls 后得到结果,然后将结果发送给 gnome-terminal,gnome-terminal 接收到结果后,会去操控显卡,在命令行中显示结果,这就是使用图形界面的命令行

我们通过 pstree 来查看图形化界面命令行中的进程树,可以看出图形化界面命令行使用的是伪终端

虚拟终端和伪终端的工作模式有较大的差别。但对于shell 进程而说,虚拟终端和伪终端没什么区别,它们都是 tty 设备

伪终端程序设计 (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>

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);   // show on screen
                write(master, txbuf, strlen(txbuf));   // keyboard input
                i = 0;
            }
            else
            {
                rxbuf[i++] = rx;
            }
        }
        
        close(master);
    }
    else
    {
        printf("create pty error...\n");
    }
    
    return 0;
}

slave.c


#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.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);
    }
    
    
    return 0;
}

程序运行结果如下图所示:

通过打印可以看出,PTY 主从设备可以收到对方发过来的消息

思考

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值