Linux TTY子系统深度剖析

Linux TTY子系统深度剖析

1. TTY的历史渊源与现代演变

1.1 TTY名称的由来

TTY(Teletypewriter, 电传打字机)的历史可以追溯到19世纪的电报系统 . 早期的计算机系统使用物理的电传打字机 作为输入输出设备, 用户通过键盘输入, 打印机输出结果. 随着技术发展, 物理终端被视频终端取代, 但"TTY"这个名称一直保留下来

在现代Linux系统中, TTY已经演变为一个复杂的I/O子系统 , 负责管理用户空间与内核空间之间的字符流通信. 它不仅仅处理简单的字符传输, 还实现了行编辑, 会话管理, 作业控制等高级功能

1.2 TTY在Linux系统中的角色

Linux是一个多用户, 多任务 操作系统, TTY子系统是实现这一特性的基石. 每个用户登录会话都关联到一个TTY设备, 无论是物理控制台 , 虚拟终端 还是网络终端(通过SSH)

c 复制代码
// 查看当前系统中的TTY设备
$ ls -l /dev/tty*
crw-rw-rw- 1 root tty     5, 0  1月 1 00:00 /dev/tty
crw--w---- 1 root tty     4, 0  1月 1 00:00 /dev/tty0
crw--w---- 1 root tty     4, 1  1月 1 00:00 /dev/tty1
crw--w---- 1 root tty     4, 2  1月 1 00:00 /dev/tty2

2. TTY核心架构全景图

2.1 TTY子系统三层架构

TTY子系统采用经典的三层架构模型, 每一层都有特定的职责:
内核空间 - TTY子系统 TTY层 终端驱动层 硬件层 用户空间 read/write 系统调用 主设备端 分发 分发 分发 从设备端 帧缓冲 RS-232 物理控制台 串口硬件 空设备/null PTY驱动
伪终端 虚拟终端驱动
VT/VCON 串口驱动
UART/USB串口 TTY线路规程
行缓冲/特殊字符处理 应用程序
bash/vim/ssh等 终端模拟器
gnome-terminal/xterm等 GLIBC库
stdio函数

2.2 数据流向对比表

数据流向 描述 典型场景
输入流 键盘 → 驱动 → 线路规程 → 进程 用户输入命令
输出流 进程 → 线路规程 → 驱动 → 屏幕 程序输出结果
控制流 特殊字符 → 线路规程 → 信号/控制 Ctrl+C中断进程
回显流 输入字符 → 线路规程 → 输出流 密码输入隐藏

3. TTY核心概念深度解析

3.1 线路规程(Line Discipline)

线路规程 是TTY子系统的"大脑", 它定义了原始模式加工模式两种数据处理方式:

c 复制代码
// 内核中的线路规程数据结构(简化版)
struct tty_ldisc {
    int magic;
    char *name;
    int num;           // 线路规程编号
    int flags;
    
    // 核心操作函数指针
    int (*open)(struct tty_struct *);
    void (*close)(struct tty_struct *);
    void (*flush_buffer)(struct tty_struct *tty);
    ssize_t (*chars_in_buffer)(struct tty_struct *tty);
    ssize_t (*read)(struct tty_struct *tty, struct file *file,
                    unsigned char *buf, size_t nr);
    ssize_t (*write)(struct tty_struct *tty, struct file *file,
                     const unsigned char *buf, size_t nr);
    int (*ioctl)(struct tty_struct *tty, struct file *file,
                 unsigned int cmd, unsigned long arg);
    // ... 更多操作函数
};

// N_TTY线路规程(默认)的关键处理函数
static void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
{
    struct n_tty_data *ldata = tty->disc_data;
    
    // 特殊字符处理(Ctrl+C, Backspace等)
    if (tty->icanon && !L_EXTPROC(tty)) {
        if (c == '\r' || c == '\n') {
            // 行结束, 唤醒读取进程
            put_tty_queue(c, ldata);
            ldata->canon_head = ldata->read_head;
            kill_fasync(&tty->fasync, SIGIO, POLL_IN);
            wake_up_interruptible(&tty->read_wait);
            return;
        }
        
        if (c == tty->termios.c_cc[VERASE]) {
            // 处理退格键
            erase(tty);
            return;
        }
        
        if (c == tty->termios.c_cc[VINTR]) {
            // Ctrl+C中断信号
            n_tty_receive_break(tty);
            return;
        }
    }
    
    // 普通字符处理
    if (tty->echo && !L_ECHO(tty))
        echo_char(tty, c);
    
    put_tty_queue(c, ldata);
    console_conditional_schedule();
}

