004@ kernel 的配置和编译总结 分析2

sudo chmod 777 * -r


kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);


kernel_init是要执行的函数的指针,NULL表示传递给该函数的参数为空,

CLONE_FS | CLONE_SIGHAND    为do_fork    产生线程时的标志,表示进程间的fs信息共享,信号处理和块信号共享


start_kernel--->vfs_caches_init----->mnt_init()


mnt_init()函数调用init_rootfs()和init_mount_tree()


init_rootfs函数注册一个文件系统,此文件系统即是vfs文件系统



init_rootfs()   通过调用   register_filesystem(&rootfs_fs_type)     函数来完成 rootfs 文件系统
注册的    (struct file_system_type rootfs_fs_type)    注册完文件系统之后,就可以直接挂载该文件系统了

init_mount_tree( )   函数会调用   do_kern_mount("rootfs", 0, "rootfs", NULL)      来挂载前面已经注册了的 rootfs 文件系统。
这看起来似乎有点奇怪,因为根据前面的说法,似乎是应该先有挂载目录,然后再在其上挂载相应的文件系统,然而此时
VFS 似乎并没有建立其根目录。没关系,这是因为这里我们调用的是   do_kern_mount(), 这个函数内部自然会创建我们
最关心也是最关键的根目录(在 Linux 中,目录对应的数据结构是 struct dentry)



init_mount_tree()       最终目的不过是要在内存中建立一颗 VFS 目录树而已,更确切地说, init_mount_tree() 这个函数
为 VFS 建立了根目录 "/",而一旦有了根,那么这棵数就可以发展壮大,比如可以通过系统调用 sys_mkdir 在这棵树
上建立新的叶子节点等,所以系统设计者又将 rootfs 文件系统挂载到了这棵树的根目录上


在noinitramfs.c中


static int __init default_rootfs(void)
{
     int err;

     err = sys_mkdir((const char __user __force *) "/dev", 0755);
     if (err < 0)
          goto out;

     err = sys_mknod((const char __user __force *) "/dev/console",
               S_IFCHR | S_IRUSR | S_IWUSR,
               new_encode_dev(MKDEV(5, 1)));
     if (err < 0)
          goto out;

     err = sys_mkdir((const char __user __force *) "/root", 0700);
     if (err < 0)
          goto out;

     return 0;

out:
     printk(KERN_WARNING "Failed to create a rootfs\n");
     return err;
}
rootfs_initcall(default_rootfs);



宏rootfs_initcall     包含的的函数default_rootfs   定义在.initcallrootfs.init  段中,调用do_basic_setup    函数中的
do_initcalls    时就会调用该函数,default_rootfs函数主要是在建好的根目录里创建了目录/dev,目录/root,
和文件/dev/console

由此,总结出:init_rootfs   函数是注册一个文件系统      init_mount_tree函数是构建一个根目录"/",并挂载之前注册的文件系统了。    

default_rootfs函数   创建两个目录/dev /root和一个文件   /dev/console

以上三部即为创建好了虚拟文件系统,即在内存中构建了VFS目录树,现在可以挂载具体的文件系统到vfs上


-------------------------------
我们准备将来自主硬盘第 2 分区(hda2)上的 ext2 文件系统安装到前面创建的 "/dev" 目录中。那么对于 sys_mount()
函数的调用便具体为:

sys_mount("hda2","/dev ","ext2",…);



设定一个进程的当前工作目录是通过系统调用 sys_chdir()   进行的。初始化期间,Linux 在将 hda1 上的 ext2 文件系统
安装到了 "/root" 上后,通过调用 sys_chdir("/root") 将当前进程,也就是 init_task 进程的当前工作目录(pwd)
设定为 ext2 文件系统的根目录。记住此时 init_task进程的根目录仍然是图 3 中的 dentry,也就是 VFS 树的根目录,
这显然是不行的,因为以后 Linux 世界中的所有进程都由这个 init_task 进程派生出来,无一例外地要继承该进程的
根目录,如果是这样,意味着用户进程从根目录搜索某一目录时,实际上是从 VFS 的根目录开始的,而事实上却是
从 ext2 的根文件开始搜索的。这个矛盾的解决是靠了在调用完 mount_root() 函数后,系统调用的下面两个函数:

sys_mount(".", "/", NULL, MS_MOVE, NULL); 将挂载点从当前目录移到根目录,即将/dev/mtdblock2挂到根目录下,
sys_chroot("."); 将当前目录当作系统的根目录,至此虚拟系统根目录文件系统切换到了实际的根目录文件系统。
其主要作用便是将 init_task 进程的根目录转化成安装上去的 ext2 文件系统的根目录。有兴趣的读者可以自行
去研究这一过程。


在linux上,任何磁盘或磁盘的分区在被使用之前必须首先被格式化成某个特定的文件系统  (如ext2,ext3等),
然后挂载到某个系统的目录树上,只有这样,该磁盘或分区才能被用户看到,使用。挂载一个文件系统的常用格式如下:

