构建linux根文件系统


有道云笔记地址:
详情看这里链接,记录太多,就不一一排版了。
http://note.youdao.com/noteshare?id=15b6e982c2e66d0f47b1c787a49f4f99&sub=B945844CB6404211B5A9E9AF51C26AB8
一、根文件系统构建的学习路线
1.rootfs的两种表现形式:
(1) .nfs方式启动的文件夹形式的rootfs
(2) .用来烧录的镜像形式rootfs
2.我们这里的学习路线是:
(1).先逐步构建文件夹形式的根文件系统再制作烧录镜像 。
(2).每做一步就用NFS去挂载,观察启动后的现象,然后理论分析原因。
(3).逐步添加形成不断复杂化的Rootfs。
二、busybox的移植实战
busybox:嵌入式开发中的瑞士军刀(提供嵌入式中linux中的各种小工具,小命令,比如像ls、pwd命令等), 一般这个文件官方会给出,或者直接在网上找一份也是可以的,然后放到虚拟机中进行解压,配置编译。
1.获取 BusyBox 源码
BusyBox 的官方网站是:http://www.busybox.net/
我们下面就以busybox-1.24.1.tar.bz2为例来讲解一下 BusyBox 的编译安装过程。
注意:因为当你决定了用什么版本的交叉编译器来编译内核时,文件系统中的所有程序也要使用同样的交叉编译器来编译。
2. 同步拷贝、解压到ubuntu
我们的根文件系统目录:/root/porting_x210/rootfs/rootfs
在根文件系统目录下创建Aliya_rootfs文件,将该压缩包解压到这里。
3. 修改Makefile
(1) ARCH = arm
(2) CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin//arm-none-linux-gnueabi-
4.make menuconfig配置
//不用像内核中make defconfig那样需要很多的配置
按照如下文档配置menuconfig即可。
5.make -j4,没有错误后make install
(make install实际上是在执行 busybox顶层目录的 install)
./include/config/feature/install
./include/config/install
make install在所有的linux下的软件中作用都是安装软件。
在传统的linux系统中安装软件时都是选择源代码方式安装的。我们下载要安装的软件源代码,然后配置、编译、安装。
这里的“安装”就是make install
make install的目的就是将编译生成的可执行程序及其依赖的库文件、 配置文件、头文件安装到当前系统中指定(一般都可以自己指定安装到哪个目录下,如果不指定一般都有个默认目录)的目录下
(1)make编译中遇到问题:sync.c:(.text.sync_main+0x78): undefined reference to `syncfs’
解决当前错误:
sync.c:(.text.sync_main+0x78): undefined reference to `syncfs’
//这个错误很可能是gcc和当前busybox版本不兼容造成的。
我们对于安装或者配置中出现的错误,通常有2种解决方式:
1 尝试去源代码中解决他
2 在编译的时候尝试避开他(很可能是我们用不着无关的程序我们可以这么做)
怎么做?
使用命令find -name "sync.c"去找到这个sync.c,在他的目录下寻找makefile,发现没有
但是有Kbuild文件(有点类似Makefile),打开Kbuild,在Kbuild将 lib+= sync.o 一项中,可以看到config的选项
lib-$(CONFIG_SYNC) += sync.o
我们要找的是 SYNC 宏
去menuconfig中 ,搜索SYNC,就找到了。禁用sync即可
make -j4解决。
6.make install
默认安装目录:./_install
里面有4个文件: bin linuxrc sbin usr
ls -l可以看到:
linuxrc -> bin/busybox//这个linuxrc其实就是个符号链接
bin里面的所有符号链接,全都指向busybox!
7.改变到我们的根文件系统目录中
我们的根文件系统目录:/root/porting_x210/rootfs/rootfs
make menuconfig
Busybox Settings —>
Installation Options (“make install” behavior) —>
点进去,改成我们将要用作NFS挂载的目录
/root/porting_x210/rootfs/rootfs
再次make install后,到我们的跟文件系统中可以看到已经生成了这四个文件
到这里,busybox移植的rootfs已经完成了。
8.设置bootargs挂载添加了busybox移植的rootfs
(1)之前建立了一个空的文件夹然后自己touch linuxrc随便创建了一个不能用的/linuxrc然后去nfs挂载rootfs,实验结果是:挂载成功,执行/linuxrc失败。
(2)现在我们移植了busybox后/linuxrc就可以用了,然后再次去nfs挂载这个rootfs。预计看到的效果是:挂载成功,执行/linuxrc也能成功。
(3)注意uboot的bootargs设置成:setenv bootargs root=/dev/nfs nfsroot=192.168.1.141:/root/porting_x210/rootfs
ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC2,115200
(4)上节NFS挂载的步骤已经做过,这里只需要在串口终端中TFTP下载zImage即可。
实验结果:
挂载成功,执行/linuxrc(也就是busybox)成功,未提示linuxrc错误。
但是因为找不到/etc/init.d/rcS和/dev/tty2等文件所以一直在打印错误提示信息,但是其实有进入命令行。
三、inittab配置文件详解
1、添加一个典型的inittab
(1)将提供的典型的inittab文件复制到我们制作的rootfs的根目录下的/etc/目录下
(2)再次启动内核挂载这个rootfs看效果
(3)再次启动开发板,提示“按回车键使能控制台”。
我们做到这里已经能使用ls查看/目录下的文件了,至此最小根文件系统能用了,
包括: busybox、 inittab
2、inittab格式解析(就是上面我们添加的典型的inittab内容)
(1)inittab的工作原理就是被/linuxrc(也就是busybox)执行时所调用起作用。
(2)inittab在/etc目录下,所以属于一个运行时配置文件,内容由文本组成。我们研究的重点是如何修改他。。
(3)我们的重点是inittab的格式究竟怎样的?我们看到一个inittab后怎么去分析这个inittab对启动的影响。
(4)【inittab格式解析】 详情:百度busybox inittab
> # 注释(linux中的习惯性思维)
> : 分隔符,每行代表一个独立的含义 ,分隔开各个部分。
> / 目录
第4个:inittab内容是以行为单位的,行与行之间没有关联,每行都是一个独立的配置项,每一个配置项表示一个具体的含义。
第5个:每一行的配置项都是由3个冒号分隔开的4个配置值共同确定的。这四个配置值就是id:runlevels:action:process。 值得注意得是有些配置值可以空缺,空缺后冒号不能空缺,所以有时候会看到连续2个冒号。
第6个:每一行的配置项中4个配置值中最重要的是action和process,action是一个条件/状态,process是一个可被执行的程序的pathname。 合起来的意思就是:当满足action的条件时就会执行process这个程序。
注意:理解inittab的关键就是明白“当满足action的条件时就会执行process这个程序。”
去分析busybox的源代码就会发现,busybox最终会进入一个死循环, 在这个死循环中去反复检查是否满足各个action的条件,如果某个action的条件满足就会去执行对应的process。
第六个:明白各个action什么意思
在busybox-1.24.1中init.c定义了以下action
sysinit
指定的进程在访问控制台之前执行,这样的条目仅用于对某些设备的初始化, 目的是为了使init在这样的设备上向用户提问有关运行级别的问题,init需要等待进程运行结束后才继续
respawn
如果process字段指定的进程不存在,就启动该进程,init不会等待处理结束,而是继续扫描inittab文件。
当该进程被终止时,init将重新启动它。如果相应的进程已经存在,init就忽略该条目并继续扫描inittab文件
askfirst
类似respawn,主要用途是减少系统上执行的终端应用程序的数量。它将会促使init在控制台上显示“Please press Enter to active this console”的信息,并在重新启动进程之前等待用户按下“enter”键
wait
告诉init必须等到相应的进程执行完成之后才能继续执行
once
启动进程,不会等待处理结束,而是继续处理下一条条目。当该进程被终止时,init不会重新启动它。 从一个运行级别进入另一个运行级别时,如果相应的进程仍 在运行,init就不会重新启动该进程
ctratldel
当按下Ctrl+Alt+Delete组合键时,执行相应的进程
shutdown
当系统关机时,执行相应的进程
restart
当init重新启动时,执行相应的进程,通常此处所执行的进程就是init本身
boot
只在系统启动时,init才处理这条条目,启动相应的进程,并不等待处理结束就去处理下一条条目。当这样的进程终止时,也不会重新启动它
bootwait
系统启动后,当第一次从单用户模式进入多用户模式时才处理该条目,init启动这样的进程,并且等待其处理结束才处理下一条条目,当该进程被终止时,也不重新启动它。
off
如果相应的进程正在运行,那么就发出一个警告信号,等待数秒后,再发出信号SIGKILL强制终止该进程。如果相应的进程不存在就忽略该条目
initdefault
指定一个默认的运行级别,如果指定了多个运行级别,其中最大的数字将是默认的运行级别。 如果inittab文件没有包含该条目,在系统启动时会请求用户为其指定一个默认的运行级别
powerwait
当初接接到断电的信号时,处理指定的进程,并且等到处理结束后才去检查其他的条目
powerfail
当init接到断电的信 时,处理指定的进程,但是不等待该进程处理结束。
(这就是上面我们添加的典型的inittab内容,分析内容如下)

              #first:run the system script file 注释

              ::sysinit:/etc/init.d/rcS

              //在控制台初始化之前执行rcS

              ::askfirst:-/bin/sh



              //执行控制台时的打印信息

              ::ctrlaltdel:-/sbin/reboot



              //同时按住3键可以重启

              #umount all filesystem

              ::shutdown:/bin/umount -a -r//关机时接触挂载



              //重启时启动init

              #restart init process

              ::restart:/sbin/init
              
