以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。
一、inittab文件介绍
- #first:run the system script file
- ::sysinit:/etc/init.d/rcS //sysinit表示控制台启动(命令行)之前执行,所以视频中会打印不能运行rcS,因为刚开始没有配置该文件
- ::askfirst:-/bin/sh //askfirst促使按回车键
- ::ctrlaltdel:-/sbin/reboot //ctrlaltdel表示按下ctrl和delet键,但在scrt中体现不了效果。
- #umount all filesystem
- ::shutdown:/bin/umount -a -r
- #restart init process
- ::restart:/sbin/init
(1)inittab的工作原理
- /linuxrc 执行时调用该文件。
(2)inittab在/etc目录下
- 属于运行时配置文件,是文本格式的(内容由一系列遵照一个格式组织的字符组成);
- /linuxrc 会按照一定的格式,去解析这个inittab文本文件,然后根据解析的内容来决定要怎么工作。
- 第一个:#开始的行是注释;
- 第二个:冒号在里面是分隔符,分隔开各个部分。
- 第三个:inittab内容是以行为单位的,行与行之间没有关联,每行都是一个独立的配置项,每一个配置项表示一个具体的含义。
- 第四个:每一行的配置项都是由3个冒号分隔开的4个配置值共同确定的,这四个配置值是id:runlevels:action:process。
- 第五个:action是一个条件/状态,process是一个可被执行的程序的pathname,当满足action的条件时就会执行process这个程序。
- 第六个:明白各个action的意思。
(4)由busybox的源代码可知,busybox最终进入一个死循环。
- 在这个死循环中去反复检查是否满足各个action的条件,如果某个action的条件满足就会去执行对应的process。
(5)当将(使用busybox生成的)sbin/、bin/目录拷贝到/root/rootfs下,然后创建etc目录,再在etc/目录下添加了linuxrc,最小的根文件系统就完成了(可以进入命令行了,但是会提示如下信息:)
二、rcS文件介绍
- #!/bin/sh
- PATH=/sbin:/bin:/usr/sbin:/usr/bin
- runlevel=S
- prevlevel=N
- umask 022
- export PATH runlevel prevlevel
- mount -a
- echo /sbin/mdev > /proc/sys/kernel/hotplug
- mdev -s
- /bin/hostname -F /etc/sysconfig/HOSTNAME
- ifconfig eth0 192.168.1.10
1、/etc/init.d/rcS文件
- 在开机时,linuxrc会调用此文件。
- 此文件是linux的运行时配置文件中最重要的一个,其他的一些配置都是由这个文件引出来的。
- 这个文件可以很复杂也可以很简单(嵌入式一般不用像ubuntu那么复杂),里面可以有很多的配置项。
2、PATH=xxx
(1)从shell脚本的语法角度分析,该行定义了一个变量PATH,值等于后面的字符串。
(2)用export导出这个PATH,那么PATH就变成一个环境变量。
(3)PATH这个环境变量(环境变量可以有很多个,这里只是PATH这个环境变量)
- 是linux系统内部定义的一个环境变量;
- 操作系统去执行程序时会默认到PATH指定的各个目录下去寻找。
- 如果找不到就认定这个程序不存在,如果找到了就去执行它。
- 将一个可执行程序的目录导出到PATH,可以不带路径地执行这个程序。
(4)rcS中为什么要导出PATH?
- 希望进入命令行后,PATH环境变量中就有默认的/bin /sbin /usr/bin /usr/sbin 这几个常见的可执行程序的路径,从而可以直接使用ls、cd等命令。
(5)为什么还没添加rcS文件,系统启动就有PATH中的值?
- 因为busybox用代码硬编码为我们导出了一些环境变量,其中就有PATH。
3、runlevel=
(1)runlevel也是一个shell变量,并且被导出为环境变量。
(2)runlevel这个环境变量到底有什么用?类似于window中的启动模式识别,如安全模式,普通模式。百度。
(3)runlevel=S表示将系统设置为单用户模式。
4、umask=
(1)umask是linux的一个命令,作用是设置linux系统的umask值。
(2)umask值决定当前用户在创建文件时的默认权限。
5、mount -a
(1)mount命令是用来挂载文件系统的;
(2)mount -a是挂载所有的应该被挂载的文件系统。
- 在busybox中mount -a时,busybox会去查找一个文件/etc/fstab文件;
- 这个文件按照一定的格式列出来所有应该被挂载的文件系统(包括了虚拟文件系统)。
- 比如下面图片(版本不一样,内容会不一样):
6、mdev
(1)mdev是udev的嵌入式简化版本
- udev/mdev是用来配合linux驱动工作的一个应用层的软件;
- udev/mdev的工作就是配合linux驱动生成相应的/dev目录下的设备文件。
(2)在rcS文件中没有mdev配置项时,/dev目录下启动后是空的;在rcS文件中添加上mdev有关的2行配置项后,再次启动系统后发现/dev目录下生成很多的设备驱动文件。
(3)/dev目录下的设备驱动文件就是mdev生成的,这就是mdev的效果和意义。
7、hostname
(1)hostname是linux中的一个shell命令。
- hostname xxx 执行后可以用来设置当前系统的主机名为xxx,直接hostname不加参数可以显示当前系统的主机名。
(2)/bin/hostname -F /etc/sysconfig/HOSTNAME,指定一个主机名配置文件/etc/sysconfig/HOSTNAME)。
(3)在制作根文件系统时,要创建/etc/sysconfig/HOSTNAME文件,然后在里面敲入文本xjh后保存。
8、ifconfig
- 如果希望开机进入命令行后,ip地址就是一个指定的ip地址(譬如192.168.1.30),需要在rcS文件中ifconfig eth0 192.168.1.30;
- 如果没有配置,完全启动后会没有ip地址,但可以在完全启动后用ifconfig eth0 xxxxxxxxx来设置。
三、rcS文件实战
1、PATH、runlevel
(1)rcS文件明明存在但是却提示不存在
- 原因是rcS文件是在windows下创建的,行尾换行符为'\r\n';
- 但是因为ubuntu中的vi对行尾做了优化,所以在ubuntu中是看不出来多了东西的。
- 但是在securecrt(vim 打开)下一看就发现每一行末尾多出来了一个^M。
- 解决方法是在securecrt下打开,然后删除^M,或者利用一些软件工具。
(2)启示
- shell脚本文件如果格式不对,运行时可能会被提示文件不存在。
- 有时候一个应用程序执行时也会提示文件不存在,问题可能是这个程序所调用的一个动态链接库找不到。
(3)测试结果
- 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
- 所谓挂载点就是我们要将目标文件系统(当然这里都是虚拟文件系统)挂载到当前文件系统中的某一个目录中,这个目录就是挂载点。
- 解决方案就是自己在制作的rootfs根目录下创建这些挂载点目录即可。
- 验证是否挂载成功,可以看挂载时输出信息;还可以启动后去看proc和sys文件夹,如果有文件出现则证明挂载成功了,如果没东西就证明失败了。
四、profile文件和用户登录理论
- # Ash profile
- # vim: syntax=sh
- # No core files by default
- ulimit -S -c 0 > /dev/null 2>&1
- USER="`id -un`"
- LOGNAME=$USER
- PS1='[\u@\h \W]\# '
- PATH=$PATH
- HOSTNAME=`/bin/hostname`
- export USER LOGNAME PS1 PATH
1、profile文件添加
(1)之前添加了/bin/hostname,在/etc/sysconfig/HOSTNAME文件中定义了一个hostname(xjh)。
- 效果:命令行下hostname命令查到的host名字确实是xjh,但是没有显示命令行的提示符#。
(2)解决方法,将提供的profile文件放入/etc/目录下即可。
(3)添加了之后的实验现象:命令行提示符前面显示:[@xjh ]#
- 表明profile文件起作用了,因为hostname显示出来了。
- 但是登录用户名没显示出来。[xxx@xjh]#
- 原因是我们直接进入了命令行而没有做登录。等后续添加了用户登录功能,并且成功登陆后这个问题就能解决。
(4)profile文件工作原理
- profile文件被busybox(init进程)自动调用的,所以是认名字的。
2、如何看到用户登录界面
(1)linux中的原则是,用一个小程序来完成一个功能。
- 如果产品需要很复杂的综合型的功能,先使用很多个小程序完成其中的一个功能,然后再将这些小程序集成起来完成整个大功能的产品。
- 这种集成很多个小程序来完成一个大的功能的思路,有很多种技术实现。譬如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)在inittab中修改,去掉/bin/sh,换上/bin/login,则系统启动后出现登录界面。可以输入用户名和密码。
(2)实验现象:成功出现用户登录界面,但是死活密码不对。
2、添加passwd和shadow文件
(1)为什么用户名和密码不对?因为根本没有为root用户设置密码。
(2)linux系统中用来描述用户名和密码的文件是passwd和shadow文件,这两个文件都在etc目录下。
- passwd文件中存储的是用户的密码设置,shadow文件中存储的是加密后的密码。
(3)直接复制ubuntu系统中的/etc/passwd和/etc/shadow文件到当前制作的rootfs目录下,然后再做修改即可。假如只有root用户。
- 每个文件都只保留root的内容;
- 从passwd文件中可知,需要将/root改为/root/rootfs/root/目录,bash(busybox不支持bash)改为sh
- shadow只保留root的内容,然后剩下的不用修改,密码和拷贝源一样。
(4)shadow中默认有一个加密的密码口令,这个口令和拷贝源的shadow本身有关。
- 比如拷贝源的ubuntu中root用户的密码是root,那么复制过来后登陆时的密码还是root。
3、重置密码实践
(1)ubuntu刚装好时,默认用普通用户登录,默认root用户是关闭的。
- 普通用户的密码是在装系统的时候设置的,普通用户登陆后使用su passwd root给root用户设置密码,设置了密码后root用户才可以登录。
(2)原因是root用户在/etc/shadow文件中加密口令是空白的,所以是不能登录的。
(3)busybox中因为没有普通用户,因此如果root用户的加密口令是空的,则默认无密码直接登录。登陆了之后,可以用passwd root给root用户设置密码。
(4)遗忘操作系统的密码的解决方法
- 用其他系统(WindowsPE系统或者ubuntu的单用户模式等)来引导启动,启动后挂载到我们的硬盘上,然后找到/etc/shadow文件,去掉密文密码后保存。
- 然后再重启系统后密码就没了。
4、getty实战
(1)inittab中最常见的用于登录的程序不是/bin/login,反而是/sbin/getty。
(2)这两个的差别不详,但是在busybox中这两个是一样的。这两个其实都是busybox的符号链接而已,因此不用严格区分这两个。
(3)我们可以在inittab中用getty替换login程序来实现同样的效果。
六、动态链接库的拷贝
1、静态编译链接helloworld程序并执行
(1)自己写一个helloworld程序,然后交叉编译连接,然后丢到开发板根文件系统中,开机后去运行。
(2)如果使用gcc编译则可以在主机ubuntu中运行,但是不能在开发板运行;要在开发板运行需要用arm-linux-gcc来交叉编译,但是此时编译文件不能在主机ubuntu中运行。
- 可以用file xx命令来查看一个elf可执行程序是哪个架构的。
(3)静态链接:arm-linux-gcc hello.c -o hello_satic -static(即在命令后添加-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。
- find -name "*.so"
(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中添加(执行某个程序的)代码。
2、前台运行与后台运行
(1)程序运行时占用当前的控制台,因此在这个程序结束前,我们都无法使用控制台,这就叫前台运行。
- 默认执行程序就是前台运行的。
(2)后台运行就是让这个程序运行,并且同时让出控制台。
- 程序可以照常运行,而且不影响当前控制台的使用。
- 让一个程序后台运行的方法就是 ./xxx &
3、开机装载驱动等其他开机自动执行
4、实际开发中rootfs的rcS是怎样的?
(1)以X210开发板九鼎科技做的rootfs中rcS部分来分析;
- 分析inittab发现,sysinit执行rcS,shutdown时执行rcK;
- 分析/etc/init.d/rcS和rcK文件发现,rcS和rcK都是去遍历执行/etc/init.d/目录下的S开头的脚本文件,区别是rcS传参是start,rcK传参是stop。
(2)由此可知
- 正式产品中的rcS和rcK都是一个引入,而不是真正干活的。
- 真正干活的配置脚本是/etc/init.d/S??*。
- 这些文件中肯定有一个判断参数是start还是stop,然后start时去做一些初始化,stop时做一些清理工作。
八、制作ext2格式的镜像并烧录启动
1、确定文件夹格式的rootfs可用(用NFS启动方式验证此文件夹格式的rootfs可用)
- 设置bootargs为nfs启动方式;
- 然后使用刚才在主机ubuntu中做好的文件夹格式的rootfs来启动;
- 查看启动效果,作为将来用ext2格式的镜像烧录时的参照物。
2、动手制作ext2格式的镜像
利用文件夹格式的rootfs,通过一些操作得到镜像格式的rootfs。
(1)执行下面命令
- 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/即我们的/root/rootfs?
(3)执行下面命令
- umount /dev/loop1
- losetup -d /dev/loop1
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制作实验圆满完成。