控制台

结构体 Console

enum cons_flags

通用控制台标志

常量

CON_PRINTBUFFER

由新注册的控制台使用,以避免重复输出已被引导控制台显示或用户空间通过 syslog() 系统调用读取的消息。

CON_CONSDEV

指示控制台驱动程序正在支持 /dev/console。

CON_ENABLED

指示是否允许控制台打印记录。 如果为 false,控制台也不会前进到后面的记录。

CON_BOOT

将控制台驱动程序标记为早期控制台驱动程序,该驱动程序在启动期间在真实驱动程序可用之前使用。 当注册真实的控制台驱动程序时,它将自动注销,除非使用“keep_bootcon”参数。

CON_ANYTIME

一个用词不当的历史标志,它告诉核心代码,传统的 console::write 回调可以在标记为 OFFLINE 的 CPU 上调用。 这是具有误导性的,因为它表明调用回调没有上下文限制。 最初的动机是每个 CPU 区域的就绪状态。

CON_BRL

指示盲文设备,出于显而易见的原因,该设备可以免于接收 printk 垃圾邮件。

CON_EXTENDED

控制台支持 /dev/kmesg 的扩展输出格式,这需要更大的输出缓冲区。

CON_SUSPENDED

指示控制台是否已挂起。 如果为 true,则不得调用打印回调。

CON_NBCON

控制台可以在传统的 console_lock 约束之外运行。

struct console

控制台描述符结构

定义:

struct console {
    char name[16];
    void (*write)(struct console *co, const char *s, unsigned int count);
    int (*read)(struct console *co, char *s, unsigned int count);
    struct tty_driver       *(*device)(struct console *co, int *index);
    void (*unblank)(void);
    int (*setup)(struct console *co, char *options);
    int (*exit)(struct console *co);
    int (*match)(struct console *co, char *name, int idx, char *options);
    short flags;
    short index;
    int cflag;
    uint ispeed;
    uint ospeed;
    u64 seq;
    unsigned long           dropped;
    void *data;
    struct hlist_node       node;
    void (*write_atomic)(struct console *con, struct nbcon_write_context *wctxt);
    void (*write_thread)(struct console *con, struct nbcon_write_context *wctxt);
    void (*device_lock)(struct console *con, unsigned long *flags);
    void (*device_unlock)(struct console *con, unsigned long flags);
    atomic_t __private nbcon_state;
    atomic_long_t __private nbcon_seq;
    struct nbcon_context    __private nbcon_device_ctxt;
    atomic_long_t __private nbcon_prev_seq;
    struct printk_buffers   *pbufs;
    struct task_struct      *kthread;
    struct rcuwait          rcuwait;
    struct irq_work         irq_work;
};

成员

name

控制台驱动程序的名称

write

用于输出消息的传统写入回调(可选)

read

用于控制台输入的读取回调(可选)

device

底层 TTY 设备驱动程序(可选)

unblank

用于取消消隐控制台的回调(可选)

setup

用于初始化控制台的回调(可选)

exit

用于拆卸控制台的回调(可选)

match

用于匹配控制台的回调(可选)

flags

控制台标志。 请参见 enum cons_flags

index

控制台索引,例如端口号

cflag

TTY 控制模式标志

ispeed

TTY 输入速度

ospeed

TTY 输出速度

seq

要打印的下一个环形缓冲区记录的序列号

dropped

未报告的丢弃的环形缓冲区记录数

data

驱动程序私有数据

node

控制台列表的 hlist 节点

write_atomic

用于在任何上下文中写出文本的 NBCON 回调。(可选)

在调用此回调时,控制台已被获取。 但是,默认情况下允许更高优先级的上下文接管它。

回调必须在任何接管不安全的代码周围调用 nbcon_enter_unsafe() 和 nbcon_exit_unsafe(),例如,在操作串行端口寄存器时。

如果在该上下文失去控制台所有权的同时,nbcon_enter_unsafe() 将失败。 在这种情况下,不再允许回调继续进行。 它必须立即小心地退出。 缓冲区内容也不再可信,因为它不再属于上下文。

回调应在安全的情况下允许接管。 这增加了在系统出现问题时查看消息的机会。 如果驱动程序必须重新获取所有权才能最终确定或恢复硬件更改,则可以使用 nbcon_reacquire_nobuf()。 但是,重新获取后,缓冲区内容不再可用。 重新获取不能用于恢复打印。

可以从任何上下文(包括 NMI)调用回调。 因此,它必须避免使用任何锁定,而是依靠控制台所有权进行同步。

write_thread

用于在任务上下文中写出文本的 NBCON 回调。

