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

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

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

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

Linux Process 1 ~ Tasks Lists

Process,在一般作業系統的教科書給它的定義是「an instance of a program 」。在 Linux 裡面,對 Process 的 Descriptor 是定義在 include/linux/sched.h 裡面的 task_struct。要注意的是, Linux 裡面是採用 lightweight process 的實作概念,換言之,每個 thread 都有一個 task descriptor,而且每個 task 的 pid 都是獨一的。可是,在 POSIX Thread 的標準裡面規定,一個 process 裡面所有 threads 的 pid 都必須相同,所以在 task_struct 裡面多導入了一個變數,叫做 tgid( task group ID ),在同一個 process 裡面不同的 threads 都有不同的 pid,但他們的 tgid 是相同的(這個事實也告訴我們,當我們在應用程式裡面呼叫 getpid() 的時候,其實回傳值是 tgid 而不是 pid 喔)。

接下來要慢慢研究這個 structure。這個 structure 有點複雜,我們先集中在 Task List 的部分。首先我們來看下面的 Macro~

#define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks)
#define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; )

顧名思義,這個 Macro 會將所有的 Process 掃過一遍。每一個存在的 Porcess 都是透過 structure 內部的 tasks( struct list_head )所彼此串起來的。可是有一件很神奇的是,當我們執行 multi-thread 的程式時,我們發現無法透過這個 Macro 來找出除了主線程以外的其他 thread 資訊。對作業系統來說,一個 Process 開再多的 threads 也還是一個 Process,所以在那隻 Macro 裡面是找不到的。那那些被創造出來的 thread descriptor 到底到哪去了呢?答案是他們被串到了 Master thread 的 sibling 裡面。那如果是 fork 的話呢?因為 fork 是 create 一個新的 Process,所以也會被加入到 tasks 的 list 裡面,而且他也會被放入 Parent Process 的 Children list 中。關於 task_struct 彼此的關係圖,請看下面:



在這裡有一件事要特別注意,假設有二個 task A 和 task B,他們是父子關係,那 B 會串在 A 的 children list 裡面,但如果 A 又產生一個 child process,那 C 會被串到 B 的 sibling list 中,而 C 自己的 sibling 則會串回 A 的 children,這一點是上圖很值得注意的地方。

下面附上一隻小程式,可以用來找出所有的 tasks,就是根據上面所描述的 list 關係圖來加以完成的。這個程式會在 /proc 底下開一個新的 node,叫做 my_pid,將所要查詢的 pid 餵給他,可以找到該 task 的相關資訊,詳細的程式碼如下:

#include linux/init.h
#include linux/kernel.h
#include linux/module.h
#include linux/sched.h
#include linux/mm.h
#include asm/uaccess.h
#include linux/proc_fs.h

#define MODULE_NAME "my_pid"
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif

#define for_each_children(pos,head) \
list_for_each_entry(pos, &(head -> children), sibling)
#define for_each_sibling(pos,head) \
for_each_children(pos, (head->parent) )

//Begin of /proc/xxx

static struct proc_dir_entry *pid_dir_entry = NULL;

static void print_task( struct task_struct *task )
{
struct task_struct *task2;

printk("%s(pid:%d)(tgid:%d)\n", task->comm, task->pid, task->tgid);
printk(" Parent:%s(pid:%d)(tgid:%d)\n", task->parent->comm, task->parent->pid, task->parent->tgid);
printk(" Sibling:\n");

for_each_sibling( task2 , task )
{
if( task2 != task )
{
printk(" %s(pid:%d)(tgid:%d)\n", task2->comm, task2->pid, task2->tgid );
}
}

printk(" Children:\n");
for_each_children( task2 , task )
{
printk(" %s(pid:%d)(tgid:%d)\n", task2->comm, task2->pid, task2->tgid);
}

return;
}

static struct task_struct * check_task_and_children( struct task_struct *task, int pid )
{
struct task_struct *task2 = NULL;
struct task_struct *taskRet = NULL;

if ( task -> pid == pid )
{
return task;
}

for_each_children( task2 , task )
{
taskRet = check_task_and_children( task2, pid );

if( taskRet != NULL )
{
return taskRet;
}
}

return NULL;
}

static int proc_pid_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
*eof = 1;
return 0;
}

static int proc_pid_write(struct file *file, const char *buffer, unsigned long count, void *data)
{
struct task_struct *init;
struct task_struct *task2 = NULL;
int pid;
char buf[16];
int len = 0;

if (count > 16)
{
len = 16;
}
else
{
len = count;
}

// get data from user space via write operation
if ( copy_from_user(buf, buffer, len) )
{
printk("[%s] proc write fail to copy\n", MODULE_NAME);
return -EFAULT;
}
buf[len] = 0;


pid = simple_strtol(buf, NULL, 10);
printk("\n[%s] got the process id to look up as %d\n", MODULE_NAME, pid);

init = &init_task;
printk("\ninit_task: %s(pid:%d)(tgid:%d)\n\n", init->comm, init->pid, init->tgid);

task2 = check_task_and_children( init, pid );

if( task2 != NULL )
{
print_task( task2 );
return len;
}

printk("\n[%s] Task (pid:%d) not found\n", MODULE_NAME, pid);

return len;
}

static int __init my_pid_load(void)
{
pid_dir_entry = create_proc_entry(MODULE_NAME, 0666, NULL);
if (pid_dir_entry == NULL)
{
printk("[%s] create proc entry fail\n", MODULE_NAME);
return -ENOMEM;
}

// set proc_dir_entry struct fields
pid_dir_entry->read_proc = &proc_pid_read;
pid_dir_entry->write_proc = &proc_pid_write;

printk("[%s] is loaded and initialized\n", MODULE_NAME);
printk("\n");
return 0;
}

static void __exit my_pid_unload(void)
{
remove_proc_entry(MODULE_NAME, NULL);
printk("[%s] is being removed now\n", MODULE_NAME);
printk("\n");
}

module_init(my_pid_load);
module_exit(my_pid_unload);

  1. 要找到所有的 sibling,我們會先回到 parent 再來找所有的 children,這樣可以避免一些無謂的判斷。
  2. 在 Linux 裡面有定義一個 init_task,請注意,它的 pid 是 0,名稱叫做 swapper(聽說叫做歷史的包袱)
  3. 雖然我們可以透過這種 DFS 的方式來找到所有的 tasks,但相信我,Kernel 內部絕對不是利用這個方式來找尋特定 pid 的 task!!事實上,Kernel 內部是使用 Hash Table 的機制來快速找到指定 pid 的 tasks。這是之後要來看的範圍,先休息囉~

留言

張貼留言

這個網誌中的熱門文章

如何將Linux打造成OpenFlow Switch:Openvswitch

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

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