控制台¶
结构体控制台¶
-
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 中的值。
prio 和 req_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
按 lines 将 top 到 bottom 的行按 dir 方向移动。如果没有进行通用处理,则返回 true。由 csi_M 和打印到控制台调用。
con_switch
关于控制台切换的通知;如果需要重绘,则应返回 true。
con_blank
使控制台空白/取消空白。目标模式在 blank 中传递。如果从文本/图形更改为文本/图形,则设置 mode_switch。如果需要重绘,则挂钩应返回 true。
con_font_set
将控制台 vc 字体设置为具有高度 vpitch 的 font。flags 可以是
KD_FONT_FLAG_DONT_RECALC
。(可选)con_font_get
将高度为 vpitch 的 vc 上当前字体提取到 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
基于 color、intensity 和其他参数构建属性。结果用于普通字符和擦除字符。(可选)
con_invert_region
反转从 p 开始的 vc 上长度为 count 的区域。(可选)
con_debug_enter
为调试器准备控制台。这包括但不限于,取消控制台空白、加载适当的调色板,以及允许调试器生成的输出。(可选)
con_debug_leave
尽可能将控制台恢复到调试前的状态。(可选)
控制台函数¶
参数
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 字段的当前值。
参数
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)
迭代已注册的控制台
-
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。
参数
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
无参数
描述
将控制台状态恢复到调用内核调试器之前的状态。
参数
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:输入正常,已使用 c,
U+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
。