只能在任务上下文中调用此回调,并且必须使用 NBCON_PRIO_NORMAL 获取 device_lock() 和 nbcon 控制台。

控制台所有权验证和不安全区域处理的规则与 write_atomic() 相同。

控制台所有权处理对于与仅通过上下文同步的 write_atomic() 进行同步是必需的。

device_lock() 为设备上的操作提供主要序列化。 它可以根据需要宽松(互斥)[*] 或紧密(禁用抢占和中断)。 它允许 kthread 在限制最少的模式下运行 [**]。

[*] 启用了抢占后,独立的 nbcon_context_try_acquire() 不安全,请参阅 nbcon_owner_matches()。 但是,当始终在设备锁下的抢占上下文中调用它时,它可以是安全的。

the preemption enabled, see nbcon_owner_matches(). But it can be safe when always called in the preemptive context under the device_lock().

[**] device_lock() 确保 nbcon_context_try_acquire()

would never need to spin which is important especially with PREEMPT_RT.

device_lock

用于开始与驱动程序代码同步的 NBCON 回调。

控制台驱动程序通常必须处理通过用户输入/输出(例如交互式登录 shell)对硬件的访问,以及通过 printk() 调用输出内核消息。 每当 printk 子系统需要与驱动程序的硬件访问同步时,都会调用此回调。 应该实现它以使用驱动程序自身正在使用的任何同步机制(例如,用于 uart 串行控制台的端口锁)。

始终从任务上下文中调用回调。 它可以使用驱动程序所需的任何同步方法。

重要提示:回调必须禁用迁移。 控制台驱动程序

may be using a synchronization mechanism that already takes care of this (such as spinlocks). Otherwise this function must explicitly call migrate_disable().

flags 参数作为驱动程序的便利提供。 它将再次传递给 device_unlock()。 如果驱动程序不需要它,则可以忽略它。

device_unlock

用于完成与驱动程序代码同步的 NBCON 回调。

它是 device_lock() 的对应项。

始终从任务上下文中调用此回调。 它必须适当地重新启用迁移(具体取决于 device_lock() 如何禁用迁移)。

flags 参数是传递给 device_lock() 的同一变量的值。

nbcon_state

nbcon 控制台的状态

nbcon_seq

nbcon 要打印的下一条记录的序列号

nbcon_device_ctxt

可用于非打印操作的上下文

nbcon_prev_seq

上次 nbcon 所有者被分配打印的序列号

pbufs

指向 nbcon 私有缓冲区的指针

kthread

此控制台的打印机 kthread

rcuwait

用于 kthread 唤醒的 RCU 安全等待对象

irq_work

kthread 唤醒推迟到 IRQ 工作上下文

内部结构

struct nbcon_state

nbcon 控制台的控制台状态

定义:

struct nbcon_state {
    union {
        unsigned int    atom;
        struct {
            unsigned int prio               :  2;
            unsigned int req_prio           :  2;
            unsigned int unsafe             :  1;
            unsigned int unsafe_takeover    :  1;
            unsigned int cpu                : 24;
        };
    };
};

成员

{unnamed_union}

anonymous

atom

用于原子操作的状态字段的复合

{unnamed_struct}

anonymous

prio

当前所有者的优先级

req_prio

切换请求的优先级

unsafe

控制台在一个非接管区域中处于忙碌状态

unsafe_takeover

过去发生了不安全状态下的恶意接管。 在重新初始化之前,控制台无法安全。

cpu

所有者在其上运行的 CPU

描述

用于读取和准备存储在 nbcon 状态变量 console::nbcon_state 中的值。

prioreq_prio 字段尤其重要,允许旋转等待超时并放弃,而不会有等待者在放弃后被分配锁的风险。

enum nbcon_prio

nbcon 控制台的控制台所有者优先级

常量

NBCON_PRIO_NONE

未使用

NBCON_PRIO_NORMAL

正常(非紧急)使用

NBCON_PRIO_EMERGENCY

紧急输出 (WARN/OOPS...)

NBCON_PRIO_PANIC

Panic 输出

NBCON_PRIO_MAX

优先级级别的数量

描述

当控制台处于安全状态时,更高优先级的上下文可以接管控制台。 在 panic() 中刷新控制台的最后一次尝试甚至可以在不安全状态下进行(希望和祈祷)。

struct nbcon_context

用于控制台获取/释放的上下文

定义:

struct nbcon_context {
    struct console          *console;
    unsigned int            spinwait_max_us;
    enum nbcon_prio         prio;
    unsigned int            allow_unsafe_takeover   : 1;
    unsigned int            backlog                 : 1;
    struct printk_buffers   *pbufs;
    u64 seq;
};

