《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 模擬設(shè)計 > 設(shè)計應(yīng)用 > 監(jiān)視嵌入式系統(tǒng)內(nèi)進程間通信的技術(shù)原理
監(jiān)視嵌入式系統(tǒng)內(nèi)進程間通信的技術(shù)原理
摘要: 本文詳細(xì)描述了一種利用ptrace系統(tǒng)調(diào)用,實現(xiàn)嵌入式系統(tǒng)內(nèi)部進程通信的監(jiān)視方法,,并提供了相應(yīng)的實現(xiàn)方案,。概述復(fù)雜的嵌入式系統(tǒng)中,,常常同時運行著相當(dāng)多的進程。
Abstract:
Key words :

本文詳細(xì)描述了一種利用 ptrace 系統(tǒng)調(diào)用,,實現(xiàn)嵌入式系統(tǒng)內(nèi)部進程通信的監(jiān)視方法,,并提供了相應(yīng)的實現(xiàn)方案,。
    概述

    復(fù)雜的嵌入式系統(tǒng)中,常常同時運行著相當(dāng)多的進程,。這些進程之間頻繁的進行著大量的通信動作,。進程的運行狀態(tài)與這些不斷發(fā)生的通信有著直接和緊密的聯(lián)系。通過對進程間通信的監(jiān)視,,開發(fā)人員可以掌控系統(tǒng)內(nèi)部運轉(zhuǎn)的狀態(tài),。發(fā)現(xiàn)錯誤時,利用獲取到的進程間通信的信息,,調(diào)試工程師更容易發(fā)現(xiàn)問題之所在,。

    但是,嵌入式系統(tǒng)與開發(fā)人員的接口往往較為單一,。開發(fā)人員廣泛使用通常是基于串口或是網(wǎng)絡(luò)接口的終端( console )方式,。在這個模式下,開發(fā)人員難以細(xì)致準(zhǔn)確的觀察進程間的通信,。而且對于計算能力薄弱的嵌入式系統(tǒng)來說,,在終端上打印出通信報文既會影響系統(tǒng)內(nèi)部的運行,同時,,也會使屏幕上充斥的過多的無用信息,,使開發(fā)人員的分析工作無從下手。

    為了解決這個問題,,在嵌入式 Linux 的平臺上,,我們開發(fā)了一整套用于監(jiān)視嵌入式系統(tǒng)內(nèi)進程間通信的軟件,用于調(diào)試我們開發(fā)的嵌入式產(chǎn)品,。本文詳細(xì)介紹了監(jiān)視嵌入式系統(tǒng)內(nèi)進程間通信的技術(shù)原理和實現(xiàn)監(jiān)視軟件的推薦方案,。

    監(jiān)視方法的基本原理

    Linux 中的 ptrace 系統(tǒng)調(diào)用是監(jiān)視進程間通信的關(guān)鍵。 ptrace 為我們提供了一種觀察和控制其它進程的方法,。利用 ptrace ,,我們可以截獲正在運行的進程的所有的系統(tǒng)調(diào)用。所謂截獲是指,,監(jiān)視程序可以在這些系統(tǒng)調(diào)用發(fā)生和退出時,,獲得系統(tǒng)調(diào)用的參數(shù),甚至修改參數(shù),。這些系統(tǒng)調(diào)用包括: read , write , sendto, recv 等等,。在 Linux 中,用戶可以通過“ man syscalls ”來查看當(dāng)前版本的 Linux 所支持的系統(tǒng)調(diào)用,。

    在我們的 Linux 嵌入式產(chǎn)品中,, AF_UNIX 域的 socket 被廣泛使用。它被用來完成進程間通信的工作,。 AF_UNIX 域的 socket 的編程模型與通常的 socket 編程模型完全相同,。我們的使用方法是:接收進程創(chuàng)建一個 AF_UNIX 域的 socket ,,設(shè)定其模式為數(shù)據(jù)報( SOCK_DGRAM )。在這之后,,為其綁定一個含路徑的文件名,例如: /var/tmp/receive.unix ,。這個文件名被內(nèi)核用于標(biāo)識socket,。發(fā)送進程創(chuàng)建一個相同模式的 AF_UNIX 域的 socket 。然后,,調(diào)用 sendto 向接收進程發(fā)送消息,。用來標(biāo)識接收進程 socket 的就是前面提到的文件名,也就是 /var/tmp/receive.unix ,。而接收進程使用 recvfrom 系統(tǒng)調(diào)用,,就可以收到發(fā)送進程發(fā)出的消息。

    因此,,通過 ptrace ,,一旦我們接管了被監(jiān)視進程的 sendto 和 recvfrom 系統(tǒng)調(diào)用,將使我們能夠截獲到使用這兩個系統(tǒng)調(diào)用進行通信的數(shù)據(jù),。

    ptrace 系統(tǒng)調(diào)用的定義如下:

   

   #include
       long int ptrace(enum __ptrace_request request, pid_t pid,
                       void * addr, void * data);
 

    它共有四個參數(shù),。 request 的值決定 ptrace 執(zhí)行什么樣的任務(wù)。 pid 指明被追蹤的進程的 id ,。 request 參數(shù)決定了是否需要一個有效的 addr 參數(shù),,還是僅用 NULL 即可。如果有必要使用有效的 addr 參數(shù),,它的含義是被追蹤的進程的進程空間的偏移量,。 data 類似于 addr 參數(shù),有時也可以使用 NULL 來代替,。如果它被使用,,它的含義是指向一些數(shù)據(jù),這些數(shù)據(jù)希望被放置到被監(jiān)視的進程的用戶空間中,。

    一個完整的示例代碼將向我們展示監(jiān)視進程間通信的技術(shù)細(xì)節(jié)和關(guān)鍵點,。代碼按前后順序分段說明。

  

 #include
               #include
               #include
               #include
               #include
               #include
               #include
               #include
 

    為了在程序中使用 ptrace 系統(tǒng)調(diào)用,,我們需要增加 ptrace.h 頭文件,。為了能夠獲得截獲的系統(tǒng)調(diào)用的函數(shù)入?yún)ⅲ覀冃枰褂?struct user_regs_struct 結(jié)構(gòu),。它在 user.h 中被定義,。由于在程序中使用了信號,因此,,我們也需要 wait.h ,。我們要監(jiān)視通信動作,, socket.h 和 un.h 則是必不可少的。

    下面是程序的入口主函數(shù):

   

   int main (int argc, char *argv[])
        {
            int status;
            int syscall_entry = 0;
            int traced_process;
            struct user_regs_struct u_in;
 

    status 用于記錄被監(jiān)視進程的狀態(tài)變化,; syscall_entry 記錄被監(jiān)視進程當(dāng)前是進入系統(tǒng)調(diào)用,,還是從系統(tǒng)調(diào)用中返回; u_in 用來獲得截獲的系統(tǒng)調(diào)用的參數(shù),; traced_process 則是被監(jiān)視進程的 PID 值,。

   

    traced_process = atoi(argv[1]); /* 從命令行得到監(jiān)視進程的PID */
             ptrace(PTRACE_ATTACH, traced_process, NULL, NULL);
             wait(&status);    /* 等待被監(jiān)視進程狀態(tài)變化 */
             ptrace(PTRACE_SYSCALL, traced_process, NULL, NULL);
 

    參數(shù)為 PTRACE_ATTACH 的 ptrace 對被監(jiān)視進程在內(nèi)核中的進程結(jié)構(gòu)進行修改。使被監(jiān)視進程成為當(dāng)前程序的子進程,。一旦被監(jiān)視進程的狀態(tài)發(fā)生變化,, wait() 將返回。程序再次調(diào)用 ptrace ,。這次的參數(shù)為 PTRACE_SYSCALL ,。被監(jiān)視進程的進程結(jié)構(gòu)再次被修改,其 trace 標(biāo)志被激活,。內(nèi)核將在被監(jiān)視進程的每一次系統(tǒng)調(diào)用時,,觸發(fā)當(dāng)前程序的運行。

   

  While (1) {
                 /* 等待被監(jiān)視程序調(diào)用系統(tǒng)調(diào)用或是發(fā)生其它狀態(tài)變化 */
                 wait(&status);
                
                 /* 如果被監(jiān)視進程退出,,函數(shù)返回真,。程序退出 */
                 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 */
 

    被監(jiān)視進程的 trace 標(biāo)志被激活后,它的每一次系統(tǒng)調(diào)用都會被內(nèi)核檢查,。我們程序也隨之被內(nèi)核用信號通知,。使用參數(shù) PTRACE_GETREGS 的 ptrace() 將獲得截獲的系統(tǒng)調(diào)用的參數(shù)。最重要的參數(shù)是系統(tǒng)調(diào)用號,。它保存在了 u_in.orig_eax 中,。通過系統(tǒng)調(diào)用號,我們可以確定發(fā)生的是那一個系統(tǒng)調(diào)用,。系統(tǒng)調(diào)用號可以在 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
 

    在這里,,我們最關(guān)心的是 sendto 系統(tǒng)調(diào)用,。在 Linux 的內(nèi)核中, sendto 的真實入口是 socketcall 系統(tǒng)調(diào)用,。它是 bind , sendto 等socket相關(guān)系統(tǒng)調(diào)用的入口,。在這個系統(tǒng)調(diào)用中,通過一個 call number 來區(qū)分出 bind ,, sendto 等不同的子系統(tǒng)調(diào)用,。在我們的程序中,這個 call number 保存在 u_in.ebx 中。 從上面的 syscall_table_32.S 示例代碼就可以看出,, socketcall 的系統(tǒng)調(diào)用號是102(從100向下數(shù)兩行),。而 call number 則在 net.h 有定義,我們關(guān)心的 sendto 的 call number 被定義為 SYS_SENDTO ,,其絕對值為11,。有了這兩個重要的數(shù)據(jù),我們的程序據(jù)此判斷當(dāng)前發(fā)生的系統(tǒng)調(diào)用是否為 sendto ,。這一點表現(xiàn)為代碼:

   

     if (u_in.orig_eax == 102 && u_in.ebx == SYS_SENDTO)
 

    被監(jiān)視進程進入系統(tǒng)調(diào)用和退出系統(tǒng)調(diào)用時,,都會觸發(fā) wait() 返回,使我們的程序有機會運行,。因此,我們需要使用 syscall_entry 來記錄當(dāng)前時刻是被監(jiān)視進程進入系統(tǒng)調(diào)用,,還是退出系統(tǒng)調(diào)用,。這是一個開關(guān)量,非常容易理解,。 最后,,每次處理完,都需要再次調(diào)用參數(shù)為 PTRACE_SYSCALL 的 ptrace ,,準(zhǔn)備監(jiān)視下一次的系統(tǒng)調(diào)用,。

    上面的程序雖然很簡單,但已經(jīng)可以完整的表現(xiàn)出利用 ptrace 截獲被監(jiān)視進程的 sendto 系統(tǒng)調(diào)用的過程,。值得補充一點的是,,利用 ptrace 也可以獲得 sendto 向外發(fā)送的數(shù)據(jù)。

    sendto 系統(tǒng)調(diào)用的定義是:

 

   #include
        #include
        size_t sendto(int s, const void *msg, size_t len, int flags,
                      const struct sockaddr *to, socket len_t tolen);
 

    sendto 包含了六個參數(shù),,特別是 msg 參數(shù)指出了發(fā)送的數(shù)據(jù)內(nèi)容,。參數(shù) to 指出了發(fā)送的目標(biāo)。利用 PTRACE_PEEKDATA 參數(shù)的 ptrace ,,監(jiān)視程序?qū)⒖梢垣@得 sendto 的全部的六個參數(shù),。這樣監(jiān)視程序就完全獲得了被監(jiān)視進程要向外發(fā)送的數(shù)據(jù)和發(fā)送目標(biāo)。具體的實現(xiàn)細(xì)節(jié)在此不再展開論述,。請參考 man ptrace 說明手冊,。監(jiān)視系統(tǒng)的體系和應(yīng)用

    利用上面討論的技術(shù),我們開發(fā)了可以運行在 mips 目標(biāo)板上的監(jiān)視程序,,名為 ipcmsg ,。它是一個命令行程序。在我們的應(yīng)用環(huán)境中,,它的使用方法是:

 

 root@host:~$ ipcmsg -p pid -l xxx.xxx.xxx.xxx -b 6000
 

    pid 是被監(jiān)視進程的 pid ,,可以通過 ps 命令獲得。 -l 參數(shù)后面指定 PC 主機的 IP 地址。 -b 參數(shù)指明了接收的端口號,。

    最初進行監(jiān)視時,, ipcmsg 是沒有 IP 地址和端口號參數(shù)的。所有信息是輸出到串口控制臺中,。這既影響了運行的效率(大量的在串口上的輸出會影響目標(biāo)板的運行速度),,也不利于信息的處理。由于我們的目標(biāo)板具備以太網(wǎng)接口,,我們很容易的想到將 ipcmsg 截獲的數(shù)據(jù)包轉(zhuǎn)發(fā)到 PC 主機上,。使用 PC 主機更便于對進程間通信的數(shù)據(jù)包進行分析。在 PC 主機上,,我們使用 wireshark 這個非常流行的開源的網(wǎng)絡(luò)報文分析軟件接收來自目標(biāo)板的信息,。整個監(jiān)視系統(tǒng)的架構(gòu)如下圖所示:

    在實際的使用過程中,我們使用以太網(wǎng)線將目標(biāo)板與 PC 主機相連,。然后,,在目標(biāo)板上啟動 ipcmsg ,并為其指定監(jiān)視進程的 pid ,。 ipcmsg運行后,,我們在PC主機上啟動 wireshark 接收來自 ipcmsg 的數(shù)據(jù)包。這些數(shù)據(jù)包中包含了 mips 目標(biāo)板上進程間通信的信息,。利用我們?yōu)?ipcmsg 專門開發(fā)的 wireshark 插件,,在 wireshark 上,我們可以詳細(xì)的分解 ipcmsg 轉(zhuǎn)發(fā)來的數(shù)據(jù)包,,非常直觀的分析進程間通信的過程和可能存在的問題,。下面是 wireshark 分解 ipcmsg 數(shù)據(jù)包的實際運行圖:   

 

    從圖中可以看到,我們從 ipcmsg 獲得了進程間通信的方式,,參數(shù)( path 是 AF_UNIX域 socket 地址參數(shù)),,方向和內(nèi)容,以及進程名稱,。這些信息幫助我們對嵌入式系統(tǒng)的運行狀態(tài)進行分析,。而這一切非常直觀和便于操作。

此內(nèi)容為AET網(wǎng)站原創(chuàng),,未經(jīng)授權(quán)禁止轉(zhuǎn)載,。