生活中的比喻 : 线路规程就像一个邮件分拣中心. 在加工模式下, 它等待完整的信封(一行文本), 检查邮政编码(特殊字符), 然后统一投递;而在原始模式下, 每个字母一到就立即投递, 不做任何处理

3.2 终端模式对比

模式类型 数据处理方式 缓冲机制 典型应用场景
加工模式 行缓冲, 特殊字符处理 行缓冲, 直到遇到换行符 交互式Shell, 命令行工具
原始模式 字符即时传递, 无处理 无缓冲或极小缓冲 vi编辑器, 串口通信, 游戏
半原始模式 部分字符处理 可配置的缓冲 特殊终端应用

3.3 伪终端(Pseudo Terminal)

伪终端是软件模拟的终端设备, 由一对设备文件组成:

  • PTY Master: 控制端, 由终端模拟器打开
  • PTY Slave: 被控端, 由应用程序打开

内核PTY驱动 伪终端对 (PTY Pair) 打开 打开 双向管道 read/write read/write pty驱动
管理pty pairs PTY Master
/dev/ptmx PTY Slave
/dev/pts/N 终端模拟器
xterm/gnome-terminal Shell/应用程序
/bin/bash

4. TTY核心数据结构与代码分析

4.1 TTY核心数据结构关系

使用 使用 使用 包含 包含 tty_struct +int magic +struct kref kref +struct device *dev +struct tty_driver *driver +const struct tty_operations *ops +struct tty_ldisc *ldisc +struct termios *termios +struct winsize winsize +unsigned char *write_buf +int write_cnt +struct tty_buffer *buf +int read_head +int read_tail +int canon_head +wait_queue_head_t read_wait +wait_queue_head_t write_wait +struct work_struct SAK_work tty_driver +int magic +const char *driver_name +const char *name +int name_base +int major +int minor_start +int num +short type +short subtype +struct tty_operations *ops +struct tty_struct **ttys +struct ktermios **termios tty_ldisc +int magic +char *name +int num +int flags +int(*open)(struct tty_struct*) +int(*close)(struct tty_struct*) +ssize_t(*read)(...) +ssize_t(*write)(...) tty_operations +int(*open)(struct tty_struct*, struct file*) +void(*close)(struct tty_struct*, struct file*) +int(*write)(struct tty_struct*, const unsigned char*, int) +int(*put_char)(struct tty_struct*, unsigned char) +void(*flush_chars)(struct tty_struct*) +int(*write_room)(struct tty_struct*) +int(*ioctl)(struct tty_struct*, unsigned int, unsigned long) +void(*set_termios)(struct tty_struct*, struct ktermios*) termios +tcflag_t c_iflag /* 输入模式标志 */ +tcflag_t c_oflag /* 输出模式标志 */ +tcflag_t c_cflag /* 控制模式标志 */ +tcflag_t c_lflag /* 本地模式标志 */ +cc_t c_cc[NCCS] /* 控制字符数组 */ +speed_t c_ispeed /* 输入速度 */ +speed_t c_ospeed /* 输出速度 */

4.2 TTY数据结构详解

4.2.1 tty_struct - TTY的核心结构体
c 复制代码
// drivers/tty/tty.h 中的关键定义(简化)
struct tty_struct {
    int magic;                      // 魔术字, 用于验证
    struct kref kref;              // 引用计数
    struct device *dev;            // 关联的设备
    
    // 驱动相关
    struct tty_driver *driver;     // 指向驱动
    const struct tty_operations *ops; // 操作函数集
    
    // 线路规程
    struct tty_ldisc *ldisc;       // 当前线路规程
    
    // 终端设置
    struct termios *termios;       // 终端属性(波特率, 模式等)
    struct termios *termios_locked; // 锁定的终端属性
    struct winsize winsize;        // 窗口大小(行数, 列数)
    
    // 缓冲管理
    unsigned char *write_buf;      // 写缓冲区
    int write_cnt;                 // 写缓冲区中的字节数
    struct tty_buffer *buf;        // 环形缓冲区
    
    // 读取相关
    int read_head, read_tail;      // 读取缓冲区头尾指针
    int canon_head;                // 规范模式下的行头
    unsigned long *read_flags;     // 读取标志
    
    // 等待队列
    wait_queue_head_t read_wait;   // 读取等待队列
    wait_queue_head_t write_wait;  // 写入等待队列
    
    // 控制相关
    struct work_struct SAK_work;   // 安全注意键工作队列
    struct tty_port *port;         // TTY端口
    
    // 会话和进程组
    struct pid *pgrp;              // 进程组ID
    struct pid *session;           // 会话ID
    unsigned char pktstatus;       // 包状态
    
    // 流控
    int flow_stopped;              // 流控停止标志
    // ... 更多字段
};
4.2.2 termios - 终端属性结构
c 复制代码
// include/uapi/asm-generic/termbits.h
struct termios {
    tcflag_t c_iflag;     /* 输入模式标志 */
    tcflag_t c_oflag;     /* 输出模式标志 */
    tcflag_t c_cflag;     /* 控制模式标志 */
    tcflag_t c_lflag;     /* 本地模式标志 */
    cc_t c_line;          /* 线路规程类型 */
    cc_t c_cc[NCCS];      /* 控制字符数组 */
    speed_t c_ispeed;     /* 输入速度 */
    speed_t c_ospeed;     /* 输出速度 */
};

// 重要标志位示例
#define IGNBRK  0000001  /* 忽略BREAK条件 */
#define BRKINT  0000002  /* BREAK产生中断 */
#define IGNPAR  0000004  /* 忽略奇偶错误 */
#define PARMRK  0000010  /* 标记奇偶错误 */
#define INPCK   0000020  /* 启用输入奇偶检查 */
#define ISTRIP  0000040  /* 剥离第8位 */
#define INLCR   0000100  /* 将NL映射为CR */
#define IGNCR   0000200  /* 忽略CR */
#define ICRNL   0000400  /* 将CR映射为NL */
#define IXON    0001000  /* 启用输出流控 */
#define IXOFF   0010000  /* 启用输入流控 */

#define OPOST   0000001  /* 实施输出处理 */
#define ONLCR   0000002  /* 将NL映射为CR-NL */
#define OCRNL   0000010  /* 将CR映射为NL */

#define CBAUD   0010017  /* 波特率掩码 */
#define B0      0000000  /* 挂断 */
#define B9600   0000015  /* 9600波特 */

#define CLOCAL  0004000  /* 忽略调制解调器控制线 */
#define CREAD   0000200  /* 启用接收器 */
#define CSIZE   0000060  /* 字符大小掩码 */
#define CS8     0000060  /* 8位数据位 */

#define ISIG    0000001  /* 启用信号 */
#define ICANON  0000002  /* 规范输入处理 */
#define ECHO    0000010  /* 启用回显 */
#define ECHONL  0000100  /* 回显NL即使无ECHO */

4.3 TTY操作流程分析

4.3.1 TTY打开流程
c 复制代码
// drivers/tty/tty_io.c
static int tty_open(struct inode *inode, struct file *filp)
{
    struct tty_struct *tty;
    int index;
    int retval;
    
    // 1. 获取设备号对应的索引
    index = tty_index(inode);
    
    // 2. 分配并初始化tty_struct
    tty = tty_init_dev(index, 0);
    if (IS_ERR(tty))
        return PTR_ERR(tty);
    
    // 3. 设置文件私有数据
    filp->private_data = tty;
    
    // 4. 设置文件操作标志
    filp->f_flags |= O_NONBLOCK;
    
    // 5. 调用驱动特定的open方法
    if (tty->ops->open)
        retval = tty->ops->open(tty, filp);
    else
        retval = -ENODEV;
    
    // 6. 如果打开失败, 清理资源
    if (retval) {
        tty_release_dev(filp);
        return retval;
    }
    
    // 7. 设置线路规程
    tty->ldisc = tty_ldisc_get(N_TTY);
    if (tty->ldisc->open)
        tty->ldisc->open(tty);
    
    return 0;
}
4.3.2 TTY读写数据流

用户进程 内核TTY子系统 终端驱动 硬件设备 TTY写入流程 write(fd, buf, count) tty_write(tty, buf, count) 线路规程处理 (n_tty_write) 缓冲管理/流控检查 tty->>ops->>write(tty, buf, count) 硬件特定写入操作 TTY读取流程 硬件中断/数据到达 接收数据到缓冲区 线路规程处理 (n_tty_receive_char) 特殊字符处理/信号发送 唤醒等待进程 read(fd, buf, count) 返回处理后的数据 用户进程 内核TTY子系统 终端驱动 硬件设备

5. TTY工作模式与特殊字符处理

5.1 终端模式详细对比

特性 加工模式 (Canonical) 原始模式 (Raw) 说明
行编辑 启用 禁用 退格键, 删除字符等
缓冲 行缓冲 字符缓冲 输入何时可用
信号生成 启用 禁用 Ctrl+C发送SIGINT
回显 可配置 可配置 显示输入字符
特殊字符 处理 不处理 CR/NL转换等
流控制 启用 禁用 Ctrl+S/Q

5.2 特殊字符处理机制

Linux TTY定义了多种控制字符, 每个都有特定功能:

c 复制代码
// 标准控制字符定义(来自termios.h)
#define VINTR     0    /* Ctrl+C: 中断进程 */
#define VQUIT     1    /* Ctrl+\: 退出进程 */
#define VERASE    2    /* Ctrl+?: 删除前一个字符 */
#define VKILL     3    /* Ctrl+U: 删除整行 */
#define VEOF      4    /* Ctrl+D: 文件结束 */
#define VTIME     5    /* 非规范模式超时 */
#define VMIN      6    /* 非规范模式最小字符数 */
#define VSWTC     7    /* 切换字符 */
#define VSTART    8    /* Ctrl+Q: 启动输出 */
#define VSTOP     9    /* Ctrl+S: 停止输出 */
#define VSUSP     10   /* Ctrl+Z: 挂起进程 */
#define VEOL      11   /* 行结束 */
#define VREPRINT  12   /* Ctrl+R: 重绘行 */
#define VDISCARD  13   /* Ctrl+O: 丢弃输出 */
#define VWERASE   14   /* Ctrl+W: 删除前一个单词 */
#define VLNEXT    15   /* Ctrl+V: 字面下一个字符 */

处理流程示例(Ctrl+C中断):

  1. 用户按下Ctrl+C
  2. 键盘驱动生成字符0x03
  3. 线路规程识别为VINTR
  4. 前台进程组发送SIGINT信号
  5. 进程收到信号, 默认终止

5.3 模式切换示例代码

c 复制代码
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>

// 保存原始终端设置
struct termios original_termios;

void disable_raw_mode() {
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios);
}