mount -t ext3 -o option /device/hda  /home/mountdir


    其中-t选项后面的参数代表挂载的文件系统的类型;-o后面的参数是一些可选的选项,具体内容可
    参考google;/device/hda代表需要挂载的的磁盘或者分区的名称;/home/mountdir代表需要将文件挂载的目录。
    下面我们就对linux内核如何实现mount进行具体的分析,参考linux内核版本2.6.36。




rootfs是一个特定的ramfs(或tmpfs,如果tmpfs被启用)的实例,它始终存在于linux2.6的系统中。rootfs不能被卸载
(与其添加特殊代码用来维护空的链表,不如把rootfs节点始终加入,因此便于kernel维护。rootfs是ramfs的一个空实例,
占用空间极小)。大部分其他的文件系统安装于rootfs之上,然后忽略它。它是内核启动初始化根文件系统.


什么是ramdisk?
linux2.6版本之后都不在使用ramdisk了,2.4中还在使用。ramdisk是一种将内存
中的的一块区域作为物理磁盘来使用的一种技术,也可以说,ramdisk是在一块内存区 域中创建的块设备,用于存放
文件系统。对于用户来说,可以把ramdisk与通常的硬盘分区同等对待来使用。ramdisk不适合作为长期保存文件的介质,
掉电后ramdisk的内容会消失。


为了能够使用ramdisk 你的内核必须要支持ramdisk,即:在编译内核时,要选中RAM disk support这一选项,会在配置文
件中定义CONFIG_BLK_DEV_RAM。同时为了让内核有能力在内核加载阶段就能装入ramdisk,并运行其中的内容,
要选中initial RAM disk(initrd) support 选项,会在配置文件中定义CONFIG_BLK_DEV_INITRD。


当系统启动的时候,bootloader    会把initrd文件读到内存中,然后把initrd文件在内存中的起始地址和大小传递给内核。
内核在启动初始化过程中会解压缩initrd文件,然后将解压后的initrd挂载为根目录,然后执行根目录
中的/init脚本(cpio格式的initrd为/init,而image格式的initrd<也称老式块设备的initrd或传统的文件镜像格式
的initrd>为/initrc),您就可以在这个脚本中运行initrd文件系统中的udevd,让它来自动加载realfs(真实文件系统)
存放设备的驱动程序以及在/dev目录下建立必要的设备节点。在udevd自动加载磁盘驱动程序之后,就可以mount真正的
根目录,并切换到这个根目录中来。


initrd总的来说目前有两种格式:image格式和cpio格式。image格式也叫文件系统镜像文件(老式块设备的initrd文件),
主要在linux 2.4内核中使用流行;在linux 2.5内核开始引入initramfs技术,initramfs实际上已经
克服了imgae-initrd的缺点,本质上也是cpio格式的initrd,只不过是和内核编译到了一个image文件,
放在了.init.ramfs段内;到linux2.6的内核支持两种格式的initrd,即image-initrd和cpio-initrd,此时的cpio-initrd文件
已不再编译进内核而单独成一文件,使用cpio工具生成。



image-initrd可以使内核在启动阶段可以顺利地完成各种存储介质的驱动的加载和realfs文件系统的挂载,
然而image-initrd存在以下缺点:


1.image-initrd大小是固定的,例如上面的压缩之前的initrd大小是4M(4k*1024),假设您的根目录
(上例中的/opt/filesystem)总大小仅仅是1M,它仍然要占用4M的空间。如果您在dd阶段指定大小为1M,后来发现
不够用的时候,必须按照上面的步骤重新来一次。


2.image-initrd是一个虚拟的块设备,您可是使用fdisk对这个虚拟块设备进行分区。在内核中,对块设备的读写还
要经过缓冲区管理模块,也就是说,当内核读取initrd中的文件内容时,缓冲区管理层会认为下层的块设备速度比较慢,
因此会启用预读和缓存功能。这样initrd本身就在内存中,同时块设备缓冲区管理层还会保存一部分内容



initramfs
为了避免上述缺点,在linux2.5中出现了initramfs,它的作用和initrd类似,只是和内核编译成一个文件
(该initramfs是经过gzip压缩后的cpio格式的数据文件),该cpio格式的文件被链接进了内核中特殊的
数据段.init.ramfs上,其中全局变量__initramfs_start和__initramfs_end分别指向这个数据段的起始地址和结束地址。
内核启动时会对.init.ramfs段中的数据进行解压,然后使用它作为临时的根文件系统。


要制作这样的内核,我们只需要在make menuconfig中配置以下选项就可以了:
General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support


(/opt/filesystem) Initramfs source file(s)


其中/opt/filesystem就是我们的小型根目录,这里可以使一个现成的gzip压缩的cpio文件,也可以使一个目录,
更可以是txt格式的配置文件,如下面的实例:
dir /dev 755 0 0
nod /dev/console 644 0 0 c 5 1
nod /dev/loop0 644 0 0 b 7 0
dir /bin 755 1000 1000
slink /bin/sh busybox 777 0 0
file /bin/busybox initramfs/busybox 755 0 0




cpio-initrd
这里所说的initrd格式和编译进内核的initramfs格式是一样的,都是cpio,也
被称为外部initramfs,它是独立存在的,需要bootloader将其加载进内存特定地址,然后将地址和大小传递给内核,
在内核的初始化阶段来进行相应的处理。
这种initrd可以使用cpio命令来实现,如下:

# sudo find /opt/filesystem/ -depth | cpio -c -o > initrd.img
# gzip -9 initrd.img
这样得到的initrd就是cpio格式的,而且这个文件的大小是可变的,意思就是根据
你的filesystem的大小而变化,不会像前面的image格式的initrd那样大小固定了。当然我觉得前面的initramfs的大小也是可变的了。


dir /proc 755 0 0
dir /sys 755 0 0
dir /mnt 755 0 0
file /init initramfs/init.sh 755 0 0


如果指定的是一个目录而不是一个像这样的配置文件,内核在编译的时候会从指定的目录创建一个
配置文件(usr/Makefile调用scripts/gen_initramfs_list.sh来生成),作为usr/gen_init_cpio.c文件的输入,最后生成一个
usr/initramfs_data.cpio.gz文件,通过usr/initramfs_data.S包含到.init.ramfs段中去,最后生成zImage


bootloader传递给内核的关于initrd的参数


root=
该参数告诉内核以那个设备作为根文件系统,通常由如下形式:


root=/dev/ramx
root=/dev/mtdblockx rw rootfstype = jffs2
root=31:0x rootfstype = jffs2
root=/dev/nfs nfsroot=serverip:nfs_dir


第一种在使用ramdisk和image-initrd的时候是必须的,initramfs和cpio-initdrd因为使用的是系统内一直存在的rootfs,
所以该参数可以省略。


第三、四种基本通用,只是,第四种使用的设备号和分区号来指定,比如root=/dev/mtdblock4 等价于 root=31:04,
意思就是使用nand设备上的第四分区来作为根文件系统, 同时用rootfstype指定文件系统类型。
最后一种就是挂在nfs文件系统了,除了要指定root类型之外,还需要指定服务器上的某个目录来作为挂载的根目录。


使用initramfs和cpio-initrd
不需要root=xxx参数,而init=/init是必须的


2. 使用image-initrd(记得打开内核对ramdisk和initrd的支持)
init=/initrc,下面是传递不同root=xxx的情况


a. root=/dev/ramx,将image-initrd作为真实的文件系统在使用


b. root=/dev/mtdblockx rw rootfstype = jffs2,这样image-initrd只是作为一个中间过渡的文件系统,最终还是需要
依靠内核1号线程将真实的文件系统挂上来。

start_kernel()       函数最后会调用rest_init(),在该函数中会创建两个内核线程kernel_init和kthreadd(2.6.14内核中
只创建了一个内核线程init,但是所做工作基本一样),内核线程创建后,原来的系统0号进程进入idle状态。


对initrd的处理函数主要有两个:

populate_rootfs()和prepare_namespace(),针对不同的格式,处理情况各不相同。


populate_rootfs()    函数在2.6.14和2.6.29中执行的位置不一样。2.6.14中在do_basic_setup()函数执行之前,而2.6.29版本
中将其移植do_basic_setup()函数中执行,怎么讲呢?高版本中将populate_rootfs()移到do_initcalls()中执行,也就是
编译的时候将函数放在了.initcallrootfs.init段中。

CONFIG_BLK_DEV_RAM           – 该宏被定义,表明内核支持ramdisk,make menuconfig中需要选中RAM disk support一项。
CONFIG_BLK_DEV_INITRD       – 该宏被定义,表明内核有能力在内核加载阶段就能装入RAMDISK,并运行其中的内容,


make menuconfig中需要选中initial RAM disk(initrd) support一项。
下面是各种initrd的不同称呼:

image-initrd : 老式块设备的initrd,也叫传统的文件镜像格式的initrd
initramfs :      和内核编译到一起的cpio格式的initrd
cpio-initrd :     cpio格式的独立文件的initrd






populate_rootfs           函数分析
static int __init populate_rootfs(void)
{
char *err = unpack_to_rootfs(  __initramfs_start,    __initramfs_end - __initramfs_start,   0);

/***
编译进内核的initramfs位于段.init.ramfs中,用全局变量__initramfs_start和__initramfs_end分

别指向这个数据段的内存起始地址和结束地址(虚拟地址)
所以这里了如果__initramfs_end - __initramfs_start等于0,那么unpack_to_rootfs函数不会做任何事情,直接退出。
系统认为该initrd不是initramfs文件,所以需要到后面去处理。
***/

if (err)
panic(err);
// 判断是否加载了initrd,bootlaoder会将initrd加载到内存地址initrd_start,
// 前面已经说到了如何给内核传递这些参数

if (initrd_start) {


#ifdef CONFIG_BLK_DEV_RAM
// CONFIG_BLK_DEV_RAM和CONFIG_BLK_DEV_INITRD经常是同时定义


int fd;
printk(KERN_INFO "checking if image is initramfs...");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 1);


/***
判断加载的是不是cpio-initrd。实际上 unpack_to_rootfs有两个功能一个是释放cpio包,另一个就是判断是不是cpio包, 这是通过最后一个参数来区分的, 0:释放 1:查看。
***/


if (!err) {


// 如果是cpio-initrd则利用函数unpack_to_rootfs将其内容释放
// 到rootfs中
printk(" it is\n");
unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 0);
free_initrd();                                   // 释放掉原来存放cpio-initrd的内存空间
return 0;
}


/***
如果执行到这里,说明这是旧的块设备格式的initrd。那么首先在前面挂载的根目录rootfs上创建一个initrd.image文件,
再把initrd_start到initrd_end的内容写入到/initrd.image中,最后释放initrd占用的内存空间(它的副本已经保存到/initrd.image中了。)。
***/

printk("it isn't (%s); looks like an initrd\n", err);


fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
if (fd >= 0) {
sys_write(fd, (char *)initrd_start,
initrd_end - initrd_start);
sys_close(fd);
free_initrd();
}

#else
// 如果没有定义支持image-initrd的宏,那么直接通过下面的代码来处理cpio-initrd
// 实际上和上面处理cpio-initrd是一样的
printk(KERN_INFO "Unpacking initramfs...");
err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start, 0);
if (err)
panic(err);
printk(" done\n");
free_initrd();
#endif
}
return 0;
}


rootfs_initcall(populate_rootfs);
上面的populate_rootfs()执行完后,如果是initramfs和cpio-initrd的话,都已经将他们释放到了前面初始化完成的rootfs中去了,那么根目录下肯定会出现init文件。

而如果是image-initrd,那么只会在rootfs根目录下出现一个initrd.image文件。



static int __init kernel_init(void * unused)
{

if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";


// cmdline中存在rdinit=xxx时,ramdisk_execute_command才不为NULL

if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();


}

/***
这里尝试访问ramdisk_execute_command,默认为/init,如果访问失败,说明根目录上不存在这个文件。

于是调用prepare_namespace(),进一步检查是不是旧的块设备的initrd。
(在这种情况下,还是一个块设备镜像文件/initrd.image,所以访问/init文件失败。)。
如果是initramfs或者cpio-initrd就会直接执行init_post()函数,然后在后面执行/init文件
***/

init_post();
return 0;
}



五、老式块设备的initrd的处理函数          prepare_namespace()分析
cmdline传了里的参数需要包括:
root=/dev/mtdblockx rw 或者{root=/dev/ramx } init=/initrc
谨记root=指定的设备是最终的真实文件系统的。在处理image-initrd的时候指定了它,那么系统就会认为你
这个initrd就是我的真实文件系统了,会跳过很多步骤不执行的。


void __init prepare_namespace(void)
{
int is_floppy;

if (root_delay) {
printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}

wait_for_device_probe();

md_run_setup();

if (saved_root_name[0]) {
// 如果cmdline有传递root=xxx,那么这里就会将其设备号保存下来

root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}


ROOT_DEV = name_to_dev_t(root_device_name);

/***
将存储真实文件系统的设备的设备号保存到ROOT_DEV中,如果cmdline传递的是/dev/ramx话,这里的ROOT_DEV = Root_RAM0,
如果不是,那么是什么就是什么,后面会根据这个做出不同的事情
***/

if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}


/***
saved_root_name全局数组保存的是由命令行传进来的root=的值,比如你传递了root=/dev/ramx或者root=/dev/mtdblockx rw,

那么上面的if条件成立,那么就通过name_to_dev_t(root_device_name)函数获得该设备的设备号ROOT_DEV。否则就跳过该段不执行。


***/

if (initrd_load())         /*** 详见后面注释 ***/
/***
加载老式块设备的initrd,这里也分两种情况,一种是将该initrd作为真实文件系统返回1另外一种就是作为一种
过渡的文件系统而已,此时返回0。
***/
goto out;

/***
如果要使用老式块设备的initrd作为真实的文件系统的话,需要在cmdline传入一下参数:root=/dev/ramx init=/initrc,
同时在内核编译的时候需要打开支持ramdisk和initrd
***/


/* wait for any asynchronous scanning to complete */


if ((ROOT_DEV == 0) && root_wait) {


printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(100);
async_synchronize_full();
}


/* 如果root=正常传递,该if不会成立 */



is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
/***
如果cmdline传递了正确的root=/dev/ramx的话,mount_root()中直接创建一个Root_RAM0类型的/dev/root节点
(前面initrd_load()中将image-initrd已经导入到了Root_RAM0ramdisk中了),所以mount_root()中就可以直接使用。
***/

if (is_floppy && rd_doload && rd_load_disk(0))
ROOT_DEV = Root_RAM0;

