当前观点:监视嵌入式系统内进程间通信的技术原理是什么?

在生活中,很多人都不知道监视嵌入式系统内进程间通信的技术原理介绍是什么意思,其实他的意思是非常简单的,下面就是小编搜索到的监视嵌入式系统内进程间通信的技术原理介绍相关的一些知识,我们一起来学习下吧!

本文详细描述了一种利用 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 */

关键词: