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

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

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

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

當 send 被 Block 的時候

今天在工作上遇到一個問題。有兩個 process ( process A 和 process B ) 透過 unix domain socket 彼此在傳遞資料,結果發現 process A 在送資料到 process B  的時候,程式居然卡在 send 這支系統函式。send 是會卡住的函式嗎?

答案是:!來看看「男人」怎麼說:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

When the message does not fit into the send  buffer  of  the  socket,  send()  normally blocks, unless the socket has been placed in nonblocking I/O mode.  In nonblocking mode it would fail with the error EAGAIN or EWOULDBLOCK in this case.   The  select(2)  call may be used to determine when it is possible to send more data.

意思是當呼叫 send 的時候,send 會先將資料放到 kernel 的 buffer,如果 buffer 已經放不進去了,而且 send 又處於 BLOCK 的模式時,這時候 send 就會卡住。而為什麼 buffer 會滿呢?因為 process B 處理封包的時間過久,進而造成 kernel buffer 被 process A 源源不絕的封包給塞滿了,然後 process A 的 send 就被 block 了。解決這個問題的方法可以將 socket 設成 non-blocking mode,或是在傳送封包的時候把 flag 設成 MSG_DONTWAIT。而這也就是為什麼 epoll, select 這類的函式需要去偵測 fd 是否已經是可寫狀態的原因。不過更根本要處理的問題是,為什麼 process B 會處理一個封包處理這麼久啊 ...

放上測試用的小程式:

unix domain socket receiver:
sleep(10) 用來模擬處理時間過久。

#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 = 10000;
    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" );
                }
                
                printf( "Receive Msg Len: %d\n", len );
                
                sleep( 10 );
                
                printf( "\n" );
            }
            else
            {
                printf( "Unknown %d!\n", events[i].data.fd );
            }
        }
    }
    
    close( unfd );
    
    return 0;
}

unix domain socket sender:
在呼叫 send時有兩種模式,透過註解來切換。

#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 <unistd.h>

#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 = 10000;
    char     msg[cBufferSize];
    int     counter = 0;
    int     result = 0;
    
    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" );
    
    memset( msg, 1, 10000 );
    
    while ( running )
    {
        printf( "Counter: %d\n", counter );
        // result = sendto( unfd, msg, 7816, 0, ( struct sockaddr * ) &unixSocketDstAddr, sizeof( unixSocketDstAddr ) );
        result = sendto( unfd, msg, 7816, MSG_DONTWAIT, ( struct sockaddr * ) &unixSocketDstAddr, sizeof( unixSocketDstAddr ) );
        
        if ( result == -1 )
        {
            printf( "Counter: %d fail\n", counter );
        }
        else
        {
            printf( "Counter: %d done\n", counter );
        }

        counter++;
        usleep( 10 );
    }
    
    close( unfd );
    
    return 0;
}

留言

這個網誌中的熱門文章

如何將Linux打造成OpenFlow Switch:Openvswitch

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

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