mount_root();          // 将ROOT_DEV设备上的真实文件系统mount到rootfs的/root目录中
out:
// 注意:上面的过程都已经将真实文件系统挂载到了    /root     (这里的/还是rootfs),
// 并且当前目录为/root


sys_mount(".", "/", NULL, MS_MOVE, NULL);
// mount当前目录为根目录,覆盖掉原来的rootfs
sys_chroot(".");
// 切换当前目录为程序执行所参考的根目录位置,至此,真实文件系统
// 挂载完毕
}

int __init initrd_load(void)
{


if (mount_initrd) {


create_dev("/dev/ram", Root_RAM0);
// 建立一个Root_RAM0类型的/dev/ram设备节点,实际上Root_RAM0设备就是
// 一ramdisk

if (rd_load_image("  /initrd.image") && ROOT_DEV != Root_RAM0) {


// rd_load_image( )  将文件initrd.image加载进了    /dev/ram0     中去了
sys_unlink("/initrd.image");                                     // 删除文件initrd.image
handle_initrd();                                                        // handle_initrd()函数负责对initrd进行具体的处理
return 1;                                                                   /* image-initrd 作为中间过渡的文件系统 */

}

/***
/initrd.image文件保存的就是image-initrd,rd_load_image函数执行具体的加载操作,
将image-nitrd的文件内容释放到设备类型为Root_RAM0的ramdisk中去(节点名为ram)。判断ROOT_DEV!=Root_RAM0的含义
是(ROOT_DEV不能=0,否则mount的时候会出错),正如前面所说,如果你在bootloader里配置了root=/dev/ramx,则实际
上真正的根设备就是这个initrd了,所以就不把它作为initrd处理 ,而是作为真实文件系统来处理。
***/
}
sys_unlink("/initrd.image");// 删除文件initrd.image
return 0; /* image-initrd作为真实的文件系统 */
}



==========================================================================================================================
根文件系统有两种,一种是虚拟根文件系统,另外一种是真实的根文件系统。一般情况下,会首先在虚拟的根文件系统中做一部分工作,然后切换到真实的根文件系统下面。

笼统的来说,虚拟的根文件系统包括三种类型,即(本人认为这不是什么虚拟的根文件系统,而是内存根文件系统)Initramfs、cpio-initrd和image-initrd。


===============================================================================
2.5.2 proc 文件系统
proc是一个重要虚拟文件系统,通过它里面的一些文件,可以获取系统状态信息并修改某些系统的配置信息。proc文件系统本身不占用
磁盘空间,它仅存在于内存之中,为操作系统本身和应用程序之间的通信提供了一个安全的接口。当我们在内核中添加了新功能或设备驱
动时,经常需要得到一些系统状态的信息,一般这样的功能可能需要经过一些象ioctl()这样的系统调用来完成。系统调用接口对于一些
功能性的信息可能是适合的,因为应用程序必须将这些信息读出后再做一定的处理。但对于一些实时性的系统信息,例如内存的使用状况,
或者是驱动设备的统计资料等,我们更需要一个比较简单易用的接口来取得它们。proc文件系统就是这样的一个接口,我们可以简单的用
cat、strings程序来查看这些信息。例如,执行下面的命令:


cat /proc/filesystems    //操作系统支持的文件系统类型
cat /proc/meminfo        //内存的实时信息,内存大小等
cat /proc/partitions     //存储器分区信息
cat /proc/cpuinfo        //查看cpu信息


同样的,free、df、top、ps等程序的功能实现,强烈依赖于proc文件系统,为了使用那些程序,一定要使内核支持proc文件系统,
并将其挂接到根文件系统的/proc目录下。其他使用 /proc 文件系统的例子:


processor        : 0
vendor_id        : AuthenticAMD
processor        : 1
vendor_id        : AuthenticAMD
model name    : AMD Athlon(tm) 64 X2 Dual Core CPU 5000+


1.vmware 虚拟机无法正常启动
在Linux下,单个进程的最大内存使用量受/proc/sys/kernel/shmmax中设置的数字限制(单位为字节),
例如 ubuntu 8.10 的shmmax默认值为33554432字节(33554432bytes/1024/1024=32MB)。

