C语言linux文件结尾标志符,[原创]Linux arm 启动 c语言部分详解第五讲(从console_init到结尾)...

[原创]Linux arm启动c语言部分详解第五讲

written by leeming

欠下的债始终是要还的,过年前基本把linux c语言启动部分看了下,对于工程来说(如何移植一个新内核,为什么要这样移植已经能够讲明白了),后来继续深入下去的时候发现没有linux内存管理,进程管理的知识的话下面的内容根本无法看下去了。

我今天也没打算把这部分讲解,只准备稍微带一下(这部分水太深了,我对其的理解也不透彻,而且我向来认为抓住系统的框架主线才是对理解最有帮助的,具体的细节可以慢慢研读)。

继续,上一讲到了start_kernel的time_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();

{

initcall_t *call;

/* Setup the default TTY line discipline. */

/*****************************************************************************/

/*这段code主要的用途就是

注册tty线路规程的,大家研究tty的驱动就会发现了在用户和硬件之间tty的驱动是分了三层的

最底层当然是tty驱动程序了,主要负责从硬件接受数据,和格式化上层发下来的数据后给硬件。

在驱动程序之上就是线路规程了,他负责把从tty核心层或者tty驱动层接受的数据进行特殊的按着某个协议的格式化,就像是

ppp或者蓝牙协议,然后在分发出去的。

在tty线路规程之上就是tty核心层了。

*/

(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

/*

在vmlinux.lds.S中连接脚本汇编中有这段代码

__con_initcall_start = .;

*(.con_initcall.init)

__con_initcall_end = .;

因此我们再此处调用的就是con_initcall.init段的代码,

将fn函数放到.con_initcall.init的输入段中,如下:

#define console_initcall(fn) \

static initcall_t __initcall_##fn \

__attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn

*/

//在我们串口驱动里面有这么一个注册语句:console_initcall(serial8250_console_init);

//因此我们的控制台初始化流程就是:start_kernel->console_init->serial8250_console_init

call = __con_initcall_start;

while (call < __con_initcall_end) {

(*call)();

call++;

}

}

if (panic_later)

panic(panic_later, panic_param);

profile_init();

{

if (!prof_on)

return;

/* only text is profiled */

//profile是用来对系统剖析的,在系统调试的时候有用

//需要打开内核选项,并且在bootargs中有profile这一项才能开启这个功能

/*

profile只是内核的一个调试性能的工具,这个可以通过menuconfig中profiling support打开。

1.如何使用profile:

首先确认内核支持profile,然后在内核启动时加入以下参数:profile=1或者其它参数,新的内核支持profile=schedule 1

2.内核启动后会创建/proc/profile文件,这个文件可以通过readprofile读取,

如readprofile -m /proc/kallsyms | sort -nr > ~/cur_profile.log,

或者readprofile -r -m /proc/kallsyms |sort -nr,

或者readprofile -r && sleep 1 && readprofile -m /proc/kallsyms |sort -nr >~/cur_profile.log

3.读取/proc/profile可获得哪些内容?

根据启动配置profile=?的不同,获取的内容不同:

如果配置成profile=?可以获得每个函数执行次数,用来调试函数性能很有用

如果设置成profile=schedule ?可以获得每个函数调用schedule的次数,用来调试schedule很有用

*/

prof_len = (_etext - _stext) >> prof_shift;

prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t));

}

//只是打开了cpsr的I位(I = 0)

local_irq_enable();

/*注意熊出没(红色部分太过恶心,暂时不讲解)*/

/*初始化dentry和inode缓冲队列的hash表,这一部分是和文件系统相关的,以后再仔细看*/

vfs_caches_init_early();

cpuset_init_early();

/*最后内存初始化,释放前边标志为保留的所有页面,这个函数结束之后就不能使用alloc_bootmem**等申请地段内存的函数了*/

mem_init();

/* slab初始化*/

kmem_cache_init();

setup_per_cpu_pageset();

/*初始化非统一内存访问系统中的内存的策略,一般用于多处理器系统,在我们这里为空*/

numa_policy_init();

//不同的architecture可以重载这个函数,默认为null

if (late_time_init)

late_time_init();

//这段代码很有意思,可以细读一下,看看Linus的代码

calibrate_delay();

pidmap_init();

pgtable_cache_init();

prio_tree_init();

anon_vma_init();

#ifdef CONFIG_X86

if (efi_enabled)

efi_enter_virtual_mode();

#endif

//num_physpages是在mem_init函数中赋值的

fork_init(num_physpages);

proc_caches_init();

buffer_init();

unnamed_dev_init();

key_init();

security_init();

