Linux中控制台初始化console_init函数的实现
控制台系统初始化console_init
void __init console_init(void)
{initcall_t *call;/* Setup the default TTY line discipline. */(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);/** set up the console device so that later boot sequences can* inform about problems etc..*/
#ifdef CONFIG_EARLY_PRINTKdisable_early_printk();
#endif
#ifdef CONFIG_SERIAL_68360/* This is not a console initcall. I know not what it's doing here.So I haven't moved it. dwmw2 */rs_360_init();
#endifcall = &__con_initcall_start;while (call < &__con_initcall_end) {(*call)();call++;}
}
1. 函数功能概述
console_init
函数负责初始化内核控制台系统,包括注册 TTY 线路规程、处理早期打印、执行控制台驱动初始化等,为内核提供输出和交互能力
2. 代码逐段分析
2.1. 函数定义和变量声明
void __init console_init(void)
{initcall_t *call;
void __init
:无返回值,__init
表示只在初始化阶段使用initcall_t *call
:初始化调用函数指针,用于遍历执行控制台初始化函数
2.2. 注册默认 TTY 线路规程
/* Setup the default TTY line discipline. */(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
- 设置默认的 TTY 线路规程
tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY)
:注册 TTY 线路规程N_TTY
:线路规程编号,表示规范模式(canonical mode)&tty_ldisc_N_TTY
:N_TTY 线路规程的操作结构体指针
(void)
:显式忽略返回值,表示不关心注册结果- 作用:为终端设备建立默认的输入处理和行为规则
2.3. 早期打印支持(条件编译)
#ifdef CONFIG_EARLY_PRINTKdisable_early_printk();
#endif
#ifdef CONFIG_EARLY_PRINTK
:条件编译,只在配置了早期打印支持时包含disable_early_printk()
:禁用早期打印功能- 背景:早期打印在内核完全初始化前使用,现在正式控制台已就绪,可以关闭
2.4. 68360 串口特殊处理(条件编译)
#ifdef CONFIG_SERIAL_68360/* This is not a console initcall. I know not what it's doing here.So I haven't moved it. dwmw2 */rs_360_init();
#endif
#ifdef CONFIG_SERIAL_68360
:针对 68360 串口的特殊处理rs_360_init()
:68360 串口初始化函数- 作用:历史遗留代码,确保特定硬件的串口正常工作
2.5. 控制台初始化调用遍历
call = &__con_initcall_start;while (call < &__con_initcall_end) {(*call)();call++;}
}
call = &__con_initcall_start
:获取控制台初始化调用段的起始地址while (call < &__con_initcall_end)
:循环直到初始化调用段的结束地址(*call)()
:执行当前指向的初始化函数call++
:移动到下一个初始化函数指针
TTY 线路规程注册tty_register_ldisc
int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
{unsigned long flags;int ret = 0;if (disc < N_TTY || disc >= NR_LDISCS)return -EINVAL;spin_lock_irqsave(&tty_ldisc_lock, flags);if (new_ldisc) {tty_ldiscs[disc] = *new_ldisc;tty_ldiscs[disc].num = disc;tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;tty_ldiscs[disc].refcount = 0;} else {if(tty_ldiscs[disc].refcount)ret = -EBUSY;elsetty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;}spin_unlock_irqrestore(&tty_ldisc_lock, flags);return ret;
}
1. 函数功能概述
tty_register_ldisc
函数负责注册或注销 TTY 线路规程,管理线路规程的全局表,为终端设备提供不同的输入处理和行为模式
2. 代码逐段分析
2.1. 函数定义和变量声明
int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
{unsigned long flags;int ret = 0;
int
:返回值类型,0表示成功,负数表示错误码int disc
:线路规程编号(如 N_TTY=0, N_SLIP=1, N_PPP=3 等)struct tty_ldisc *new_ldisc
:要注册的线路规程结构指针,NULL表示注销unsigned long flags
:保存中断状态的变量int ret = 0
:返回值,初始化为成功
2.2. 参数有效性检查
if (disc < N_TTY || disc >= NR_LDISCS)return -EINVAL;
disc < N_TTY
:检查编号是否小于最小有效值(N_TTY=0)disc >= NR_LDISCS
:检查编号是否超过最大支持数量NR_LDISCS
:最大线路规程数return -EINVAL
:如果编号无效,返回"无效参数"错误
2.3. 获取保护锁
spin_lock_irqsave(&tty_ldisc_lock, flags);
spin_lock_irqsave(&tty_ldisc_lock, flags)
:获取线路规程锁并保存中断状态tty_ldisc_lock
:保护线路规程表的自旋锁flags
:保存当前中断启用状态,解锁时恢复- 作用:防止多CPU并发修改线路规程表
2.4. 注册新线路规程分支
if (new_ldisc) {tty_ldiscs[disc] = *new_ldisc;tty_ldiscs[disc].num = disc;tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;tty_ldiscs[disc].refcount = 0;
if (new_ldisc)
:如果传入非空指针,执行注册操作
注册步骤:
tty_ldiscs[disc] = *new_ldisc
:将传入的线路规程结构复制到全局数组tty_ldiscs[disc].num = disc
:设置线路规程编号tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED
:设置"已定义"标志位tty_ldiscs[disc].refcount = 0
:初始化引用计数为0
2.5. 注销线路规程分支
} else {if(tty_ldiscs[disc].refcount)ret = -EBUSY;elsetty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;}
else
:如果传入NULL指针,执行注销操作
注销逻辑:
if(tty_ldiscs[disc].refcount)
:检查是否有活跃引用ret = -EBUSY
:如果有活跃引用,返回"设备忙"错误tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED
:如果没有引用,清除"已定义"标志
2.6. 释放锁并返回
spin_unlock_irqrestore(&tty_ldisc_lock, flags);return ret;
}
spin_unlock_irqrestore(&tty_ldisc_lock, flags)
:释放锁并恢复中断状态return ret
:返回操作结果(0成功,负数错误)
内核紧急情况处理panic
NORET_TYPE void panic(const char * fmt, ...)__attribute__ ((NORET_AND format (printf, 1, 2)));
1. 代码逐段分析
1.1. 返回值类型声明
NORET_TYPE void panic(const char * fmt, ...)
NORET_TYPE void
:特殊的返回值类型声明panic
:函数名,表示内核恐慌/紧急状态const char * fmt
:格式化字符串参数,不可修改的字符串指针...
:可变参数列表,表示可以接受多个参数
1.2. 函数属性声明
__attribute__ ((NORET_AND format (printf, 1, 2)));
__attribute__
:GCC 编译器扩展,用于指定函数属性((NORET_AND format (printf, 1, 2)))
:包含两个属性的复合属性
NORET_TYPE 宏定义
// 在 Linux 内核中的典型定义
#define NORET_TYPE /* */
#define NORET_AND noreturn,
noreturn
属性含义:
- 告诉编译器这个函数永远不会返回
- 函数会终止程序执行或无限循环
- 编译器可以据此优化代码和警告
format 属性详解
format (printf, 1, 2)
printf
:指定格式字符串风格与printf
相同1
:格式字符串参数的位置(第1个参数)2
:可变参数开始的位置(从第2个参数开始)