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

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

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

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

Linux UK Communication: Netlink

上一篇文章所介紹使用的 Linux User/Kernel 溝通方式:Character Device,是一個很標準的範例程式,過去在公司的案子開發上也大多採用這種方法(另一種方法是 ioctl )。這個方式最大的好處是簡單,而且移植性高,因為從很早以前的 Linux Kernel 就已經支援了(2.4,你問 2.2?抱歉,我沒那麼老 ... )缺點呢?Kernel Module 只能夠被動的等待 User Space 的程式呼叫,無法主動進行溝通。

Linux Kernel 最近發展的方向,在 UK 這一塊盡可能改採 Netlink 的方式來進行通訊。這是一個很簡單的思維:既然所有網路的封包都會到 Kernel 去進行處理,那我為什麼不直接把網路封包拿來當作給 Kernel 的訊息呢?而 Kernel 要和 User Space 的程式溝通時,只要製作一個網路封包往 socket 丟不就好了嗎?於是 Netlink 就誕生了。先說缺點,因為算是比較新的技術,所以 API 的介面改的很快,因此程式似乎也要常常變動。下面是由同一位同事 taco 所撰寫的範例。(其實我本來想自己寫的,但 ... 懶啊)

Kernel:
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/init.h>

MODULE_AUTHOR("tacolin");
MODULE_DESCRIPTION("NETLINK KERNEL TEST MODULE");
MODULE_LICENSE("GPL");

#define NETLINK_TEST 17
#define MAX_PAYLOAD  512

static struct sock* g_pSocket = NULL;

static void recv_msg_from_user(struct sk_buff *pSkb, char* pRecvBuf, int *pPid)
{
    struct nlmsghdr *pNlhdr = (struct nlmsghdr*)(pSkb->data);

    memcpy(pRecvBuf, nlmsg_data(pNlhdr), strlen( (char*)nlmsg_data(pNlhdr) ) );

    *pPid = pNlhdr->nlmsg_pid;
}

static void send_msg_to_user(struct sock* pSocket, int pid, char* pSendBuf)
{
    int msgSize = 0;
    int sendResult = -1;
    struct sk_buff *pSkb;
    struct nlmsghdr *pNlhdr;

    msgSize = strlen(pSendBuf);
    pSkb = nlmsg_new( msgSize, 0 );
    pNlhdr = nlmsg_put(pSkb, 0, 0, NLMSG_DONE, msgSize, 0);

    sprintf( nlmsg_data(pNlhdr), pSendBuf, msgSize );
    NETLINK_CB(pSkb).dst_group = 0;

    sendResult =  nlmsg_unicast(pSocket, pSkb, pid);
    if ( sendResult < 0 )
    {
        printk("[KERNEL-PART] unicast a message to user failed\n");
    }
}

static void process_user_msg(struct sk_buff *pSkb) 
{
    int pid = -1;
    char pMsgBuf[MAX_PAYLOAD];

    memset(pMsgBuf, 0, MAX_PAYLOAD);
    recv_msg_from_user(pSkb, pMsgBuf, &pid);

    printk("[KERNEL-PART] received pid=%d's message : %s\n", pid, pMsgBuf);

    memset(pMsgBuf, 0, MAX_PAYLOAD);
    sprintf(pMsgBuf, "Hello from Kernel");
    send_msg_to_user(g_pSocket, pid, pMsgBuf);
}

static int __init kernel_module_init(void)
{
    g_pSocket = netlink_kernel_create(&init_net, NETLINK_TEST, 0, process_user_msg, NULL, THIS_MODULE);

    printk("[KERNEL-PART] Module Inserted\n");

    return 0;
}

static void __exit kernel_module_exit(void)
{
    netlink_kernel_release(g_pSocket);

    printk("[KERNEL-PART] Module removed\n");

    return;
}

module_init( kernel_module_init );
module_exit( kernel_module_exit );


User:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/socket.h>

#define NETLINK_TEST 17
#define MAX_PAYLOAD  512

int main(int argc, char* argv[])
{
    int socketFd;
    socketFd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);


    struct sockaddr_nl srcAddr;
    memset( &srcAddr, 0, sizeof(srcAddr) );
    srcAddr.nl_family = AF_NETLINK;
    srcAddr.nl_pid    = getpid();  
    srcAddr.nl_groups = 0;
    bind( socketFd, (struct sockaddr*)&srcAddr, sizeof(srcAddr) );


    struct sockaddr_nl dstAddr;
    memset( &dstAddr, 0, sizeof(dstAddr) );
    dstAddr.nl_family = AF_NETLINK;
    dstAddr.nl_pid    = 0;
    dstAddr.nl_groups = 0;


    struct nlmsghdr *pNlhdr;
    pNlhdr = (struct nlmsghdr*)malloc( NLMSG_SPACE(MAX_PAYLOAD) );
    memset( pNlhdr, 0, NLMSG_SPACE(MAX_PAYLOAD) );
    pNlhdr->nlmsg_len   = NLMSG_SPACE(MAX_PAYLOAD);
    pNlhdr->nlmsg_pid   = getpid();
    pNlhdr->nlmsg_flags = 0;    
    sprintf( NLMSG_DATA(pNlhdr), "Hello from User" );


    struct iovec iov;
    memset( &iov, 0, sizeof(struct iovec) );
    iov.iov_base = (void*)pNlhdr;
    iov.iov_len  = pNlhdr->nlmsg_len;


    struct msghdr msg;
    memset( &msg, 0, sizeof(struct msghdr) );
    msg.msg_name    = (void*)&dstAddr;
    msg.msg_namelen = sizeof(dstAddr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;    


    sendmsg( socketFd, &msg, 0 );


    memset( pNlhdr, 0, NLMSG_SPACE(MAX_PAYLOAD) );
    recvmsg( socketFd, &msg, 0 );
    printf("[USER-PART] Receive message from kernel : %s\n", (char*)NLMSG_DATA(pNlhdr) );


    close(socketFd);

    return 0;
}


從程式碼可以看出來,User Space的程式和一般的 Socket 程式沒啥兩樣,除了要包一些特殊的 Header 而已。而 Kernel Space 的程式就是 skb 的處理。

留言

這個網誌中的熱門文章

如何將Linux打造成OpenFlow Switch:Openvswitch

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

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