四、busybox源码分析
1、SourceInsight工程建立
2、整个程序入口确认
(1)分析一个程序,不管多庞大还是小,最好的路线都是按照程序运行时的逻辑顺序来。所以找到一个程序的入口至关重要。
(2)在uboot和linux kernel这两个大的C语言的项目中,main函数都没有,都不是入口。在我们这种裸机程序中入口不是main函数,而是由连接脚本来指定的。
(3)busybox是linux启动起来后工作的一个应用程序,因此其中必然有main函数,而且main就是入口。
3、busybox的总入口
(1)busybox入口就是main函数,其中有很多个main但是只有一个起作用了,其他的是没起作用的(查看对应的文件中没有链接生成.o文件)。
真正的busybox工作时的入口是libbb/appletlib.c中的main函数
(2)busubox中有很多xxx_main函数,这些main函数每一个都是busybox支持的一个命令的真正入口。譬如ls_main函数就是busybox当作ls函数使用时的入口程序。
(3)ls或者cd等命令其实都是busybox一个程序,但是实际执行时的效果却是各自的效果。busybox是如何实现一个程序化身万千还能各自工作的?
答案就是main转xxx_main。
也就是说busybox每次执行时都是先执行其main, 在main函数中识别(靠main函数的传参argv[0]来识别)我们真正要执行的函数(譬如ls)然后去调用相应的xxx_main(譬如ls_main)来具体实现这个命令。
譬如:在Init.c中有init_main()函数,inittab配置文件解析用的
4、inittab解析与执行
(1)inittab的解析是在busybox/init/init.c/init_main函数中
(2)执行逻辑是:先通过parse_inittab函数解析/etc/inittab(解析的重点是将inittab中的各个action和process解析出来), 然后后面先直接执行sysinit和wait和once(注意这里只执行一遍),然后在while(1)死循环中去执行respwan和askfirst。
5、pwd命令执行路径分析
(1)根据上节讲的,我们在busybox命令行下执行pwd命令时实际执行的是pwd_main这个函数。
(2)pwd的解析是在busybox/coreutils/pwd.c/pwd_main函数中
6、busybox的体积优势原理
(1)busybox实际上就是把ls、cd、mkdir等很多个linux中常用的shell命令集成在一起了。 集成在一起后有一个体积优势:就是busybox程序的大小比busybox中实现的那些命令的大小加起来要小很多。
(2)busybox体系变小的原因主要有2个:
第一个是busybox本身提供的shell命令是阉割版的(busybox中的命令支持的参数选项比发行版中要少,譬如ls在发行版中可以有几十个-x, 但是在busybox中只保留了几个常用的选项,不常用的都删除掉了);
第二个是busybox中因为所有的命令的实现代码都在一个程序中实现, 而各个命令中有很多代码函数都是通用的(譬如ls和cd、mkdir等命令都会需要去操作目录,因此在busybox中实现目录操作的函数就可以被这些命令共用), 共用会降低重复代码出现的次数,从而减少总的代码量和体积。
(3)经过分析,busybox的体积优势是嵌入式系统本身的要求和特点造成的。
五、rcS文件介绍1
/etc/init.d/rcS文件是linux的运行时配置文件中最重要的一个,其他的一些配置都是由这个文件引出来的。
这个文件可以很复杂也可以很简单,里面可以有很多的配置项。
可以看出,rcS就是一个脚本文件。

              #!/bin/sh 需要继续添加环境变量,在后面:/new 即可

              PATH=/sbin:/bin:/usr/sbin:/usr/bin



              runlevel=S

              prevlevel=N



              umask 022



              export PATH runlevel prevlevel



              mount -a

              