成员

console

关联的控制台

spinwait_max_us

旋转等待获取的限制

prio

上下文的优先级

allow_unsafe_takeover

即使不安全也允许执行接管。 只能与 NBCON_PRIO_PANIC prio 一起使用。 当稍后使用控制台时,它可能会导致系统冻结。

backlog

环形缓冲区具有待处理的记录

pbufs

指向此上下文的文本缓冲区的指针

seq

要为此上下文打印的序列号

struct nbcon_write_context

传递给 nbcon 写入回调的上下文

定义:

struct nbcon_write_context {
    struct nbcon_context    __private ctxt;
    char *outbuf;
    unsigned int            len;
    bool unsafe_takeover;
};

成员

ctxt

核心控制台上下文

outbuf

指向用于输出的文本缓冲区的指针

len

要写入的长度

unsafe_takeover

如果发生了不安全状态下的恶意接管

结构体 Consw

struct consw

用于控制台的回调

定义:

struct consw {
    struct module *owner;
    const char *(*con_startup)(void);
    void (*con_init)(struct vc_data *vc, bool init);
    void (*con_deinit)(struct vc_data *vc);
    void (*con_clear)(struct vc_data *vc, unsigned int y, unsigned int x, unsigned int count);
    void (*con_putc)(struct vc_data *vc, u16 ca, unsigned int y, unsigned int x);
    void (*con_putcs)(struct vc_data *vc, const u16 *s,unsigned int count, unsigned int ypos, unsigned int xpos);
    void (*con_cursor)(struct vc_data *vc, bool enable);
    bool (*con_scroll)(struct vc_data *vc, unsigned int top,unsigned int bottom, enum con_scroll dir, unsigned int lines);
    bool (*con_switch)(struct vc_data *vc);
    bool (*con_blank)(struct vc_data *vc, enum vesa_blank_mode blank, bool mode_switch);
    int (*con_font_set)(struct vc_data *vc,const struct console_font *font, unsigned int vpitch, unsigned int flags);
    int (*con_font_get)(struct vc_data *vc, struct console_font *font, unsigned int vpitch);
    int (*con_font_default)(struct vc_data *vc, struct console_font *font, const char *name);
    int (*con_resize)(struct vc_data *vc, unsigned int width, unsigned int height, bool from_user);
    void (*con_set_palette)(struct vc_data *vc, const unsigned char *table);
    void (*con_scrolldelta)(struct vc_data *vc, int lines);
    bool (*con_set_origin)(struct vc_data *vc);
    void (*con_save_screen)(struct vc_data *vc);
    u8 (*con_build_attr)(struct vc_data *vc, u8 color,enum vc_intensity intensity, bool blink, bool underline, bool reverse, bool italic);
    void (*con_invert_region)(struct vc_data *vc, u16 *p, int count);
    void (*con_debug_enter)(struct vc_data *vc);
    void (*con_debug_leave)(struct vc_data *vc);
};

成员

owner

当使用此控制台时,要获取引用的模块

con_startup

设置控制台并返回其名称(如 VGA、EGA 等)

con_init

vc 上初始化控制台。 对于在此 vc 上的第一次调用,init 为 true。

con_deinit

vc 中取消初始化控制台。

con_clear

vc 上的 [x, y] 处擦除 count 个字符。 count >= 1。

con_putc

