Linux在启动时利用NFS方式挂在根文件系统时无法初始化console的问题

    技术2025-06-26  13

            前几天在FriendlyARM的 mini2440板子上跑Linux的时候碰到了一个很费解的现象,按照教程上说,是可以利用NFS的方式来挂载Host开发机上的根目录来开发的。教程中给的kernel_cmd_line 是:

    param set linux_cmd_line "console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.111:/opt/FriendlyARM/mini2440/root_qtopia ip=192.168.1.70:192.168.1.111:192.168.1.111:255.255.255.0:sbc2440.arm9.net:eth0:off"

            后面很长的部分主要是ip地址设置和nfs的目录,这里首先碰到的问题是它写了console=ttySAC0。这个不是很理解为什么,ttySAC0不是应该是windows上的超级终端么,我是在Fedora13下面用minicom连接的,系统里面显示的设备名字是/dev/ttyUSB0。但是奇怪的是这个参数是对的,我改成ttyUSB0之后反而启动的时候就收不到串口的数据了。

            之后就是正常启动,然后到VFS mount之后突然说 unable to initial console之类的,然后串口就失去控制了,但是板子上的系统还是可以正常启动,而且起来之后我尝试着打开终端然后往串口发送数据居然成功了。也就是说通信驱动之类的问题应该都是好的,只是设备文件名弄的不太对之类的问题。手动建立个这个设备应该就可以了。网上找到有人说在/dev下面 mknod –m 600 console c 5 1就可以了,我本来觉得是不行的,结果死马当活马医的试了试居然行了,我觉得很奇怪。按照我的理解,虽然我在host上面在mknod了这个设备,但是系统启动的时候会重新挂载一个/dev文件系统在/dev下面啊,应该会有个脚本执行了类似于mount –t /dev 之类的命令,那么原来的创建的文件不是应该就看不见了么?不理解是为什么。

            我还是想知道这个设备启动的过程中在何时由哪个函数创建的。查了些资料以及内核的代码,大概有以下的理解:

            首先真正意义上的Linux开始启动是在bootloader将系统的控制权交给Linux Kernel的时候开始的,也就是在start_kernel()这个函数中。这个函数定义在init/main.c里。然后你会发现在这个函数干了一大堆初始化的事情,其中有一个函数叫做console_init(),

    /*      * HACK ALERT! This is early. We're enabling the console before      * we've done PCI setups etc, and console_init() must be aware of      * this. But we do want output early, in case something goes wrong.      */     console_init();     if (panic_later)         panic(panic_later, panic_param);

            这应该是个对我们有用的函数,就像注释里面说的,This is early,我们先记着这回子事,等一会儿再说它,先往后看。

            正常人都知道start_kernel()这个函数如果返回了的话导致的唯一结果必定是系统会挂掉,所以它一定会在哪个函数中一直跑下去。然后看来看去发现最后一个函数:rest_init() 函数貌似比较像,

    /* Do the rest non-__init'ed, we're now alive */ rest_init();

           人家注释里面也写着呢,we’re now alive,看来应该是进到这个里面去了,看看这个函数:

    static noinline void __init_refok rest_init(void)     __releases(kernel_lock) {     int pid;

        rcu_scheduler_starting();     /*      * We need to spawn init first so that it obtains pid 1, however      * the init task will end up wanting to create kthreads, which, if      * we schedule it before we create kthreadd, will OOPS.      */     kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);     numa_default_policy();     pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);     rcu_read_lock();     kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);     rcu_read_unlock();     complete(&kthreadd_done);     unlock_kernel();

        /*      * The boot idle thread must execute schedule()      * at least once to get things moving:      */     init_idle_bootup_task(current);     preempt_enable_no_resched();     schedule();     preempt_disable();

        /* Call into cpu_idle with preempt disabled */     cpu_idle(); }

            我看到这个函数先注意它的最后,这丫调用了cpu_idle(),那说明开始死循环了。果然前面调用了schedule()开启了调度器,开始任务调度。看到这里心里就踏实了,事儿肯定是在这个之前干的,这之后没东西了。于是我们往前看,发现它在这里开始分支了。用的是kernel_thread(),产生两个内核线程,一个是kernel_init(),一个是kthreadd,内核线程的守护线程。我们关注的事情肯定是在kernel_init()里面,再往下找到kernel_init()函数,发现了令人惊喜的东西:

    /* Open the /dev/console on the rootfs, this should never fail */     if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)        printk(KERN_WARNING "Warning: unable to open an initial console./n");

            我们发现红的那一句就是我们见到的提示信息,然后看了一下大概意思应该是说,如果在这个时候能够成功打开/dev/console这个文件,就没事,否则就有那个提示。也就是说到这一刻,应该已经有/dev/console这个设备创建好了,所以我要关注的一定是在这前面的某个地方。

     

    未完待续,还是没找到到底是哪里创建的。

    最新回复(0)