2.scratchbox 开发工具不能登录
/scratchbox/login
Inconsistency detected by ld.so: rtld.c: 1192: dl_main: Assertion `(void *) ph->p_vaddr ==
_rtld_local._dl_sysinfo_dso' failed!


NOTE: on Ubuntu installation, you have to disable VDSO to make Scratchbox work fine,
or you'll get errors like this:


在 ubuntu 系统中,我们必须关闭 VDSO 标记,以便scratchbox能正常工作
echo 0 | sudo tee /proc/sys/vm/vdso_enabled
echo 4096 | sudo tee /proc/sys/vm/mmap_min_addr
vm.vdso_enabled = 0
vm.mmap_min_addr = 4096
修改 /proc 文件系统值的方法


1.直接修改
echo "2147483648" | sudo tee /proc/sys/kernel/shmmax
echo 0 | sudo tee /proc/sys/vm/vdso_enabled
echo 4096 | sudo tee /proc/sys/vm/mmap_min_addr


2.将以下命令放入 /etc/rc.local 启动文件中:
echo "2147483648" > /proc/sys/kernel/shmmax
echo 0            > /proc/sys/vm/vdso_enabled
echo 4096         > /proc/sys/vm/mmap_min_addr

3.使用 sysctl 命令来更改 SHMMAX 的值:
sysctl -w kernel.shmmax=2147483648


4.内核参数插入到 /etc/sysctl.conf 启动文件中,使这种更改永久有效
echo "kernel.shmmax=2147483648" >> /etc/sysctl.conf
sudo sysctl –p
./system/core/logcat/logcat.cpp:403:   fd = open("/proc/cmdline", O_RDONLY);
./system/core/init/init.c:553:    char cmdline[1024];
./system/core/init/init.c:557:    fd = open("/proc/cmdline", O_RDONLY);
./system/core/init/init.c:580:    chmod("/proc/cmdline", 0440);
./system/core/init/bootchart.c:139:   proc_read("/proc/cmdline", cmdline, sizeof(cmdline));
./system/core/init/bootchart.c:319:   proc_read( "/proc/cmdline", cmdline, sizeof(cmdline) );
./system/core/init/bootchart.c:320:   s = strstr(cmdline, KERNEL_OPTION);
./system/core/rootdir/init.rc:162:    chown root radio /proc/cmdline


2.5.3 sysfs文件系统
与proc文件系统类似,sysfs文件系统也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在/sys目录下。

sysfs文件系统是Linux2.6内核引入的,它把连接在系统上的设备和总线组织成为一个分级的文件,使得它们可以在用户空间存取。

其实     sysfs  是从proc和devfs   中划分出来的。



一、devfs
   linux下有专门的文件系统用来对设备进行管理,devfs和sysfs就是其中两种。
在2.6内核以前一直使用的是devfs,devfs挂载于/dev目录下,提供了一种类似于文件的方法来管理位于/dev目录下的所有设备,我们知道
/dev目录下的每一个文件都对应的是一个设备,至于当前该设备存在与否先且不论,而且这些特殊文件是位于根文件系统上的,在制作文件
系统的时候我们就已经建立了这些设备文件,因此通过操作这些特殊文件,可以实现与内核进行交互。但是devfs文件系统有一些缺点,例如:
不确定的设备映射,有时一个设备映射的设备文件可能不同,例如我的U盘可能对应sda有可能对应sdb;没有足够的主/辅设备号,当设备过多
的时候,显然这会成为一个问题;/dev目录下文件太多而且不能表示当前系统上的实际设备;命名不够灵活,不能任意指定等等。


二、sysfs
      正因为上述这些问题的存在,在linux2.6内核以后,引入了一个新的文件系统sysfs,它挂载于/sys目录下,跟devfs一样它也是一个
虚拟文件系统,也是用来对系统的设备进行管理的,它把实际连接到系统上的设备和总线组织成一个分级的文件,用户空间的程序同样可以利用
这些信息以实现和内核的交互,该文件系统是当前系统上实际设备树的一个直观反应,它是通过kobject子系统来建立这个信息的,当一个
kobject被创建的时候,对应的文件和目录也就被创建了,位于/sys下的相关目录下,既然每个设备在sysfs中都有唯一对应的目录,那么也
就可以被用户空间读写了。用户空间的工具udev 就是利用了sysfs提供的信息来实现所有devfs的功能的,但不同的是udev运行在用户空间中,
而devfs却运行在内核空间,而且udev不存在 devfs那些先天的缺陷。很显然,sysfs将是未来发展的方向。


========================================================================================================
1. 什么是initrd.img,它有什么用?
   initrd.img是Linux启动过程中很重要的一个文件,如果你编译内核时将一部分功能编译为可加载模块。如果系统的一些设备的驱动编译为可加载模,那么启动时如果没有指定INITRD=/path_to_initrd.img,那么系统启动或者会失败,或者启动后会有设备无法使用(像网卡或者其它设备)。
比如我的Dell Precision 470 计算机的Adaptec HostRaid 39320B SCSI控制卡驱动就编为可加载模块,如果没指定initrd.img或者指定的initrd.img中并没有包含正确的驱动模块(有些硬件很多系列的驱动都是一个名称,像Adaptec 的一系列Ultra320卡用的驱动模块名称都是aic79xx.o,但当前很多2.4版本内核模块中并不支持较新的39320B驱动),则系统启动时会挂起,并报告"kernel panic: VFS: Unable to mount root fs on 08:06"的错误。


2. 拆解initrd.img
  很庆幸initrd.img可以进行拆解,或许这正是设计者高明所在。initrd.img不像通常的以.img为扩展名的ramdisk cramfs文件。它是经过用gzip -9进行压缩过的ramdisk文件。所以,如果直接用#mount initrd.img /temppath -o loop不能mount上,会报告你指定一个文件类型。 所以我拆解它的过程要先将其进行解压缩,然后再mount。下面是我的操作过程,可能有些命令用加些参数的方式更简捷,当我知道后会进行更新。
#cd 假设已经到你的initrd.img文件所在目录(最好先将其备份一个)
#mv  initrd.img   initrd.gz         <--在我的RHEL AS3 U3 /bsh下,不做这一步的话,用gunzip解压时会报告扩展名不对
#gunzip initrd.gz /tmp/initrd     <--解压后会生成一个不带扩展名的initrd文件
#mkdir /mnt/tmp
#mv /tmp/initrd /tmp/initrd.img  <--将解压出的initrd文件加个.img的扩展名,在我的RHEL AS3 U3 下不做这一步,mount时会出错
#mount /tmp/initrd.img  /mnt/tmp -o loop  <--mount成功后,/mnt/tmp目录中将能看到initrd.img中的所有文件及目录
#cp -a /mnt/tmp/*   /tmp/initrd.new   <--拷贝一份方便编辑
#umount  /mnt/tmp  
#cd /tmp/initrd.new  
切换到/tmp/initrd.new目录后,你可以按需要进行编辑。比如更新一些设备驱动模块,或者对其中的一些启动过程中会执行的shell script进行修改。
所有想要的修改完成后,进行打包生成新的initrd.img文件。方法如下:
#mkcramfs /tmp/initrd.new /tmp/newinitrd
#gzip -9 /tmp/newinitrd /tmp/newinitrd.gz
#mv /tmp/newinitrd.gz  /tmp/initrd.img
上面的目录可以根据需要进行更改。如果在gnome下,有些解压缩、改名、复制可以直接用右键弹出菜单完成。


3. 定制自已的initrd.img
   我修改initrd.img的起因还是归于用的那台Dell precision 470 及要用OSCAR4.1组建一个linux cluster(正在进行中,尚未完成)。

OSCAR4.1包中所用的SystemImager3.2.2-1带的用于网络引导客户机的BOEL-kernel-2.4.25和initrd.img不支持Dell precision 470的Adaptec39320B HostRaid Control Card。

此外,在上面装RedHat 9.0后进行内核重编译时,可能因为mkinitrd或者9.0内核版本的原因,生成的img文件无法使用。

在此以我实际遇到的问题,主要讲讲更新initrd.img中的硬件驱动模块的方法,抛砖引玉。


   模块跟kernel的版本相关,直接的用其它支持你硬件但kernel版本不同的模块进行替换通常不会成功,所以最好到kernel.org上下载所需要的kernel版本的源程序包。

我解决我上面的问题的文法就是下载了Linux-2.4.25.tar.gz源程序,并解压到/usr/src目录,之后到adaptec.com网站下载到支持39320B HostRaid的适合linux-2.4版的驱动源程序,并解压替换2.4.25内核源程序中的驱动程序源代码。之后对内核进行重新编译(在本文不详述具体方法,网上有很多相关资料,或许有时间我会写一篇),用make menuconfig时最好直接选上随着原来kernel & initrd.img在一起的config文件。如果不出错,执行make modules_install后就生成了需要的模块(通常在目录/lib/modules/kernel-version)。 之后我用新的/lib/modules/kernel-version/lib/下的modules目录及其文件替换掉旧的initrd.img中的modules目录(当然先得拆解之,方法见第2部分)。
当然initrd.img可修改的部分还有很多,但在此只以自已实际做过的部分进行说明。


================================================================================================================================================


解压2.6内核的cpio的initrd.img
2.6内核中的initrd.img采用cpio压缩,不再是2.4内核使用的ext2格式,无法使用mount -o loop 挂载。需要使用gunzip解压缩,然后再使用cpio解包

cp /boot/initrd-***.img initrd.img.gz
gunzip initrd.img.gz
mkdir initrd
mv initrd.img initrd
cd initrd
cpio -ivmd < initrd.img
(-i copy-in模式,即解压,-v verbose,-m 当创建文件时,保留以前的文件修改时间,-d 在必要创建目录时创建它)
通过以上命令就将initrd.img解压了,现在就可以对其进行编辑,完成后使用以下命令重新压制

find . | cpio -ov > ../initrd.new.img
gzip ../initrd.new.img
(-o copy-out模式,即压缩,-v verbose详细信息)
再将其改名拷贝至/boot目录,重启就可以观察修改后的效果
========================================================
如果自己根据内核来制作initrd.img
如下
Decide on the RAM disk size that you want. Say 8 MB for this example.
a) dd if=/dev/zero of=imitrd.img bs=1k count=8192
b) Make a filesystem on it. Say ext2fs for this example.
$mke2fs -F -vm0 initrd.img
/*看有的帖子上说是这样制作initrd文件系统的,不过个人以为下面那个才对,没试验过!*/

cd /lib/modules/kernel_version
  mkinitrd /tmp/initrd-kernel_version.img kernel_version
  cp /tmp/initrd-kernel_version.img /boot
========================================================


gzip 命令详解

减少文件大小有两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间。gzip是在Linux系统中经常使用的一个对文件进行压缩和解压缩的命令,既方便又好用。

语法:gzip [选项] 压缩(解压缩)的文件名
各选项的含义:
-c 将输出写到标准输出上,并保留原有文件。
-d 将压缩文件解压。
-l 对每个压缩文件,显示下列字段:
   压缩文件的大小
   未压缩文件的大小
   压缩比
   未压缩文件的名字
-r 递归式地查找指定目录并压缩其中的所有文件或者是解压缩。
-t 测试,检查压缩文件是否完整。
-v 对每一个压缩和解压的文件,显示文件名和压缩比。
-num 用指定的数字num调整压缩的速度,-1或--fast表示最快压缩方法(低压缩比),-9或--best表示最慢压缩方法(高压缩比)。系统缺省值为6。

假设一个目录/home下有文件mm.txt、sort.txt、xx.com。
例1:把/home目录下的每个文件压缩成.gz文件。
$ cd /home
$ gzip *
$ ls
m.txt.gz sort.txt.gz xx.com.gz

例2:把例1中每个压缩的文件解压,并列出详细的信息。
$ gzip -dv *
mm.txt.gz 43.1%-----replaced with mm.txt
sort.txt.gz 43.1%-----replaced with sort.txt
xx.com.gz 43.1%-----replaced with xx.com
$ ls
mm.txt sort.txt xx.com

例3:详细显示例1中每个压缩的文件的信息,并不解压。
$ gzip -l *
compressed uncompr. ratio uncompressed_name
277 445 43.1% mm.txt
278 445 43.1% sort.txt
277 445 43.1% xx.com
$ ls
mm.txt.gz sort.txt.gz xx.com.gz

例4:压缩一个tar备份文件,如usr.tar,此时压缩文件的扩展名为.tar.gz
$ gzip usr.tar
$ ls
usr.tar.gz



===========================================================================================
initrd.img是一个小的映象,包含一个最小的linux系统。通常的步骤是先启动内核,然后内核挂载initrd.img,并执行里面的脚本来进一步挂载各种各样的模块,然后发现真正的root分区,挂载并执行/sbin/init...

initrd.img当然是可选的了,如果没有initrd.img,内核就试图直接挂载root分区。

说initrd.img文件还会提到另外一个名角---vmlinuz。vmlinuz是可引导的、压缩的内核。“vm”代表 “Virtual Memory”。

Linux 支持虚拟内存,不像老的操作系统比如DOS有640KB内存的限制。

Linux能够使用硬盘空间作为虚拟内存,因此得名“vm”。另外:vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。


为什么要initrd.img

系统内核vmlinuz被加载到内存后开始提供底层支持,在内核的支持下各种模块,服务等被加载运行。

这样当然是大家最容易接受的方式,曾经的linux就是这样的运行的。假设你的硬盘是scsi 接口而你的内核又不支持这种接口时,

你的内核就没有办法访问硬盘,当然也没法加载硬盘上的文件系统,怎么办?把内核加入scsi驱动源码然后重新编译出一个新的内核文件替换原来vmlinuz。

需要改变标准内核默认提供支持的例子还有很多,如果每次都需要编译内核就太麻烦了。

所以后来的linux就提供了一个灵活的方法来解决这些问题---initrd.img。

initrd.img文件就是个ram disk的映像文件。ramdisk是用一部分内存模拟成磁盘,让操作系统访问。

ram disk是标准内核文件认识的设备(/dev/ram0)文件系统也是标准内核认识的文件系统。

内核加载这个ram disk作为根文件系统并开始执行其中的"某个文件"(2.6内核是 init文件)来加载各种模块,服务等。

经过一些配置和运行后,就可以去物理磁盘加载真正的root分区了,然后又是一些配置等,最后启动成功。

也就是你只需要定制适合自己的 initrd.img 文件就可以了。这要比重编内核简单多了,省时省事低风险。


===============================================================================================
6.         编译内核模块:make modules  :这步编译的是刚才配置的内核模块

7.         安装内核模块:make modules_install

a)         作用:将编译好的内核模块从内核源代码目录copy至/lib/modules下

8.         制作Init ramdisk  内存盘

a)         mkinitrd initrd-$version $version 前一个变量可自己取,后一个变量是/lib/modules下的目录名,这个目录是安装内核时产生的

b)         例:mkinitrd initrd-2.6.29 2.6.29   当前使用的内核版本号
调用mkinitrd(如果是ubuntu则用mkinitramfs命令)
进行内核编译时,需要进行制作initrd.img.在Fedora下面一般是用mkinitrd,而在Ubuntu/Debian下是用mkinitramfs.它们的用法稍微有些不一样,如下所示:

mkinitrd /boot/initrd.img 2.6.26

mkinitramfs 2.6.xx -o /boot/initrd.img

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值