靈修分享:亞略巴古的演講

亞略巴古的演說是使徒行傳一篇非常著名的講道。基督徒往往用這段經文來說明上帝的屬性,同時也會參考這段經文作為和外邦人互動的一個參考方式。最近因為團契查經查到這一段,重新思想後看到了以前沒有考慮過的面向,在這邊記錄一下。

徒17:16-34保羅在雅典等候他們的時候,看見滿城都是偶像,就心裏着急;於是在會堂裏與猶太人和虔敬的人,並每日在市上所遇見的人,辯論。還有伊壁鳩魯和斯多亞兩門的學士,與他爭論。有的說:「這胡言亂語的要說甚麼?」有的說:「他似乎是傳說外邦鬼神的。」這話是因保羅傳講耶穌與復活的道。他們就把他帶到亞略‧巴古,說:「你所講的這新道,我們也可以知道嗎?因為你有些奇怪的事傳到我們耳中,我們願意知道這些事是甚麼意思。」(雅典人和住在那裏的客人都不顧別的事,只將新聞說說聽聽。)
保羅站在亞略‧巴古當中,說:「眾位雅典人哪,我看你們凡事很敬畏鬼神。我遊行的時候,觀看你們所敬拜的,遇見一座壇,上面寫着『未識之神』。你們所不認識而敬拜的,我現在告訴你們。創造宇宙和其中萬物的神,既是天地的主,就不住人手所造的殿,也不用人手服事,好像缺少甚麼;自己倒將生命、氣息、萬物,賜給萬人。他從一本本:有古卷是血脈造出萬族的人,住在全地上,並且預先定準他們的年限和所住的疆界,要叫他們尋求神,或者可以揣摩而得,其實他離我們各人不遠;我們生活、動作、存留,都在乎他。就如你們作詩的,有人說:『我們也是他所生的。』我們既是神所生的,就不當以為神的神性像人用手藝、心思所雕刻的金、銀、石。世人蒙昧無知的時候,神並不監察,如今卻吩咐各處的人都要悔改。因為他已經定了日子,要藉着他所設立的人按公義審判天下,並且叫他從死裏復活,給萬人作可信的憑據。」
眾人聽見從死裏復活的話,就有譏誚他的;又有人說:「我們再聽你講這個吧!」於是保羅從他們當中出去了。 但有幾個人貼近他,信了主,其中有亞略‧巴古的官丟尼修,並一個婦人,名叫大馬哩,還有別人一同信從。

對基督徒來說,保羅這篇講道講的真好,不但講出了上帝的超越性(像是不住人手所造的殿、也不是金銀石的彫刻),也帶出了耶穌基督復活的大能。但最近我才注意到眾人的反應:「眾人聽見從死裏復活的話,就有譏誚他的;又有人說:「我們再聽你講這個吧!」」不曉得你看到這一段有什麼感想?可能我是玻璃心吧,如果我是保羅,我大概會非常非常的難過,看起來似乎沒有人理會這個福音、這篇講道。這篇被基督…

IPC: Unix Domain Socket

要讓系統中的兩個 process 彼此溝通該如何處理?過去常見的守法有 pipeline、shared memory 等,不過其實我一個都沒有用過。我比較喜歡的作法是 socket programming,在兩個 process 間建立 network socket,因為這樣的概念最簡單(我一向很懶)。

問題是,使用 network socket 會造成效率上的浪費,儘管兩個 process 都在同一台機器(localhost)上,但是還是會到 kernel 的 TCP/IP Stack 裏面逛個一圈,這在執行上勢必會有效能的浪費。既然都已經在本機了,那又何必經過網路層的處理呢?今天要介紹的 IPC 技術就是一個解決這問題的方案: Unix Domain Socket。從下面的範例上來看,寫法和 network socket 幾乎一模一樣。所以就不多做介紹了,直接看程式吧。

#include <stdio.h>          // for printf() and fprintf() 
#include <sys/socket.h>     // for socket(), bind(), and connect() 
#include <sys/un.h>   // for struct sockaddr_un
#include <stdlib.h>         // for atoi() and exit() 
#include <string.h>         // for memset() 
#include <unistd.h>         // for close() 
#include <sys/time.h>       // for struct timeval {} 
#include <fcntl.h>          // for fcntl() 

#include <sys/epoll.h>