vfs_caches_init(num_physpages);

radix_tree_init();

signals_init();

/* rootfs populating might need page-writeback */

page_writeback_init();

#ifdef CONFIG_PROC_FS

proc_root_init();

{

//为proc文件系统索引节点创建高速缓存内存描述结构

int err = proc_init_inodecache();

if (err)

return;

//注册proc文件系统

err = register_filesystem(&proc_fs_type);

if (err)

return;

/*该函数基本完成三个步骤,首先调用read_super()函数,在这个函数里,

VFS将为proc文件系统分配一个超级块结构,并设置s_dev,s_flags等域,然后,

将调用proc文件系统的自己的read_super例程,对应proc文件系统,该例程是proc_read_super(),

该例程将设置超级块结构的其他值。我们将在下一节进行分析。

其次,使用add_vfsmnt()函数建立proc文件系统的vfsmount结构,并将其加入到已装载文件系统的链表中(可参考图-xx)。

最后,返回该vfsmount结构,并利用返回值,使用指针proc_mnt指向该vfsmount结构。*/

proc_mnt = kern_mount(&proc_fs_type);

err = PTR_ERR(proc_mnt);

if (IS_ERR(proc_mnt)) {

unregister_filesystem(&proc_fs_type);

return;

}

/*创建一些文件,通过create_proc_read_entry与create_seq_entry创建

可读文件和读写文件*/

proc_misc_init();

//利用proc_mkdir创建目录,也可以通过symlinks以及proc_symlink实现

proc_net = proc_mkdir("net", NULL);

proc_net_stat = proc_mkdir("net/stat", NULL);

#ifdef CONFIG_SYSVIPC

proc_mkdir("sysvipc", NULL);

#endif

#ifdef CONFIG_SYSCTL

proc_sys_root = proc_mkdir("sys", NULL);

#endif

#if defined(CONFIG_BINFMT_MISC) || defined(CONFIG_BINFMT_MISC_MODULE)

proc_mkdir("sys/fs", NULL);

proc_mkdir("sys/fs/binfmt_misc", NULL);

#endif

proc_root_fs = proc_mkdir("fs", NULL);

proc_root_driver = proc_mkdir("driver", NULL);

proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */

#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)

/* just give it a mountpoint */

proc_mkdir("openprom", NULL);

#endif

proc_tty_init();

#ifdef CONFIG_PROC_DEVICETREE

proc_device_tree_init();

#endif

proc_bus = proc_mkdir("bus", NULL);

}

#endif

//用于smp架构,我们没有

cpuset_init();

//不同架构对这一段的定义不同,在arm中是验证内存一致性

check_bugs();

主要调用了check_writebuffer_bugs函数

{

struct page *page;

const char *reason;

unsigned long v = 1;

printk(KERN_INFO "CPU: Testing write buffer coherency: ");

//申请一页空间,并将该页的struct page指针给page

page = alloc_page(GFP_KERNEL);

if (page) {

unsigned long *p1, *p2;

//配置二级页表描述符

pgprot_t prot = __pgprot(L_PTE_PRESENT|L_PTE_YOUNG|

L_PTE_DIRTY|L_PTE_WRITE|

L_PTE_BUFFERABLE);

//将page所指向的物理页映射为两个不同的虚拟地址

//p1, p2

p1 = vmap(&page, 1, VM_IOREMAP, prot);

p2 = vmap(&page, 1, VM_IOREMAP, prot);

if (p1 && p2) {

//通过向p1,p2写数据来验证,返回0表明正确

v = check_writebuffer(p1, p2);

reason = "enabling work-around";

} else {

reason = "unable to map memory\n";

}

//取消这两块虚拟地址的映射

vunmap(p1);

vunmap(p2);

put_page(page);

} else {

reason = "unable to grab page\n";

}

if (v) {

printk("failed, %s\n", reason);

shared_pte_mask |= L_PTE_BUFFERABLE;

} else {

printk("ok\n");

}

}

//arm架构,用不起来ACPI

acpi_early_init(); /* before LAPIC and SMP init */

/* Do the rest non-__init'ed, we're now alive */

rest_init();

{

//新建init进程,也就是1号进程,原来的为0号进程

//linux进程流程:0号内核进程->1号内核进程->1号用户进程

kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);

numa_default_policy();

unlock_kernel();

/*

* The boot idle thread must execute schedule()

* at least one to get things moving:

*/

//preempt是开启抢占式内核的时候才生效的

preempt_enable_no_resched();

//进入idle之前,主动调用下schedule,看有没有其他进程

schedule();

preempt_disable();

/* Call into cpu_idle with preempt disabled */

cpu_idle();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值