嵌入式复位流程研究与优化

来源:互联网 发布:应聘java简历自我评价 编辑:程序博客网 时间:2024/06/03 04:28

背景

嵌入式系统中,一般均可通过写寄存器的方式进行整个系统的复位。该种复位方式类似于直接下电。若在复位时,存在对文件系统的读写,则可能造成文件系统的损坏。因此,在复位前,需要针对文件系统进行保护。

老复位流程

老复位流程如下:
(1) 调整当前任务优先级为99;
(2) 给所有进程(除自己)发SIGTERM以及SIGKILL信号;
(3) 关闭所有fd;
(4) umount所有文件系统;
(5) 写寄存器复位
若直接使用该流程,在复位过程中,会看到大量与错误打印,大多数和套接字、消息队列等有关系。查看系统错误码(0x9)含义为bad file description。对比上述流程,在步骤(3)中就会将所有fd关闭。对于Linux操作系统,“一切皆文件”,因此系统中套接字、消息队列等等均需使用fd。在telnet控制台上输入ls –al /proc/进程pid/fd,即可查看进程已打开的所有fd,打印如下(仅列出部分):

lrwx------    1 root     root            64 Mar  2 05:54 1 -> socket:[232]l-wx------    1 root     root            64 Mar  2 05:54 10 -> pipe:[23561]lrwx------    1 root     root            64 Mar  2 05:54 101 -> /MsgQ202lrwx------    1 root     root            64 Mar  2 05:54 102 -> socket:[24765]lrwx------    1 root     root            64 Mar  2 05:54 103 -> socket:[24766]lrwx------    1 root     root            64 Mar  2 05:54 104 -> socket:[24767]lrwx------    1 root     root            64 Mar  2 05:54 105 -> /MsgQ2

问题分析

出现刷屏打印,是由于错误地关闭了socket、消息队列等的fd,接收任务使用这些fd时,fd已经非法,导致刷屏打印。另一方面,关闭fd的目的实际上是为了后面umount文件系统,所以,我们不需要关闭所有已打开的fd,只需要关闭与文件系统有关的文件即可。

问题解决

通过fstat甄别

与fd有关的首先想到的是fstat函数,原型如下:

int fstat(int filedes, struct stat *buf);

通过传递fd,可以获取一个struct stat结构,struct stat结构如下:

struct stat {    dev_t     st_dev;     /* ID of device containing file */    ino_t     st_ino;     /* inode number */    mode_t    st_mode;    /* protection */    nlink_t   st_nlink;   /* number of hard links */    uid_t     st_uid;     /* user ID of owner */    gid_t     st_gid;     /* group ID of owner */    dev_t     st_rdev;    /* device ID (if special file) */    off_t     st_size;    /* total size, in bytes */    blksize_t st_blksize; /* blocksize for filesystem I/O */    blkcnt_t  st_blocks;  /* number of blocks allocated */    time_t    st_atime;   /* time of last access */    time_t    st_mtime;   /* time of last modification */    time_t    st_ctime;   /* time of last status change */};

通过下列宏,可以判断出当前文件的文件类型,参数为stat结构中的st_mode:
S_ISREG() 普通文件
S_ISRDIR() 目录文件
S_ISCHR() 字符特殊文件
S_ISBLK() 块特殊文件
S_ISFIFO() 管道或FIFO
S_ISLNK() 符号链接
S_ISSOCK() 套接字
在程序中首先针对fd调用fstat,然后使用S_ISREG进行判断,当为普通文件时,才执行close操作。使用该方案后,仍然会打印消息队列相关的失败。而之前套接字相关的打印已经消失,说明,我们已经剥离出了fd是socket的情况,但是仍然没有剥离出是消息队列的情况。
通过阅读APUE,POSIX.1允许将进程间通信(IPC)对象(如消息队列和信号量等)说明为文件。可以使用下面的宏来进行判断,参数是stat结构指针:
S_TYPEISMQ() 消息队列
S_TYPEISSEM() 信号量
S_TYPEISSHM() 共享存储对象
在代码里再次加入这些判断,即必须为普通文件(S_ISREG)且不能为消息队列、信号量、以及共享存储对象中的任意一个,加入后仍然刷打印。注意到,APUE中提到的进程间通信,而我们刷打印的应该是一个线程级别的消息队列。

转化为绝对路径甄别

另外一种思路,就是将fd转化为绝对路径,通过判断绝对路径中,是否含有存储设备的挂载点(/ata1,/ata2,/flashDev,/bin/tool),仅关闭含有挂载点的文件。这里使用的函数是readlink函数,该函数获取一个符号链接所指向的路径。在/proc/进程号/fd目录下的,实际上均是符号链接,我们只需要遍历fd下的所有文件,获取其链接指向的路径,然后判断其中是否有挂载点即可。
为了验证该方法的准确性,我们首先使用creat函数,在/ata1下创建一个目录:

creat "/ata1/test",0677value = 264 = 0x108

返回值264即为打开的文件指针,然后去/proc/进程ID/fd目录下寻找该fd,如下:

l-wx------    1 root     root            64 Mar  2 12:25 264 -> /ata1/test

通过在复位前的LOG,我们确实针对该fd进行了关闭。

0 0
原创粉丝点击