int main( int argc, char *argv[] )
{
    struct sockaddr_un   unixSocketAddr;   // Unix Domain Socket
    int      unfd;     // Unix Socket FD
    struct sockaddr_un      unixSocketDstAddr;      // Unix Domain Socket
    struct sockaddr_un      unixSocketSrcAddr;      // Unix Domain Socket
    socklen_t     unixSocketAddrLen;  // Socket Length
    
    int                     epfd;                   // EPOLL File Descriptor. 
    struct epoll_event      ev;                     // Used for EPOLL.
    struct epoll_event      events[120];            // Used for EPOLL.
    int                     noEvents;               // EPOLL event number.
    
    int      i, j, k, len, running = 1;
    const int    cBufferSize = 1024;
    char     msg[cBufferSize];
    
    if ( argc < 3 )     
    {
        fprintf( stderr, "Usage:  %s   ...\n", argv[0]);
        exit(1);
    }
    
    // Create epoll file descriptor.
    // Standard Input + Bind Socket
    epfd = epoll_create( 2 );
    
    // Create Unix domain socket + Bind + add to the EPOLL set
    
    memset( &unixSocketAddr, 0, sizeof( unixSocketAddr ) );
    unixSocketAddr.sun_family = AF_UNIX;
    strcpy( unixSocketAddr.sun_path, argv[1] );
    
    if( ( unfd = socket( AF_UNIX, SOCK_DGRAM, 0 ) ) < 0 ) 
    {
        perror("socket error.");
        exit(1);
    }
    
    unlink( argv[1] );
    if ( bind( unfd, (struct sockaddr *)&unixSocketAddr, sizeof( unixSocketAddr ) ) == -1 ) 
    {
        perror("bind error.");
        exit(1);
    }
    
    ev.data.fd = unfd;
    ev.events = EPOLLIN | EPOLLET;
    epoll_ctl( epfd, EPOLL_CTL_ADD, unfd, &ev ); 
    
    // Create destination Unix domain socket
    
    memset( &unixSocketDstAddr, 0, sizeof( unixSocketDstAddr ) );
    unixSocketDstAddr.sun_family = AF_UNIX;
    strcpy( unixSocketDstAddr.sun_path, argv[2] );
    
    // Add STDIN into the EPOLL set.
    ev.data.fd = STDIN_FILENO;
    ev.events = EPOLLIN | EPOLLET;
    epoll_ctl( epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev ); 
    
    printf( "Starting server:  \n" );
    
    while ( running )
    {
        printf( "Input (q to quit): " );
        fflush( stdout ); 
        
        noEvents = epoll_wait( epfd, events, FD_SETSIZE , -1 );
        
        printf( "Get %d events.\n ", noEvents );
        
        for( i = 0 ; i < noEvents; i++ )
        {
            if( events[i].events & EPOLLIN && STDIN_FILENO == events[i].data.fd )
            {
                memset( msg, cBufferSize, 0 );
                fgets( msg, cBufferSize, stdin );
                
                if( strlen( msg ) == 2 && msg[0] == 'q' && msg[1] == '\n' )
                {
                    running = 0;
                    continue;
                }  
                
                len = sendto( unfd, msg, strlen( msg ), 0, ( struct sockaddr * ) &unixSocketDstAddr, sizeof( unixSocketDstAddr ) );
                
                if( len < 0 )
                {
                    perror( "Send Error!!\n" );
                }
            }
            else if( events[i].events & EPOLLIN && unfd == events[i].data.fd )
            {
                memset( msg, cBufferSize, 0 );
                len = recvfrom( unfd, msg, cBufferSize, 0, ( struct sockaddr * ) &unixSocketSrcAddr, &unixSocketAddrLen );
                
                if( len < 0 )
                {
                    perror( "Recv Error!!\n" );
                }
                
                for( j = 0 ; j < len ; j++ )
                {
                    printf( "%02X ", msg[j] );
                    
                    if( j % 16 == 15 )
                    {
                        printf( "\n" );
                    } 
                }
                
                printf( "\n" );
            }
            else
            {
                printf( "Unknown %d!\n", events[i].data.fd );
            }
        }
    }
    
    close( unfd );
    
    return 0;
}
上面的程式應該很好懂,基本上建立 socket 的流程和過去 network socket 的流程一模一樣,除了使用 AF_UNIX 以及 address 上給的是檔案名稱這兩件事情。在程式範例裏面,寫法類似 UDP,但其實還可以使用 connect 以及 send 來處理,因為概念類似,就不另外提供範例了。

留言

這個網誌中的熱門文章

如何將Linux打造成OpenFlow Switch:Openvswitch

我弟家的新居感恩禮拜分享:善頌善禱

如何利用 Wireshark 來監聽 IEEE 802.11 的管理封包