控制台

结构体控制台

enum cons_flags

通用控制台标志

常量

CON_PRINTBUFFER

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

CON_CONSDEV

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

CON_ENABLED

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

CON_BOOT

将控制台驱动程序标记为早期控制台驱动程序,该驱动程序在实际驱动程序可用之前在引导期间使用。除非使用“keep_bootcon”参数,否则当实际控制台驱动程序注册时,它将自动取消注册。

CON_ANYTIME

一个误称的历史标志,它告诉核心代码可以对标记为 OFFLINE 的 CPU 调用遗留的 console::write 回调。这是具有误导性的,因为它表明调用回调没有上下文限制。最初的动机是每个 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 回调,用于在任务上下文中写出文本。

必须仅在任务上下文中调用此回调,其中 device_lock() 和使用 NBCON_PRIO_NORMAL 获取的 nbcon 控制台。

控制台所有权验证和不安全部分处理的相同规则适用于 write_atomic()。

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

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

[*] 独立的 nbcon_context_try_acquire() 与

启用了抢占是不安全的,请参阅 nbcon_owner_matches()。但当总是在 device_lock() 下的抢占上下文中调用时,它可能是安全的。

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

永远不需要旋转,这在使用 PREEMPT_RT 时尤其重要。

device_lock

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

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

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

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

可能正在使用已经处理此问题的同步机制(例如自旋锁)。否则,此函数必须显式调用 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}

匿名联合体

原子操作

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

{unnamed_struct}

匿名联合体

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

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

Struct 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

count 个具有属性 s 的字符发送到 vc 上的 [x, y]。

con_cursor

根据 enable 启用/禁用光标

con_scroll

linestopbottom 的行按 dir 方向移动。如果没有进行通用处理,则返回 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

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

con_debug_enter

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

con_debug_leave

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

控制台函数

short console_srcu_read_flags(const struct console *con)

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

参数

const struct console *con

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

描述

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

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

如果调用者持有 console_list_lock 或 _确定_ 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 保证控制台列表将保持一致,但在迭代时,其他 CPU 可能会更新 struct console 字段。

需要持有 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() 方法来更新结构。

锁定:调用者必须持有控制台信号量来保护控制台内部和 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)

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

参数

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 调用来修改该默认设置。

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

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

当内核在编译时没有使用 CONFIG_VT_CONSOLE 时,此函数会忽略参数并始终返回 0