卢骏 · 2020年09月09日

根文件系统(三)profile文件与跟文件镜像制作

一、profile文件与用户登录

/etc/sysconfig/HOSTNAME文件中保存了主机名。在/etc目录下,创建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 

再次启动开发板,就会打印设置的hostname。并且还会有#号。也就是命令提示符。
11.png

但是没有显示用户。

1、profile的工作原理

profile文件是被busybox(init进程)自动调用执行的,所以这个文件的文件名是固定的。

2、用户登录

linux中有一个原则就是用一个小程序来完成一个功能。如果需要很负责的功能,我们倾向于先使用很多个小程序完成其中的一个功能,然后再将这些小程序集成起来完成整个大功能的产品。

这种集成很多个小程序来完成一个大的功能,有很多中技术实现。如shell脚本,还有一些别的技术,如linux启动中的inittab。

因为在inittab中有一个配置项:

::askfirst:-/bin/sh 

这个配置项的作用就是当系统启动后,就去执行/bin/sh,执行这个就会出现命令行。因此这样的安排,就会直接进入命令行,而不会出现登录界面。

为了要出现登录界面,就不能直接执行/bin/sh,而应该执行一个负责出现登录界面并且负责管理用户名和密码的一个程序。busybox中也集成了这个程序(就是/bin/login和/sbin/gettty)。因此要在inittab中用/bin/login或者/sbin/gettty替代/bin/sh。

3、用户名和密码的设置

用户名和密码的设置和登录程序有关联的,但是/bin/login和/sbin/gettty在用户名和密码的管理上是一样的。其实常见的所有的linux系统的用户名和密码的管理几乎都是一样的。

密码一般都是用加密文字的,而不是用明文。系统中的密码是保存在一个专门用来保存密码的文件中存储的。用明文存密码有风险,因此linux系统用密文来存储密码的。

二、用户登录实战
1、修改inittab文件

修改inittab文件,将askfirst去掉,增加sysinit:/bin/login

#first:run the system script file
::sysinit:/etc/init.d/rcS
#::askfirst:-/bin/sh
::sysinit:/bin/login
::ctrlaltdel:-/sbin/reboot
#umount all filesystem
::shutdown:/bin/umount -a -r
#restart init process
::restart:/sbin/init 

重启开发板,出现登录信息。然后可以输入用户名和密码。

22.png

输入用户名,再输入密码,但是会提示错误,因此就不能登录。原因在于没有添加用户,也没有设置用户的密码。
3.png

2、添加passwd和shadow文件

linux系统中用来描述用户名和密码的文件是passwd和shadow文件,这两个文件都在/etc目录下。passwd文件中存储的是用户的密码设置,shadow文件中存储的是加密后的密码。

直接复制linux发行版的/etc/passwd和/etc/shadow文件到当前制作的rootfs目录下,然后在做修改。

将/etc/passed修改为以下,设置用户root,该用户的home目录在/root下,使用的shell是/bin/sh(busybox默认使用的shell)

root:x:0:0:root:/root:/bin/sh 

将/etc/shadow文件修改为以下:

root:$6
$MB53NA9D$T64dgJ1jNCvS.9UaghQSQrazGzl.hifUHLyADjoQvRaS5HXxIVBM8lna61wKIuyu5X.LYqneGRlZuc0jif4x51:16586:0:99999:7::: 

中间的字符串就是密码为root的加密信息。

重启开发板,使用用户root,密码root登录,登录成功,登录后,shell自动进入/root目录。
4.png

busybox中没有普通用户,如果默认root用户加密口令是空的,那么登录root用户不需要输入密码即可登录。之后登陆了之后,就用passwd命令,给root用户设置密码。

3、getty实战

inittab中最常见的用于登录的程序不是/bin/login,而是/sbin/getty。

在busybox中这两个是一样的。这两个其实都是busybox的符号链接而已。

可以在inittab中用getty替换login程序来实现同样的效果。

#first:run the system script file
::sysinit:/etc/init.d/rcS
#::askfirst:-/bin/sh
#s3c2410_serial2::sysinit:/bin/login
s3c2410_serial2::respawn:/sbin/getty -L s3c2410_serial2 115200 vt100
::ctrlaltdel:-/sbin/reboot
#umount all filesystem
::shutdown:/bin/umount -a -r
#restart init process
::restart:/sbin/init 
三、动态链接库的拷贝
1、静态编译链接helloword程序并执行

实现一个helloworld程序,然后交叉编译链接,然后放到开发板中去执行。

