嵌入式体系中进程间通讯的监督要领
发布日期:2011-04-12
本文细致形貌了一种利用 ptrace 体系调用,实现嵌入式体系内部进程通讯的监督要领,并提供了相应的实现方案。
概述
巨大的嵌入式体系中,通常同时运行着相称多的进程。这些进程之间频繁的举行着大量的通讯举措。进程的运行状态与这些不绝产生的通讯有着直接和精密的接洽。通过对进程间通讯的监督,开辟职员可以掌控体系内部运转的状态。发明错误时,利用获取到的进程间通讯的信息,调试工程师更容易发明题目之地点。
但是,嵌入式体系与开辟职员的接口每每较为单一。开辟职员遍及利用通常是基于串口或是网络接口的终端( console )方法。在这个模式下,开辟职员难以过细正确的观察进程间的通讯。并且对付谋略本领单薄的嵌入式体系来说,在终端上打印出通讯报文既会影响体系内部的运行,同时,也会使屏幕上弥漫的过多的无用信息,使开辟职员的阐发事变无从动手。
为了办理这个题目,在嵌入式 Linux 的平台上,我们开辟了一整应用于监督嵌入式体系内进程间通讯的软件,用于调试我们开辟的嵌入式产品。本文细致先容了监督嵌入式体系内进程间通讯的技能原理和实现监督软件的保举方案。
监督要领的基源头根本理
Linux 中的 ptrace 体系调用是监督进程间通讯的关键。 ptrace 为我们提供了一种观察和控制别的进程的要领。利用 ptrace ,我们可以截获正在运行的进程的全部的体系调用。所谓截获是指,监督步伐可以在这些体系调用产生和退出时,得到体系调用的参数,乃至修改参数。这些体系调用包括: read , write , sendto, recv 等等。在 Linux 中,用户可以通过“ man syscalls ”来查察当前版本的 Linux 所支持的体系调用。
在我们的 Linux 嵌入式产品中, AF_UNIX 域的 socket 被遍及利用。它被用来完成进程间通讯的事变。 AF_UNIX 域的 socket 的编程模型与通常的 socket 编程模型完全雷同。我们的利用要领是:吸取进程创建一个 AF_UNIX 域的 socket ,设置其模式为数据报( SOCK_DGRAM )。在这之后,为其绑定一个含路径的文件名,比方: /var/tmp/receive.unix 。这个文件名被内核用于标识socket。发送进程创建一个雷同模式的 AF_UNIX 域的 socket 。然后,调用 sendto 向吸取进程发送消息。用来标识吸取进程 socket 的便是前面提到的文件名,也便是 /var/tmp/receive.unix 。而吸取进程利用 recvfrom 体系调用,就可以收到发送进程发出的消息。
因此,通过 ptrace ,一旦我们担当了被监督进程的 sendto 和 recvfrom 体系调用,将使我们可以或许截获到利用这两个体系调用举行通讯的数据。
ptrace 体系调用的定义如下:
#include <sys/ptrace.h> long int ptrace(enum __ptrace_request request, pid_t pid, \ void * addr, void * data); |
它共有四个参数。 request 的值决定 ptrace 实行什么样的任务。 pid 指明被追踪的进程的 id 。 request 参数决定了是否必要一个有效的 addr 参数,还是仅用 NULL 即可。要是有须要利用有效的 addr 参数,它的含义是被追踪的进程的进程空间的偏移量。 data 雷同于 addr 参数,偶然也可以利用 NULL 来代替。要是它被利用,它的含义是指向一些数据,这些数据盼望被安排到被监督的进程的用户空间中。
一个完备的示例代码将向我们展示监督进程间通讯的技能细节和关键点。代码按前后次序分段阐明。
#include <stdio.h> #include <stdlib.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <linux/user.h> #include <sys/socket.h> #include <sys/un.h> #include <linux/net.h> |
为了在步伐中利用 ptrace 体系调用,我们必要增长 ptrace.h 头文件。为了可以或许得到截获的体系调用的函数入参,我们必要利用 struct user_regs_struct 布局。它在 user.h 中被定义。由于在步伐中利用了信号,因此,我们也必要 wait.h 。我们要监督通讯举措, socket.h 和 un.h 则是必不可少的。
下面是步伐的入口主函数:
int main (int argc, char *argv[]) { int status; int syscall_entry = 0; int traced_process; struct user_regs_struct u_in; |
status 用于记录被监督进程的状态变革; syscall_entry 记录被监督进程当前是进入体系调用,还是从体系调用中返回; u_in 用来得到截获的体系调用的参数; traced_process 则是被监督进程的 PID 值。
traced_process = atoi(argv[1]); /* 屈从令行得到监督进程的PID */ ptrace(PTRACE_ATTACH, traced_process, NULL, NULL); wait(&status); /* 等待被监督进程状态变革 */ ptrace(PTRACE_SYSCALL, traced_process, NULL, NULL); |
参数为 PTRACE_ATTACH 的 ptrace 对被监督进程在内核中的进程布局举行修改。使被监督进程成为当前步伐的子进程。一旦被监督进程的状态产生变革, wait() 将返回。步伐再次调用 ptrace 。这次的参数为 PTRACE_SYSCALL 。被监督进程的进程布局再次被修改,其 trace 标记被激活。内核将在被监督进程的每一次体系调用时,触发当前步伐的运行。
While (1) { /* 等待被监督步伐调用体系调用或是产生别的状态变革 */ wait(&status); /* 要是被监督进程退出,函数返回真。步伐退出 */ if ( WIFEXITED(status) ) break; ptrace(PTRACE_GETREGS, traced_process, 0, &u_in); if (u_in.orig_eax == 102 && u_in.ebx == SYS_SENDTO) { if (syscall_entry == 0) { /* syscall entry */ insyscall = 1; printf("call sendto()\n"); } else { /* Syscall exit */ Syscall_entry = 0; } } ptrace(PTRACE_SYSCALL, traced_process, NULL, NULL); } /* while */ return 0; } /* main */ |
被监督进程的 trace 标记被激活后,它的每一次体系调用都市被内核查抄。我们步伐也随之被内核用信号关照。利用参数 PTRACE_GETREGS 的 ptrace() 将得到截获的体系调用的参数。最紧张的参数是体系调用号。它生存在了 u_in.orig_eax 中。通过体系调用号,我们可以确定产生的是那一个体系调用。体系调用号可以在 Linux 的源代码中查找。它的定义在 linux-source-2.6.xx/arch/x86/kernel/syscall_table_32.S 中。它的部分代码如下所示:
.long sys_fstatfs /* 100 */ .long sys_ioperm .long sys_socketcall .long sys_syslog |
在这里,我们最体贴的是 sendto 体系调用。在 Linux 的内核中, sendto 的真实入口是 socketcall 体系调用。它是 bind , sendto 等socket干系体系调用的入口。在这个体系调用中,通过一个 call number 来区分出 bind , sendto 等差别的子体系调用。在我们的步伐中,这个 call number 生存在 u_in.ebx 中。 从上面的 syscall_table_32.S 示例代码就可以看出, socketcall 的体系调用号是102(从100向下数两行)。而 call number 则在 net.h 有定义,我们体贴的 sendto 的 call number 被定义为 SYS_SENDTO ,其绝对值为11。有了这两个紧张的数据,我们的步伐据此果断当前产生的体系调用是否为 sendto 。这一点表现为代码:
if (u_in.orig_eax == 102 && u_in.ebx == SYS_SENDTO) |
被监督进程进入体系调用和退出体系调用时,都市触发 wait() 返回,使我们的步伐有机遇运行。因此,我们必要利用 syscall_entry 来记录当前时候是被监督进程进入体系调用,还是退出体系调用。这是一个开关量,非常容易明白。 末了,每次处理惩罚完,都必要再次调用参数为 PTRACE_SYSCALL 的 ptrace ,准备监督下一次的体系调用。
上面的步伐固然很大略,但已经可以完备的表现出利用 ptrace 截获被监督进程的 sendto 体系调用的进程。值得补充一点的是,利用 ptrace 也可以得到 sendto 向外发送的数据。
sendto 体系调用的定义是:
#include <sys/types.h> #include <sys/socket.h> size_t sendto(int s, const void *msg, size_t len, int flags, \ const struct sockaddr *to, socket len_t tolen); |
sendto 包括了六个参数,分外是 msg 参数指出了发送的数据内容。参数 to 指出了发送的目标。利用 PTRACE_PEEKDATA 参数的 ptrace ,监督步伐将可以得到 sendto 的全部的六个参数。如许监督步伐就完全得到了被监督进程要向外发送的数据和发送目标。详细的实现细节在此不再展开叙述。请参考 man ptrace 阐明手册。
监督体系的体系和应用
利用上面讨论的技能,我们开辟了可以运行在 mips 目标板上的监督步伐,名为 ipcmsg 。它是一个下令行步伐。在我们的应用环境中,它的利用要领是:
root@host:~$ ipcmsg -p pid -l xxx.xxx.xxx.xxx -b 6000 |
pid 是被监督进程的 pid ,可以通过 ps 下令得到。 -l 参数背面指定 PC 主机的 IP 地点。 -b 参数指明白吸取的端标语。
最初举行监督时, ipcmsg 是没有 IP 地点和端标语参数的。全部信息是输出到串口控制外观中。这既影响了运行的服从(大量的在串口上的输出会影响目标板的运行速率),也倒霉于信息的处理惩罚。由于我们的目标板具备以太网接口,我们很容易的想到将 ipcmsg 截获的数据包转发到 PC 主机上。利用 PC 主机更便于对进程间通讯的数据包举行阐发。在 PC 主机上,我们利用 wireshark 这个非常盛行的开源的网络报文阐发软件吸取来自目标板的信息。
在实际的利用进程中,我们利用以太网线将目标板与 PC 主机相连。然后,在目标板上启动 ipcmsg ,并为其指定监督进程的 pid 。 ipcmsg运行后,我们在PC主机上启动 wireshark 吸取来自 ipcmsg 的数据包。这些数据包中包括了 mips 目标板上进程间通讯的信息。利用我们为 ipcmsg 专门开辟的 wireshark 插件,在 wireshark 上,我们可以细致的分析 ipcmsg 转发来的数据包,非常直观的阐发进程间通讯的进程和大概存在的题目。
从图中可以看到,我们从 ipcmsg 得到了进程间通讯的方法,参数( path 是 AF_UNIX域 socket 地点参数),方向和内容,以及进程名称。这些信息资助我们对嵌入式体系的运行状态举行阐发。而这统统非常直观和便于操纵。