使用属性 ca 将一个字符发送到 vc 上的 [x, y]。 (可选 -- 将调用 con_putcs

con_putcs

使用属性 scount 个字符发送到 vc 上的 [x, y]。

con_cursor

根据 enable 启用/禁用光标

con_scroll

lines 在方向 dir 中将 topbottom 的行移动。 如果不应进行通用处理,则返回 true。 由 csi_M 调用和打印到控制台。

con_switch

有关控制台切换的通知器; 如果需要重绘,则应返回 true。

con_blank

消隐/取消消隐控制台。 目标模式在 blank 中传递。 如果从/向文本/图形更改,则设置 mode_switch。 如果需要重绘,则挂钩应返回 true。

con_font_set

将控制台 vc 字体设置为高度为 vpitchfontflags 可以是 KD_FONT_FLAG_DONT_RECALC。(可选)

con_font_get

在高度为 vpitchvc 上获取当前字体到 font 中。(可选)

con_font_default

vc 上设置默认字体。 name 可以是 NULL 或要搜索的字体名称。 可以将 font 填充回。(可选)

con_resize

vc 控制台调整为 width x height。 当此更改来自用户空间时,from_user 为 true。

con_set_palette

将控制台 vc 的调色板设置为 table(可选)

con_scrolldelta

控制台的内容应按 lines 滚动。 由用户调用。(可选)

con_set_origin

设置 vc 的原点(请参阅 vc_data::vc_origin)。 如果未提供或返回 false,则原点设置为 vc->vc_screenbuf。(可选)

con_save_screen

将屏幕内容保存到 vc->vc_screenbuf 中。 例如,在进入图形时调用。(可选)

con_build_attr

基于 colorintensity 和其他参数构建属性。 结果用于普通字符和擦除字符。(可选)

con_invert_region

反转 vc 上从 p 开始的长度为 count 的区域。(可选)

con_debug_enter

为调试器准备控制台。 这包括但不限于,取消消隐控制台、加载适当的调色板和允许调试器生成的输出。(可选)

con_debug_leave

尽可能将控制台恢复到调试前的状态。(可选)

控制台函数

short console_srcu_read_flags(const struct console *con)

以无锁方式读取可能已注册的控制台的标志

参数

const struct console *con

要从中读取标志的控制台的 struct console 指针

描述

以无锁方式读取 con->flags 提供了 Read 一致的值,因为最多只有一个 CPU 正在修改 con->flags,并且该 CPU 仅使用读取-修改-写入操作来执行此操作。

需要保持 console_srcu_read_lock,这意味着 con 可能是已注册的控制台。 保持 console_srcu_read_lock 的目的是保证控制台状态有效 (CON_SUSPENDED/CON_ENABLED),并且如果控制台当前正在注销,则不会运行任何退出/清理例程。

如果调用方保持 console_list_lock,或者 _certain_ con 未注册且不会变为已注册,则调用方可以直接读取 con->flags

上下文

任何上下文。

返回

con->flags 字段的当前值。

void console_srcu_write_flags(struct console *con, short flags)

为已注册的控制台写入标志

参数

struct console *con

要写入标志的控制台的 struct console 指针

short flags

要写入的新标志值

描述

仅使用此函数为已注册的控制台写入标志。 它需要保持 console_list_lock。

上下文

任何上下文。

for_each_console_srcu

for_each_console_srcu (con)

遍历已注册的控制台

参数

con

用作循环游标的 struct console 指针

描述

尽管 SRCU 保证控制台列表将保持一致,但在迭代时,struct console 字段可能会被其他 CPU 更新。

需要保持 console_srcu_read_lock。 可以从任何上下文中调用。

for_each_console

for_each_console (con)

遍历已注册的控制台

参数

con

用作循环游标的 struct console 指针

描述

控制台列表和 console.flags 在迭代时是不可变的。

需要保持 console_list_lock。

void clear_selection(void)

删除当前选择

参数

void

无参数

描述

从保持选择的控制台中删除当前选择突出显示(如果有)。

锁定:调用方必须保持控制台锁。

int __vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows, bool from_user)

调整 VT 的大小

参数

struct vc_data *vc

虚拟控制台

unsigned int cols

unsigned int rows

bool from_user

由用户调用?

描述

从控制台的角度来看,调整虚拟控制台的大小。 我们使用通用的 vc_do_resize() 方法来更新结构。

锁定:调用方必须保持控制台 sem 以保护控制台内部结构和 vc->port.tty

int con_is_bound(const struct consw *csw)

检查驱动程序是否绑定到控制台

参数

const struct consw *csw

控制台驱动程序

返回

如果未绑定,则为零; 如果已绑定,则为非零

描述

驱动程序可以调用此函数,如果为零,则应释放 consw.con_startup() 上分配的所有资源

bool con_is_visible(const struct vc_data *vc)

检查当前控制台是否可见

参数

const struct vc_data *vc

虚拟控制台

返回

如果不可见,则为零; 如果可见,则为非零

void con_debug_enter(struct vc_data *vc)

为内核调试器准备控制台

参数

struct vc_data *vc

虚拟控制台

描述

当控制台被内核调试器接管时调用,此函数需要保存当前控制台状态,然后将控制台置于适合内核调试器的状态。

void con_debug_leave(void)

恢复控制台状态

参数

void

无参数

描述

将控制台状态恢复到调用内核调试器之前的状态。

int do_unregister_con_driver(const struct consw *csw)

从控制台层注销控制台驱动程序

参数

const struct consw *csw

控制台驱动程序

描述

所有注册到控制台层的驱动程序都必须在退出时调用此函数,或者如果控制台驱动程序处于无法处理控制台服务的状态,例如没有加载帧缓冲驱动程序的帧缓冲控制台。

驱动程序必须先取消绑定,然后才能注销。

内部结构

int sel_loadlut(u32 __user *lut)

