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个参数开始)
相关推荐
t198751289 小时前
在Ubuntu 22.04系统上安装libimobiledevice
linux·运维·ubuntu
skywalk81639 小时前
linux安装Code Server 以便Comate IDE和CodeBuddy等都可以远程连上来
linux·运维·服务器·vscode·comate
晚风吹人醒.10 小时前
缓存中间件Redis安装及功能演示、企业案例
linux·数据库·redis·ubuntu·缓存·中间件
Hard but lovely11 小时前
linux: pthread库的使用和理解
linux
这儿有一堆花13 小时前
Kali Linux:探测存活到挖掘漏洞
linux·运维·服务器
松涛和鸣13 小时前
从零开始理解 C 语言函数指针与回调机制
linux·c语言·开发语言·嵌入式硬件·排序算法
皮小白14 小时前
ubuntu开机检查磁盘失败进入应急模式如何修复
linux·运维·ubuntu
邂逅星河浪漫14 小时前
【CentOS】虚拟机网卡IP地址修改步骤
linux·运维·centos
hhwyqwqhhwy14 小时前
linux 驱动开发相关
linux·驱动开发
IT逆夜15 小时前
实现Yum本地仓库自动同步的完整方案(CentOS 7)
linux·运维·windows