void enable_raw_mode() {
    // 获取当前终端设置
    tcgetattr(STDIN_FILENO, &original_termios);
    atexit(disable_raw_mode);
    
    struct termios raw = original_termios;
    
    // 修改输入模式标志
    raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    raw.c_iflag &= ~(IXON | IXOFF);  // 禁用软件流控
    
    // 修改输出模式标志
    raw.c_oflag &= ~(OPOST);
    
    // 修改控制标志
    raw.c_cflag |= (CS8);
    
    // 修改本地标志 - 禁用规范模式和回显
    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    
    // 设置控制字符
    raw.c_cc[VMIN] = 0;   // 非规范模式下read立即返回
    raw.c_cc[VTIME] = 1;  // 超时时间(十分之一秒)
    
    // 应用新的终端设置
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

int main() {
    enable_raw_mode();
    
    printf("原始模式已启用. 按'q'退出. \n");
    
    char c;
    while (read(STDIN_FILENO, &c, 1) == 1 && c != 'q') {
        // 显示按键的ASCII码
        printf("按键: 0x%02x (%c)\r\n", c, (c >= 32 && c < 127) ? c : ' ');
    }
    
    disable_raw_mode();
    return 0;
}

6. 实际应用示例: 创建简单的伪终端

6.1 创建伪终端对并通信

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <pty.h>
#include <utmp.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>

int main() {
    int master_fd, slave_fd;
    char slave_name[256];
    pid_t pid;
    char buffer[1024];
    ssize_t bytes;
    
    // 1. 打开主设备(自动分配从设备)
    master_fd = posix_openpt(O_RDWR | O_NOCTTY);
    if (master_fd < 0) {
        perror("posix_openpt");
        exit(1);
    }
    
    // 2. 设置从设备权限
    if (grantpt(master_fd) < 0) {
        perror("grantpt");
        exit(1);
    }
    
    // 3. 解锁从设备
    if (unlockpt(master_fd) < 0) {
        perror("unlockpt");
        exit(1);
    }
    
    // 4. 获取从设备名
    if (ptsname_r(master_fd, slave_name, sizeof(slave_name)) != 0) {
        perror("ptsname_r");
        exit(1);
    }
    
    printf("从设备: %s\n", slave_name);
    
    // 5. 创建子进程
    pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    }
    
    if (pid == 0) {  // 子进程 - 在伪终端中运行shell
        // 关闭主设备端
        close(master_fd);
        
        // 打开从设备作为控制终端
        slave_fd = open(slave_name, O_RDWR);
        if (slave_fd < 0) {
            perror("open slave");
            exit(1);
        }
        
        // 设置从设备为控制终端
        if (setsid() < 0) {
            perror("setsid");
            exit(1);
        }
        
        // 复制从设备到标准输入/输出/错误
        dup2(slave_fd, STDIN_FILENO);
        dup2(slave_fd, STDOUT_FILENO);
        dup2(slave_fd, STDERR_FILENO);
        
        // 关闭不需要的文件描述符
        if (slave_fd > STDERR_FILENO) {
            close(slave_fd);
        }
        
        // 执行shell
        execlp("/bin/bash", "bash", "--login", NULL);
        perror("execlp");
        exit(1);
    } else {  // 父进程 - 主设备端
        printf("主设备FD: %d, 子进程PID: %d\n", master_fd, pid);
        
        // 设置非阻塞读取
        int flags = fcntl(master_fd, F_GETFL, 0);
        fcntl(master_fd, F_SETFL, flags | O_NONBLOCK);
        
        // 简单的主设备循环
        while (1) {
            // 从主设备读取(子进程输出)
            bytes = read(master_fd, buffer, sizeof(buffer) - 1);
            if (bytes > 0) {
                buffer[bytes] = '\0';
                printf("从子进程接收: %s", buffer);
                fflush(stdout);
            }
            
            // 从标准输入读取, 发送到主设备
            bytes = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
            if (bytes > 0) {
                buffer[bytes] = '\0';
                write(master_fd, buffer, bytes);
            }
            
            // 检查子进程是否退出
            int status;
            if (waitpid(pid, &status, WNOHANG) == pid) {
                printf("子进程已退出\n");
                break;
            }
            
            // 短暂休眠避免CPU占用过高
            usleep(10000);  // 10ms
        }
        
        close(master_fd);
    }
    
    return 0;
}

6.2 示例应用场景说明

这个示例展示了终端模拟器的基本工作原理. 实际应用中, 终端模拟器(如xterm, gnome-terminal)就是通过类似的机制:

  1. 创建伪终端对
  2. 在从设备端运行shell
  3. 在主设备端处理用户输入和显示输出
  4. 实现丰富的终端功能(颜色, 光标控制等)

7. TTY调试与诊断工具

7.1 常用调试命令

命令 功能描述 使用示例
stty 查看/设置终端属性 stty -a 显示所有设置
tty 显示当前终端设备 tty
ps 查看进程的TTY ps -ef | grep $$
who 显示登录用户和TTY who
w 显示系统用户和活动 w
screen 终端复用和管理 screen -S session
script 记录终端会话 script session.log
infocmp 比较终端描述 infocmp xterm vt100
reset 重置终端状态 reset
echo 测试终端响应 echo -e "\033[31mRed Text\033[0m"