加载 LUT 表

参数

u32 __user *lut

用户表

描述

从用户空间加载 LUT 表。 创建一个临时副本,以便部分更新不会造成混乱。

锁定:已获取控制台锁。

int set_selection_user(const struct tiocl_selection __user *sel, struct tty_struct *tty)

设置当前选择。

参数

const struct tiocl_selection __user *sel

用户选择信息

struct tty_struct *tty

控制台 tty

描述

由 vt 层的 ioctl 句柄调用。

锁定:整个选择过程都在 console_lock 下进行管理。 它在锁定下进行了很多处理,但这几乎不是性能路径。

int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, unsigned int cols, unsigned int lines, bool from_user)

tty 的调整大小方法

参数

struct tty_struct *tty

正在调整大小的 tty

struct vc_data *vc

虚拟控制台私有数据

unsigned int cols

unsigned int lines

行数

bool from_user

由用户调用?

描述

调整虚拟控制台的大小,根据实际约束进行裁剪。 如果调用者传递了一个 tty 结构,则更新 termios winsize 信息并执行任何必要的信号处理。

锁定:调用者必须持有控制台信号量。 如果传递了 tty,则获取 tty 的 termios rwsem 和 ctrl.lock。

int vt_resize(struct tty_struct *tty, struct winsize *ws)

调整 VT 的大小

参数

struct tty_struct *tty

要调整大小的 tty

struct winsize *ws

winsize 属性

描述

调整虚拟终端的大小。 这是由 tty 层调用的,因为我们注册了自己的调整大小处理程序。 互助助手完成了所有实际工作。

锁定:获取控制台信号量,然后被调用的方法按顺序获取 tty termios_rwsem 和 tty ctrl.lock。

enum vc_ctl_state

vt 的控制字符状态

常量

ESnormal

初始状态,未解析控制字符

ESesc

已解析 ESC

ESsquare

已解析 CSI -- 预期修饰符/参数/控制字符

ESgetpars

已解析 CSI -- 预期参数/控制字符

ESfunckey

已解析 CSI [

EShash

已解析 ESC #

ESsetG0

已解析 ESC (

ESsetG1

已解析 ESC )

ESpercent

已解析 ESC %

EScsiignore

已解析 CSI [0x20-0x3f]

ESnonstd

已解析 OSC

ESpalette

已解析 OSC P

ESosc

已解析 OSC [0-9]

ESANSI_first

用于忽略 ansi 控制序列的第一个状态

ESapc

已解析 ESC _

ESpm

已解析 ESC ^

ESdcs

已解析 ESC P

ESANSI_last

用于忽略 ansi 控制序列的最后一个状态

int vc_sanitize_unicode(const int c)

将无效的 Unicode 代码点替换为 U+FFFD

参数

const int c

收到的代码点

int vc_translate_unicode(struct vc_data *vc, int c, bool *rescan)

vc_data.vc_utf_char 中将 UTF-8 组合成 Unicode

参数

struct vc_data *vc

虚拟控制台

int c

要翻译的 UTF-8 字节

bool *rescan

如果 c 未在此处使用并且需要重新处理,则设置为 true

描述

  • vc_data.vc_utf_char 是正在构建的 Unicode 代码点。

  • vc_data.vc_utf_count 是仍然期望到达的延续字节数。

  • vc_data.vc_npar 是到目前为止到达的延续字节数。

返回

  • -1 - 到目前为止输入正常,c 已使用,预期有更多字节。

  • 0xFFFD - 可能性 1:输入无效,可能已使用 c(请参阅

    rescan 的描述)。 可能性 2:输入正常,已使用 cU+FFFD 是生成的代码点。 U+FFFD 有效,REPLACEMENT CHARACTER

  • 否则 - 输入正常,已使用 c,返回生成的代码点。

int vt_kmsg_redirect(int new)

设置/获取内核消息控制台

参数

int new

新的虚拟终端编号,如果控制台应保持不变,则为 -1

描述

默认情况下,内核消息始终打印在当前的虚拟控制台上。 但是,用户可以使用 TIOCL_SETKMSGREDIRECT ioctl 调用修改该默认值。

此函数将内核消息控制台设置为 new。 它返回旧的虚拟控制台编号。 虚拟终端编号 0(作为参数和返回值)表示没有重定向(即始终打印在当前活动的控制台上)。

参数 -1 表示仅返回当前控制台,但不修改该值。 在这种情况下,可以使用宏 vt_get_kmsg_redirect() 来使代码更易于理解。

如果内核在没有 CONFIG_VT_CONSOLE 的情况下编译,则此函数会忽略该参数并始终返回 0