Linux中控制台初始化console_init函数的实现

控制台系统初始化console_init

c 复制代码
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_PRINTK
        disable_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();
#endif
        call = &__con_initcall_start;
        while (call < &__con_initcall_end) {
                (*call)();
                call++;
        }
}

1. 函数功能概述

console_init 函数负责初始化内核控制台系统,包括注册 TTY 线路规程、处理早期打印、执行控制台驱动初始化等,为内核提供输出和交互能力

2. 代码逐段分析

2.1. 函数定义和变量声明

c 复制代码
void __init console_init(void)
{
        initcall_t *call;
  • void __init:无返回值,__init 表示只在初始化阶段使用
  • initcall_t *call:初始化调用函数指针,用于遍历执行控制台初始化函数

2.2. 注册默认 TTY 线路规程

c 复制代码
        /* 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. 早期打印支持(条件编译)

c 复制代码
#ifdef CONFIG_EARLY_PRINTK
        disable_early_printk();
#endif
  • #ifdef CONFIG_EARLY_PRINTK:条件编译,只在配置了早期打印支持时包含
  • disable_early_printk():禁用早期打印功能
  • 背景:早期打印在内核完全初始化前使用,现在正式控制台已就绪,可以关闭

2.4. 68360 串口特殊处理(条件编译)

c 复制代码
#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. 控制台初始化调用遍历

c 复制代码
        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

c 复制代码
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;
                else
                        tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
        }
        spin_unlock_irqrestore(&tty_ldisc_lock, flags);

        return ret;
}

1. 函数功能概述

tty_register_ldisc 函数负责注册或注销 TTY 线路规程,管理线路规程的全局表,为终端设备提供不同的输入处理和行为模式

2. 代码逐段分析

2.1. 函数定义和变量声明

c 复制代码
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. 参数有效性检查

c 复制代码
        if (disc < N_TTY || disc >= NR_LDISCS)
                return -EINVAL;
  • disc < N_TTY:检查编号是否小于最小有效值(N_TTY=0)
  • disc >= NR_LDISCS:检查编号是否超过最大支持数量
  • NR_LDISCS:最大线路规程数
  • return -EINVAL:如果编号无效,返回"无效参数"错误

2.3. 获取保护锁

c 复制代码
        spin_lock_irqsave(&tty_ldisc_lock, flags);
  • spin_lock_irqsave(&tty_ldisc_lock, flags):获取线路规程锁并保存中断状态
  • tty_ldisc_lock:保护线路规程表的自旋锁
  • flags:保存当前中断启用状态,解锁时恢复
  • 作用:防止多CPU并发修改线路规程表

2.4. 注册新线路规程分支

c 复制代码
        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. 注销线路规程分支

c 复制代码
        } else {
                if(tty_ldiscs[disc].refcount)
                        ret = -EBUSY;
                else
                        tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
        }
  • else:如果传入NULL指针,执行注销操作

注销逻辑:

  • if(tty_ldiscs[disc].refcount):检查是否有活跃引用
  • ret = -EBUSY:如果有活跃引用,返回"设备忙"错误
  • tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED:如果没有引用,清除"已定义"标志

2.6. 释放锁并返回

c 复制代码
        spin_unlock_irqrestore(&tty_ldisc_lock, flags);

        return ret;
}
  • spin_unlock_irqrestore(&tty_ldisc_lock, flags):释放锁并恢复中断状态
  • return ret:返回操作结果(0成功,负数错误)

内核紧急情况处理panic

c 复制代码
NORET_TYPE void panic(const char * fmt, ...)
        __attribute__ ((NORET_AND format (printf, 1, 2)));

1. 代码逐段分析

1.1. 返回值类型声明

c 复制代码
NORET_TYPE void panic(const char * fmt, ...)
  • NORET_TYPE void:特殊的返回值类型声明
  • panic:函数名,表示内核恐慌/紧急状态
  • const char * fmt:格式化字符串参数,不可修改的字符串指针
  • ...:可变参数列表,表示可以接受多个参数

1.2. 函数属性声明

c 复制代码
        __attribute__ ((NORET_AND format (printf, 1, 2)));
  • __attribute__:GCC 编译器扩展,用于指定函数属性
  • ((NORET_AND format (printf, 1, 2))):包含两个属性的复合属性

NORET_TYPE 宏定义

c 复制代码
// 在 Linux 内核中的典型定义
#define NORET_TYPE    /* */
#define NORET_AND     noreturn,

noreturn 属性含义:

  • 告诉编译器这个函数永远不会返回
  • 函数会终止程序执行或无限循环
  • 编译器可以据此优化代码和警告

format 属性详解

c 复制代码
format (printf, 1, 2)
  • printf:指定格式字符串风格与 printf 相同
  • 1:格式字符串参数的位置(第1个参数)
  • 2:可变参数开始的位置(从第2个参数开始)
相关推荐
skywalk81631 小时前
尝试Auto-coder.chat使用星河社区AIStudio部署的几个大模型:文心4.5-21b、Deepseek r1 70b、llama 3.1 8b
linux·服务器·人工智能·大模型·aistudio
QiTinna1 小时前
系统运维Day02_数据同步服务
linux·同步·rsync
阿猿收手吧!2 小时前
【Linux网络】shutdown()与close()的区别
linux·网络
LCG元2 小时前
Linux 磁盘管理从入门到精通:LVM 扩容实战案例
linux
liu****2 小时前
12.线程(二)
linux·开发语言·c++·1024程序员节
咯哦哦哦哦2 小时前
vscode arm交叉编译 中 cmakeTools 编译器设置
linux·arm开发·vscode·编辑器
工具人55553 小时前
Linux 抓取 RAM Dump 完整指南
linux·运维·安全
不懂音乐的欣赏者3 小时前
Windows 下 ROS/ROS2 开发环境最优解:WSL 比直接安装、虚拟机、双系统更优雅!
linux·windows·ubuntu·ros·wsl·ros2·双系统
小狗爱吃黄桃罐头4 小时前
正点原子【第四期】Linux之驱动开发学习笔记-10.1 Linux 内核定时器实验
linux·驱动开发·学习
Kang强5 小时前
tcpdump 抓到 icmp 包,但是抓不到 tcp 包??
linux