在AM335X的SD卡更新系统学习记录中最后更新完系统后,以后运行应用程序都会从EMMC中取出Linux系统运行。接着介绍Linux系统是怎么自己启动我们编写的应用程序的。
1、在AM335X的SD卡更新系统学习记录中已经详细介绍了整个系统的启动流程,这里在复述一下。AM335X上电后,根据Boot Sequence启动配置(LCD0-LCD15引脚,具体可参考TI官方的335X参考手册),从相应的存储设备启动,这里配置的是从SPI启动。内部的ROM从SPI的flash中拷贝镜像到RAM中运行。然后启动u-boot,u-boot启动后会从MMC的/sys分区的/boot目录下取出uImage镜像再到RAM中运行。Linux启动后,会接着挂接EMMC的/sys分区,启动BusyBox里的Init进程。init进程首先主要对/etc/inittab文件进行解析,然后按照它的指示创建各种子进程。inittab文件的内容主要以下几部分。其中系统运行后最先执行的是/etc/rc.d/rcS进程,并且它只执行一次,所以应用程序的启动是在这里运行的。
::sysinit:/etc/rc.d/rcS ::respawn:/sbin/getty 9600 tty1 ::ctrlaltdel:/sbin/reboot ::shutdown:/etc/rc.d/rcS stop ::restart:/sbin/init
2、接着分析/etc/rc.d/rcS文件,这是一个脚本文件。这个文件的分析在AM335X的SD卡更新系统学习记录已经分析过了这里不再重复。直接看到这个脚本运行的另一个脚本/etc/rc.d/rc.local start。下面是它的内容,可以看到它的工作主要是启动daemon守护进程,应用程序由它启动,并且守护,所谓守护进程,就是不依赖于控制台会话的进程,它可以在应用程序意外退出时重新启动应用程序。如果把daemon换成应用程序,这样应用程序就能自启动了。
#!/bin/sh mode=${1:-start} if [ $mode = "start" ] then #set sgtl5000 volume 0~127 amixer cset numid=5 65 #set power led light echo 1 > /sys/class/leds/user_led0/brightness #start daemon daemon # 启动守护进程 fi
3、接着分析daemon程序,它的配置文件存放在/opt/Daemon/config/daemon.xml中。主要配置的是需要启动的程序路径与名称。并且可以添加多条,其中/opt/App为目录,xxx.out为可执行文件名。并且可以设置多个路径与文件。
<ProcList Path="/opt/App" Name="xxx.out" args=""/>
4、分析到这里。可以知道一下几点。
a、存在在一个名为/opt的分区
b、其中应用程序存放在/opt/App中
c、其中daemon程序的配置文件存放在/opt/Daemon/config/daemon.xml中
d、daemon程序被放在/usr/sbin中
插上U盘后,会出现PID为13673的进程,进而出现13675、13683的进程。从这一现象可以发现./Daemon.out的另外一个作用,检测有无U盘插入,若有U盘插入的话运行/shell/udisk脚本。接下来跟着这个脚本慢慢分析。
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
13577 1 root S 28776 3.7 0 0.0 ./Daemon.out 13673 13577 root S 2936 0.3 0 0.0 sh -c ./shell/udisk mount /dev/sda1 13675 13673 root S 2936 0.3 0 0.0 {udisk} /bin/sh ./shell/udisk mount /dev/sda1 13683 13675 root S 2936 0.3 0 0.0 {update} /bin/sh ./shell/update ./download/xxx.bin
5、开始分析以下脚本,其中$1=mount;$2=/dev/sda1。可以看到其中有一句./shell/update ./download/xxx.bin。这个脚本将在接下来分析
sh -c ./shell/udisk mount /dev/sda1 $0 $1 $2
#!/bin/sh case "$1" in mount) #挂载U盘 if [ ! -b $2 ] #是否存在/dev/sda1块设备装置 then echo "$2 not exist!" #如果不存在打印出不存在,然后退出 exit 0 fi #cd /opt/Daemon echo "Current Path " $(pwd) "..." #当前路径已经在Daemon中切换到/opt/Daemon中了 echo "mount $2 to /mnt/udisk..." echo 1 > /sys/class/leds/user_buzzer/brightness #打开蜂鸣器 mkdir /mnt/udisk #创建/mnt/udisk目录 mount -t vfat $2 /mnt/udisk #以fat文件系统格式挂接/dev/sda1到/mnt/udisk echo 0 > /sys/class/leds/user_buzzer/brightness #关闭蜂鸣器? if [ -x /mnt/udisk/xxx.bin ] #如果U盘里的xxx.bin文件可执行 then echo "exist firmware file[xxx.bin], auto update self..." - rm /log/x.log cp -a /mnt/udisk/xxx.bin ./download/xxx.bin #以递归、保留符号链接的方式拷贝文件到/opt/Daemon/download中 chmod +x ./download/xxx.bin #加上可执行权限 ./shell/update ./download/xxx.bin #解压出xxx.bin文件到相应的目录下 ./shell/hmkill 1 #运行/shell/hmkill 1脚本,用于杀掉现在运行的进程 ./daemon.sh #运行新更新的守护进程 fi ;; umount) #卸载U盘 if [ -d /mnt/udisk ] #如果/mnt/udisk存在 then echo "umount /mnt/udisk..." umount /mnt/udisk #卸载/mnt/udisk rm -rf /mnt/udisk #删除/mnt/udisk fi ;; *) echo "Usage: /shell/udisk {mount|umount}" #其它 echo exit 1 ;; esac #case语句结束 exit 0
对以上脚本功能做一个总结:
a、挂载U盘到/mnt/udisk
b、读出U盘的MD5文件xxx.bin。并且解压出xxx.bin文件到相应的目录下。
c、杀掉现在运行的现在运行的daemon进程、app进程
d、运行新的daemon进程,这个进程会运行新的app进程
6、分析./shell/update ./download/xxx.bin。主要作用是读出U盘的MD5文件xxx.bin。并且解压出xxx.bin文件到相应的目录下,分析如下:
#!/bin/sh # if [ ! -f $1 ] #检测文件是否存在 then echo "$1 not exist!" exit 0 fi echo 1 > /sys/class/leds/user_buzzer/brightness #打开蜂鸣器 chmod +x $1 #加可执行权限 dd if=$1 of=env.sh bs=1 count=128 #从xxx.bin读入文件放入env.sh中,每次读1字节,一共读128字节 . env.sh #执行env.sh脚本 # 生成原始打包文件 dd if=$1 of=$NAME bs=1 count=$SIZE skip=128 #从xxx.bin读入文件放入NAME中,每次读1字节,一共读SIZE字节,跳过前128个字节 # 执行脚本 MD5=`md5sum $NAME | awk '{print $1}'` #MD5校验 if [ "$SIGN" = "$MD5" ] #如果校验成功 then tar -zxvf $NAME -C / #解压 chmod +x /opt/Daemon/Daemon.out #加上可执行权限 chmod +x /opt/Daemon/shell/x #加上可执行权限 chmod +x /opt/App/x.out #加上可执行权限 sync #同步,立刻写入磁盘 fi rm env.sh #删除之前生成的文件 rm $NAME #删除之前生成的文件 rm $1 #删除之前生成的文件 echo 0 > /sys/class/leds/user_buzzer/brightness #关闭蜂鸣器 exit 0 #退出
7、制作xxx.bin的MD5文件,其中opt里存放的是App目录与Daemon目录,pack.sh是一个脚本文件,用于打包成加密的MD5格式。这个三步之后生成xxx.bin文件。放入FAT32格式的U盘,将这个U盘插入到335X的USB口即可实现更新。
a、sudo tar -czvf ./xxx.tar.gz ./opt
b、sudo chmod 755 ./xxx.tar.gz
c、sudo ./pack.sh ./xxx.tar.gz
8、分析pack.sh脚本,这个脚本的前面128字节为其他数据。与/shell/update脚本对应,/shell/update脚本会先取出前128字节。然后取出MD5签名,然后解密文件。
1 # !/bin/sh 2 # 3 4 [ $# != "1" ] && echo "Usage: $0 tarfile" && exit 0 #参数个数是否为1 5 6 FILE="reader.bin" #加密后的文件名 7 NAME="$1" #待加密的文件名 8 SIZE=`wc -c $1 | awk '{print $1}'` #以字节方式计算出待加密文件大小 9 SIGN=`md5sum $1 | awk '{print $1}'` #加密,MD5签名生成 awk处理输入的整行数据的某个字段(以空格分开)、sed处理输入的整行数据 10 11 echo "NAME=$NAME" >> $FILE # 被打包文件名称 12 13 echo "SIZE=$SIZE" >> $FILE # 被打包文件大小 14 15 echo "SIGN=$SIGN" >> $FILE # 被打包文件MD5签名 16 17 chmod +x $FILE # 加上可执行权限 18 19 dd if=$1 of=$FILE bs=1 count=$SIZE seek=128 # 在被打包文件前面加上$FILE内容,共计128字节