7.2 高级调试技巧

7.2.1 使用stty深入调试
bash 复制代码
# 1. 显示当前终端的所有设置
$ stty -a
speed 38400 baud; rows 40; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z;
rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke -flusho -extproc

# 2. 保存和恢复终端设置
$ stty -g > saved_settings
$ stty $(cat saved_settings)

# 3. 测试特殊字符处理
$ stty intr ^P  # 将中断字符改为Ctrl+P
$ stty erase ^H # 将删除字符改为Ctrl+H
7.2.2 内核调试技巧
c 复制代码
// 在内核中添加调试输出
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/tty.h>

void tty_debug_function(struct tty_struct *tty) {
    // 打印TTY状态信息
    pr_debug("TTY %p: count=%d, buf=%p, head=%d, tail=%d\n",
             tty, tty->count, tty->buf, tty->read_head, tty->read_tail);
    
    // 检查等待队列
    pr_debug("read_wait: %p, write_wait: %p\n",
             &tty->read_wait, &tty->write_wait);
}

// 使用ftrace跟踪TTY函数
# echo function > /sys/kernel/debug/tracing/current_tracer
# echo tty_* > /sys/kernel/debug/tracing/set_ftrace_filter
# echo 1 > /sys/kernel/debug/tracing/tracing_on

7.3 常见问题诊断表

问题现象 可能原因 诊断命令 解决方案
输入无回显 ECHO标志被禁用 stty -a | grep echo stty echo
退格键显示^? 擦除字符设置错误 stty erase ^H stty erase ^?
Ctrl+S冻结终端 软件流控启用 stty -ixon stty -ixon 禁用流控
终端乱码 终端类型不匹配 echo $TERM export TERM=xterm-256color
串口通信失败 波特率不匹配 stty -F /dev/ttyS0 stty -F /dev/ttyS0 115200
无法后台运行 终端信号干扰 stty tostop 使用nohup或disown

8. 总结

经过对Linux TTY子系统的深入分析, 我们可以总结出以下核心要点:

  1. 分层架构: TTY采用清晰的三层架构(线路规程层, 终端驱动层, 硬件层), 每层职责分明, 便于维护和扩展

  2. 模式灵活: 通过加工模式和原始模式的切换, TTY既能提供用户友好的交互体验, 又能满足高性能数据传输需求

  3. 会话管理: TTY是Linux会话管理的基础, 实现了进程组控制, 作业控制等关键功能

  4. 设备抽象: 通过伪终端机制, TTY成功地将物理终端抽象为软件概念, 为网络登录和终端模拟器奠定了基础

  5. 高度可配置: termios结构提供了细粒度的终端控制, 可以精确调整终端行为

相关推荐
moringlightyn1 小时前
Linux---基础IO(文件理解 文件接口使用 文件系统层面)
linux·运维·服务器·c语言·笔记·系统·文件
艾莉丝努力练剑1 小时前
【Python基础:语法第三课】Python 函数详解:定义、参数、返回值与作用域
服务器·人工智能·windows·python·pycharm
烟囱土著1 小时前
捣鼓30天,我写了一个数学加减练习小程序
学习·算法·微信小程序·小程序
拾忆,想起1 小时前
Dubbo灰度发布完全指南:从精准引流到全链路灰度
前端·微服务·架构·dubbo·safari
杜子不疼.1 小时前
【Linux】进程控制(一):进程的创建和终止
linux·运维·服务器
翼龙云_cloud1 小时前
阿里云渠道商:连接无影云电脑时最常见的问题有哪些?
服务器·阿里云·云计算·电脑·玩游戏
摇滚侠1 小时前
2025最新 SpringCloud 教程,Seata-基础-架构原理-整合 Seata 完成,笔记68,笔记69
笔记·spring cloud·架构
AndrewHZ1 小时前
【图像处理基石】如何用OpenCV入门计算机视觉?
图像处理·深度学习·opencv·算法·计算机视觉·机器视觉·cv
源代码•宸2 小时前
GoLang并发示例代码2(关于逻辑处理器运行顺序)
服务器·开发语言·经验分享·后端·golang