#include <stdio.h>  
int main(void)
{
   printf("hello world\n");
   return 0;
} 

使用arm-none-linux-eabi-gcc工具对该程序进行静态编译。要加上-static选项。

arm-none-linux-gnueabi-gcc –static hello.c  -o hello

将该程序,放置在开发板上执行,可以打印出hello world。

2、动态编译链接helloworld程序并执行

使用arm-none-linux-eabi-gcc工具对该程序进行动态编译。

arm-none-linux-gnueabi-gcc hello.c -o hello_dy

可以用ls –lh查看这两个生成文件的大小。
5.png

可以看出,使用静态链接,生成的文件是643k大小,使用动态链接,生成的文件是5.7k大小。

将该程序放到开发板上运行,就出现错误。
6.png

动态链接hello的程序要调用到printf函数,而printf函数在动态链接时要在运行时环境中取寻找对应的库文件(开发板rootfs中部署的动态链接库中包含了printf函数的哪个库文件)。如果找到了,printf函数就会被成功解析,然后hello程序就会被执行,如果找不到则程序就不能被执行,命令行会提示错误信息。

-sh: ./hello_dy : not found

解决方法:将arm-linux-gcc的动态链接库文件复制到开发板rootfs的/lib目录下即可解决。

在交叉编译器的libc/lib下,即是动态链接库。

/usr/local/arm/arm-none-linux-gnueabi/libc/lib

7.png

在开发板的根文件系统创建lib文件夹,然后将上面的动态链接库都拷贝到lib文件夹下去。

cp /usr/local/arm/arm-none-linux-gnueabi/libc/lib/*so* ./ -rdf 

只拷贝动态库文件so,加参数-rdf,表示不考虑符号链接文件。

此时再执行hello_dy程序,就可以执行了。
8.png

3、使用strip工具去掉库中符号信息

动态链接库so文件包含了调试符号信息,这些符号信息在运行时是没用的(调试时使用),这些符号会占用一定空间。在传统的嵌入式系统中flash空间是有限的,为了节省空间常常把这些符号信息给去掉,这样节省空间不影响运行。

使用du –h lib查看lib大小,为3.8M。

9.png

使用arm-none-linux-gnueabi-strip命令,将动态库的符号信息去掉,再查看空间,lib的空间大小变为3M,节省了0.8M空间。
10.png

四、开机自启动

让一些应用程序,在开机后自动启动。

开机自启动的实现原理,就是在开机会自动执行的脚本rcS中添加执行某个程序的语句代码即可。

比如在/root目录下有一个应用程序 hello_dy。那么修改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.77
cd /root && ./hello_dy 

那么在开发板开机后,就会自动执行/root目录下的hello_dy程序。

五、制作ext2格式的镜像
1、制作ext2格式的镜像
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    ./ext_rootfs/ 

创建ext_rootfs文件夹,用于挂载ext2镜像。挂载后,就将之前做好的根文件系统复制rootfs中。

dd命令,制作一个10M大小(扇区大小是1024字节,count大小是10240,那么总大小是1024字节 * 10240 = 10M)数据文件,并且往数据文件全部填充0。

losetup命令,将数据文件rootfs.ext2虚拟化成块设备/dev/loop1。

mkde2fs命令,在虚拟化的块设备上,制作ext2文件类型的根文件系统。

mount命令,挂载创建的文件系统到ext_rootfs目录下。

然后将busybox制作好的文件,拷贝到ext_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    ./ext_rootfs/ 

这样,就得到了一个rootfs.ext2镜像文件,这个就是制作好的ext2格式的镜像。

2、烧录并设置参数

使用fastboot进行烧录。

fastboot flash system rootfs.ext2

设置开发板的bootargs。

set bootargs "console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext2"

重启开发板,kernel就可以挂载这个ext2格式的根文件系统了。

六、总结

以上就是rootfs的制作步骤和原理。使用busybox,制作根文件系统,然后往这根文件系统里面加些必要的文件夹和文件。制作的时候,使用nfs方式进行挂载测试,最终测试完毕后,在用工具制作成指定格式的文件镜像,烧录到开发板中即可。

更多相关阅读

arm64 earlycon分析
根文件系统与ramdisk用
根文件系统(二)busybox构建根文件系统

原文首发于骏的世界博客
作者:卢骏
更多IC设计相关的文章请关注IC设计极术专栏,每日更新。

推荐阅读
关注数
10943
内容数
1209
主要交流IC以及SoC设计流程相关的技术和知识
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息