1、PATH=xxx
(1)首先从shell脚本的语法角度分析,这一行定义了一个变量PATH,值等于后面的字符串
(2)后面用export导出了这个PATH,那么PATH就变成了一个环境变量。
(3)PATH这个环境变量是linux系统内部定义的一个环境变量,含义是操作系统去执行程序时会默认到PATH指定的各个目录下去寻找。 如果找不到就认定这个程序不存在,如果找到了就去执行它。将一个可执行程序的目录导出到PATH,可以让我们不带路径来执行这个程序。
(4)rcS中为什么要先导出PATH?
就是因为我们希望一旦进入命令行下时,PATH环境变量中就有默认的/bin /sbin /usr/bin /usr/sbin 这几个常见的可执行程序的路径, 这样我们进入命令行后就可以ls、cd等直接使用了。
(5)为什么我们的rcS文件还没添加,系统启动就有了PATH中的值?
原因在于busybox自己用代码硬编码为我们导出了一些环境变量,其中就有PATH。
2、runlevel=
(1)runlevel也是一个shell变量,并且被导出为环境变量。
(2)runlevel这个环境变量到底有什么用? linux操作系统自从开始启动至启动完毕需要经历几个不同的阶段,这几个阶段就叫做runlevel。
例如init 0就是关机,init 6 就是重启
Linux系统有7个运行级别(runlevel)
运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆
运行级别2:多用户状态(没有NFS)
运行级别3:完全的多用户状态(有NFS),登陆后进入控制台命令行模式
运行级别4:系统未使用,保留
运行级别5:X11控制台,登陆后进入图形GUI模式
运行级别6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动
(3)runlevel=S表示将系统设置为单用户模式
3、umask=
(1)umask是linux的一个命令,作用是设置linux系统的umask值。
(2)umask值决定当前用户在创建文件时的默认权限。
4、mount -a
(1)mount命令是用来挂载文件系统的
(2)mount -a是挂载所有的应该被挂载的文件系统,在busybox中mount -a时busybox会去查找一个文件/etc/fstab文件, 这个文件按照一定的格式列出来所有应该被挂载的文件系统(包括了虚拟文件系统)
六、rcS文件实战
将etc中的所有文件全部拷贝到/root/porting_x210/rootfs/rootfs目录下,然后从新烧录zImage,查看打印信息
1、PATH&runlevel
(1)我们实战发现rcS文件明明存在但是却提示不存在,为什么纳?
问题原因就是rcS文件在windows下创建的,行尾换行符为'\r\n',多了点东西。 但是因为ubuntu中的vi对行尾做了优化,所以在ubuntu中是看不出来多了东西的。但是在securecrt下一看就发现每一行末尾多出来了一个^M。
去掉结尾的^M 即可!//不删除会因格式不对提示没有rcS
此时从新下载启动,发现已经没有 这个错误了,说明这个问题解决了。打印信息如下
mount: mounting proc on /proc failed: No such file or directory ,
在下面的 3、mount测试中会讲这个错误。
原因是因为根文件系统中找不到挂载点。所谓挂载点就是我们要将目标文件系统(当然这里都是虚拟文件系统)挂载到当前文件系统中的某一个目录中,这个目录就是挂载点。
【解决方案】就是自己在制作的rootfs根目录下创建这些挂载点目录即可。
(2)这个错误告诉我们:shell脚本文件如果格式不对,运行时可能会被提示文件不存在。
(3)扩展讲一个:有时候一个应用程序执行时也会提示文件不存在,问题可能是这个程序所调用的一个动态链接库找不到。
(4)测试结果:PATH本来在busybox中就已经用代码导出过了,所以rcS中再次导出没有任何明显的现象,因此看不出什么差别;
runlevel实际执行结果一直是unknown,问题在于busybox并不支持runlevel这个特性。
2、umask测试
(1)umask是022的时候,默认touch创建一个文件的权限是644
(2)umask是044的时候,默认touch创建一个文件的权限是622
(3)umask是444的时候,默认touch创建一个文件的权限是222
总结:umask的规律就是:umask值和默认创建文件的权限值加起来是666.
3、mount测试
(1)挂载时全部出错:
mount: mounting proc on /proc failed: No such file or directory
mount: mounting sysfs on /sys failed: No such file or directory
mount: mounting tmpfs on /var failed: No such file or directory
mount: mounting tmpfs on /tmp failed: No such file or directory
mount: mounting tmpfs on /dev failed: No such file or directory
(2)原因是因为根文件系统中找不到挂载点。所谓挂载点就是我们要将目标文件系统(当然这里都是虚拟文件系统)挂载到当前文件系统中的某一个目录中,这个目录就是挂载点。
(3)解决方案就是自己在制作的rootfs根目录下创建这些挂载点目录即可。
(4)验证是否挂载成功,可以看挂载时输出信息;还可以启动后去看proc和sys文件夹,如果有文件出现则证明挂载成功了,如果没东西就证明失败了。
这些文件在串口终端中查看才有,但是linux主机服务器中是ls不到的这些意义暂时不讲,在驱动中会用到的。
七、rcS文件介绍2
1、mdev
(1)mdev是udev的嵌入式简化版本,udev/mdev是用来配合linux驱动工作的一个应用层的软件,udev/mdev的工作就是配合linux驱动生成相应的/dev目录下的设备文件。
(因为这个问题涉及到驱动,因此详细讲解要等到驱动部分。)
(2)在rcS文件中没有启动mdev的时候,/dev目录下启动后是空的;
在rcS文件中添加以下与mdev有关的2行配置项后,
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
再次启动系统后发现/dev目录下生成了很多的设备驱动文件。
(3)/dev目录下的设备驱动文件就是mdev生成的,这就是mdev的效果和意义。
2、hostname
(1)hostname是linux中的一个shell命令。hostname xxx执行后可以设置当前主机名为xxx ,直接hostname不加参数可以显示当前系统的主机名。
(2)/bin/hostname -F /etc/sysconfig/HOSTNAME
指定了一个主机名配置文件(这个文件一般文件名叫hostname或者HOSTNAME)
根据/bin/hostname -F /etc/sysconfig/HOSTNAME,在etc下创建sysconfig
在sysconfig/下创建 HOSTNAME,直接输入主机名:Aliya
重启,发现没有显示用户名和hostname ,怎么解决?
//注意:不设置HOSTNAME,默认的是一个ip地址,在我实验的时候是
192.168.1.10
添加profile文件后,即可显示用户名和hostname。下节会讲如何添加。
3、ifconfig
(1)有时候我们希望开机后进入命令行时ip地址就是一个指定的ip地址(譬如192.168.1.10),这时候就可以在rcS文件中ifconfig eth0 192.168.1.10
八、profile文件和用户登录理论
解决上节的实验:没有显示用户名和hostname ,
1、profile文件添加
(1)之前添加了/bin/hostname在/etc/sysconfig/HOSTNAME文件中定义了一个hostname(Aliya210), 实际效果是:命令行下hostname命令查到的host名字确实是Aliya210。但是问题就是命令行的提示符是没有显示的。
(2)这个问题的解决就要靠profile文件。将提供的profile文件放入/etc/目录下即可。
(3)添加了之后的实验现象:命令行提示符前面显示:[@Aliya210]#
结论是:第一,profile文件起了作用,hostname显示出来了。
第二,还有个问题,登录用户名没显示出来。
原因就是我们直接进入了命令行而没有做登录。等我们添加了用户登录功能,并且成功登陆后这个问题就能解决。
(4)profile文件工作原理是:profile文件也是被busybox(init进程)自动调用的,所以是认名字的。
2、如何看到用户登录界面
(1)linux中有个原则:用一个小程序来实现一个功能。不要把程序写的特别大。
如果我们的产品确实需要很复杂的综合功能,那么需要吧小程序集成起来,比如shell脚本。譬如shell脚本,还有一些别的技术,譬如linux启动中的inittab。
(2)因为我们之前intttab中有一个配置项
::askfirst:-/bin/sh,这个配置项作用就是当系统启动后就去执行/bin/sh,执行这个就会出现命令行。因此我们这样的安排就会直接进入命令行而不会出现登录界面。
(3)我们要出现登录界面,就不能直接执行/bin/sh,而应该执行一个负责出现登录界面并且负责管理用户名和密码的一个程序, busybox中也集成了这个程序(就是/bin/login和/sbin/gettty),因此我们要在inittab中用/bin/login或者/sbin/getty去替代/bin/sh。
3、用户名和密码的设置
(1)用户名和密码的设置是和登录程序有关联的,但是/bin/login和/sbin/getty在用户名和密码的管理上是一样的。 其实常见的所有的linux系统的用户名和密码的管理几乎都是一样的。
(2)密码一般都是用加密文字的,而不是用明文。 意思就是系统中的密码肯定是在系统中的一个专门用来存密码的文件中存储的,用明文存密码有风险,因此linux系统都是用密文来存储密码的。 关于密文密码的使用下节课实践时会详细讲。
九、用户登录实战
1、添加/bin/login到sysinit
(1)在etc/inittab文件中修改,去掉/bin/sh,换上/bin/login,则系统启动后出现登录界面。可以输入用户名和密码。
注释掉::askfirst:-/bin/sh
在注释的下面添加 s3c2410_serial2::sysinit:-/bin/login
注意:此处的“s3c2410_serial2”是选择串口2做为tty,在/dev中有这个文件
(2)从新tftp下载启动,实验现象:成功出现用户登录界面,但是死活密码不对。
2、添加passwd和shadow文件
(1)为什么用户名和密码不对?因为我们根本没有为root用户设置密码。
(2)linux系统中用来描述用户名和密码的文件是passwd和shadow文件,这两个文件都在etc目录下。 passwd文件中存储的是用户的密码设置,shadow文件中存储的是加密后的密码。
(3)我们直接复制ubuntu系统中的/etc/passwd和/etc/shadow文件到当前制作的rootfs目录下,然后再做修改即可。
修改1:在rootfs文件夹下要为root用户创建一个/root目录
修改2: vi etc/passwd 把其他非root用户的信息全删除
修改3:在passwd中把默认脚本改为sh(ubuntu中默认是bash,busybox不支持bash,支持sh)
修改4:vi etc/shadow ,把其他非root用户的信息全删除,把root用户的密码信息全部删除
(4)/etc/passwd和/etc/shadow修理好后,因为shadow中的加密口令被我们删除了,是空的则默认无密码直接登录。
3、重置密码实践
(1)ubuntu刚装好的时候默认登录是用普通用户登录的,默认root用户是关闭的。普通用户的密码是在装系统的时候设置的,普通用户登陆后可以使用su passwd
root给root用户设置密码,设置了密码后root用户才可以登录。
(2)其实这个原因就是root用户在/etc/shadow文件中加密口令是空白的。所以是不能登录的。
(3)busybox中因为没有普通用户,所以做法是:
默认root用户如果加密口令是空的则默认无密码直接登录。等我们登陆了之后还是可以用passwd root给root用户设置密码。
例如,我们这里从新设置root用户密码为 123456
在uboot中打开vi etc/shadow ,发现写入了加密口令
4、getty实战
(1)大家后面做项目会发现,inittab中最常见的用于登录的程序不是/bin/login,反而是/sbin/getty。
(2)这两个的差别不详,但是在busybox中这两个是一样的。这两个其实都是busybox的符号链接而已。因此不用严格区分这两个
(3)我们可以在inittab中用getty替换login程序来实现同样的效果。
在inittab中改成:将/bin/login相关的注释掉,添加如下
s3c2410_serial2::respawn:/sbin/getty -L s3c2410_serial2 115200 vt100
串口格式样式
重新下载启动,和/bin/login程序一样的效果。
十、动态链接库的拷贝
1、静态编译链接helloworld程序并执行
(1)任务:自己写一个helloworld程序,然后交叉编译连接,然后丢到开发板根文件系统中,开机后去运行。
(2)C程序如果使用gcc来编译则可以在主机ubuntu中运行,但是不能在开发板运行;
要在开发板运行需要用arm-linux-gcc来交叉编译,但是这时候就不能在主机ubuntu中运行了。
我们可以用file xx命令来查看一个elf可执行程序是哪个架构的。
直接在/porting_x210/rootfs/rootfs/root下创建Makefile脚本,将动态和静态链接helloword的命令都写入:
(3)静态链接:arm-linux-gcc hello.c -o hello_satic -static
(4)实验结果:静态编译连接后生成的hello_satic已经可以成功运行。
2、动态编译连接helloworld程序并执行
(1)动态链接:arm-linux-gcc hello.c -o hello_dynamic
(2)实验结果:-sh: ./hello_dynamic: not found运行时提示找不到程序。
(3)错误分析:动态连接的hello程序中调用到了printf函数, 而printf函数在动态连接时要在运行时环境(开发板的rootfs)中去寻找对应的库文件(开发板rootfs中部署的动态链接库中包含了printf函数的那个库文件)。 如果找到了则printf函数就会被成功解析,然后hello_dynamic程序就会被执行;如果找不到则程序就不能被执行,命令行会提示错误信息-sh:
./hello_dynamic: not found
(4)解决方案:
将arm-linux-gcc的动态链接库文件复制到开发板rootfs的/lib目录下即可解决。
3、找到并复制动态链接库文件到rootfs中
(1)我们用的arm-2009q3这个交叉编译工具链的动态链接库在/usr/local/arm/arm-2009q3/arm-none-linux-gnueabi/libc/lib目录下。
其他的一些交叉编译工具链中动态链接库的目录不一定在这里,要去找一下。找的方法就是find -name "xxx"命令
(2)复制动态链接库到roots/lib目录下。复制时要注意参数用-rdf,主要目的就是符号链接复制过来还是符号链接。
复制命令:cp lib/*so* /root/porting_x210/rootfs/rootfs/lib/ -rdf
(3)现在再去测试./hello_dynamic看看是否可以运行,实验结果是可以运行。
4、使用strip工具去掉库中符号信息
动态链接库so文件中包含了调试符号信息,这些符号信息在运行时是没用的(调试时用的),这些符号会占用一定空间。 在传统的嵌入式系统中flash空间是有限的,为了节省空间常常把这些符号信息去掉。这样节省空间并且不影响运行。
去掉符号信息的命令:arm-linux-strip *so*
实际操作后发现库文件由3.8M变成了3.0M,节省了0.8M的空间。
十一、开机自启动与主流rcS格式介绍
1、修改rcS实现开机自启动
(1)开机自启动指的是让一些应用程序能够开机后自动执行
(2)开机自启动的实现原理就是在开机会自动执行的脚本rcS中添加上执行某个程序的语句代码即可
譬如,我们想要将上节讲的动态链接的helloword程序实现开机启动启动
只需要在 vi /etc/init.d/rcS 中,在最后添加 一下命令即可保存重启即可。
cd /root //先进入helloword程序的目录,执行之后,再次退出
./hello_dynamic
cd ..
2、前台运行与后台运行
(1)程序运行时占用了当前的控制台,因此这个程序不结束我们都无法使用控制台,这就叫前台运行。默认执行程序就是前台运行的。
(2)后台运行就是让这个程序运行,并且同时让出控制台。这时候运行的程序还能照常运行而且还能够不影响当前控制台的使用。
(3)让一个程序后台运行的方法就是 ./xxx &
3、开机装载驱动等其他开机自动执行
如果驱动是模块化的,没有在zImage中加载。我们希望开机装载驱动,解决方法就是在rcS文件中添加对应的启动命令即可。
4、实际开发中rootfs的rcS是怎样的
(1)我们以X210开发板九鼎科技做的rootfs中rcS部分来分析
(2)分析inittab发现:sysinit执行rcS,shutdown时执行rcK。
(3)分析/etc/init.d/rcS和rcK文件发现,rcS和rcK都是去遍历执行/etc/init.d/目录下的S开头的脚本文件,区别是rcS传参是start,rcK传参是stop。
(4)由此可以分析出来,正式产品中的rcS和rcK都是一个引入,而不是真正干活的。真正干活的配置脚本是/etc/init.d/S??*。
这些文件中肯定有一个判断参数是start还是stop,然后start时去做一些初始化,stop时做一些清理工作。
十二、制作ext2格式的镜像并烧录启动
1、确定文件夹格式的rootfs可用
(1)设置bootargs为nfs启动方式,然后从主机ubuntu中做好的文件夹格式的rootfs去启动,然后看启动效果,作为将来的参照物。
2、动手制作ext2格式的镜像(不懂可以参考十九,有详细分析)
(1)在当前的rootfs文件夹创建一个 mkdir ext2_rootfs,用于我们的挂载目录
依次输入一下命令:
dd if=/dev/zero of=rootfs.ext2 bs=1024 count=10240
losetup /dev/loop1 rootfs.ext2
mke2fs -m 0 /dev/loop1 10240
mount -t ext2 /dev/loop1 ./ext2_rootfs/
挂载好了之后,进入ext2_rootfs命令,会有如下内容
(2)向./rootfs中复制内容,用cp ../rootfs/* ./ -rf
(3)接下来就是:在挂载目录的上一层目录下进行清理卸载
umount /dev/loop1
losetup -d /dev/loop1
(4)完成后得到的rootfs.ext2就是我们做好的rootfs镜像。拿去烧录即可。
将该rootfs.ext2文件拷贝到windows中,进行下面的烧录。
cp rootfs.ext2 /mnt/hgfs/Linux/winshare/ -f
3、烧录镜像并设置合适的bootargs
(1)使用fastboot烧录制作好的rootfs.ext2到开发板inand中
fastboot flash system rootfs.ext2
烧录完成后重启系统
(2)设置bootargs为:
set bootargs console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext2
(3)启动后发现现象和之前nfs方式启动挂载rootfs后一样的,至此rootfs制作实验圆满完成。
4、总结
(1)到这里就将rootfs的制作步骤和原理已经完全弄完了
(2)对rootfs工作的原理以及如何从零开始够建根文件系统,有了更清晰深刻的认识和积累。
(3)总体来说,对rootfs的移植可以分为三部分:文件系统介绍、 BusyBox 的移植、根文件系统构建。 Linux 文件系统发展很快,不断会有新的安装源码在官网上更新,但其总体原理以及全局设计观念是不会变化的, 因此,关键是要掌握其方法,特别要了解各种文件系统的特点以及其应用场合。
————————————————
版权声明:本文为CSDN博主「MrT_WANG」的编辑整理,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangweijundeqq/article/details/82533485

相关阅读:
linux基础
linux怎么学
linux和GNU
GNU Free Documentation License
最受欢迎的linux发行版
initroot编辑整理,转载请注明www.initroot.com

100次点赞 100次阅读