Search Google

Tuesday, July 29, 2008

Another way of debugging

最近在寫windows service的時候依舊採用老舊的debug方式 --> 把msg寫到文字檔中,然後再開啟文字檔檢查。如果service持續將msg寫入文字檔中,我們就必須要將文字編輯器關閉然後重新開啟文字檔,十分麻 煩。我今天很幸運地發現其實可以用DebugView1取代文字檔的功能即時檢視除錯訊息!
使用方法則是用OutputDebugString2函式取代原本使用的fopen,fpritnf函式。



1 http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
2 http://msdn.microsoft.com/en-us/library/aa363362(VS.85).aspx

Monday, July 28, 2008

Create Native Windows Service In C -- supplementary

我在上個星期五花了一點篇幅紀錄目前研究windows service的進度1,今天下午測試後發現有些功能完全沒有作用,經過google的幫忙後在CodeProject上找到一篇不錯的tutorial2,文中剛好講到我之前疏忽的重點:

我們利用RegisterServiceCtrlHandlerEx函式註冊service control handler時所輸入的第二個參數正影響著handler的prototype。

若是將handler cast成為(LPHANDLER_FUNCTION_EX)則handler的prototype將會是:
handler(DWORD dwOpcode,DWORD evtype, PVOID evdata, PVOID Context);

若是將handler cast成為(LPHANDLER_FUNCTION)則handler的prototype就會變成:
handler(DWORD dwOpcode;


至於要將handler cast成上述的哪一種,端看這支程式/service的功用為何。
如果只是想要執行簡單的控制然後在start, stop, pause, continue service的時候改變控制行為,那麼將handler cast成
(LPHANDLER_FUNCTION)即可,若是想要控制usb(舉例)勢必需要更多的資訊才能知道usb的狀態,因此就必須將handler cast成(LPHANDLER_FUNCTION_EX)。

有心人如果翻翻我上星期五貼出來的範例就會發現我在註冊service control handler的時候只有將handler cast成(LPHANDLER_FUNCTION)卻肖想handler的prototype為:
void ControlHandler(DWORD request, DWORD evtype, PVOID evdata, PVOID Context);
這麼一來,有功能沒有作用是再正常不過的事了!

以上仍為嘴泡理論,等我有時間測試的時候再來確認一下,如果到時候發現嘴泡有誤再上來訂正 ^^



1 Create Native Windows Service In C
2 http://www.codeproject.com/KB/system/Windows_Services.aspx

Sunday, July 27, 2008

auto mount usb drive failed

累積兩個星期的ubuntu update終於在昨天一口氣更新完畢,只不過更新完後卻發現原本用得好好的外接usb硬碟現在卻沒有辦法在接上usb port時自動掛載到系統中,自動掛載的過程會出現下列的錯誤訊息:

org.freedesktop.hal.storage.mount-removable no <-- (action, result).
原本一度以為可能是硬碟壞掉,由於不甘心眼睜睜看著硬碟中的資料1就這麼付諸流水,因此特地將上面提及的錯誤訊息丟到google中好好搜尋一番,沒有多久便在某網頁2找到類似的解決方法:
sudo gnome-mount -vbd /dev/sdb1
從dmesg中得知我的外接usb硬碟的代號為/dev/sdb,而原本主要的資料都存在第一個劃分區塊因此決定mount /dev/sdb1。



1 hm... you know exactly what i am saying :-)
2 http://ubuntuforums.org/showthread.php?t=857374

Friday, July 25, 2008

Create Native Windows Service In C

最近忙著開車因此在tracing Linux kernel的進度上嚴重落後,除了開車之外也忙著寫程式1。這次寫的程式是native windows service,強調native是因為新版visual studio所寫出來的service都需要.net平台才能執行2
一開始google的時候發現一篇十分有用的教學文章3,該篇文章交我們如何用C寫windows service(坊間的教學大多使用VB, C#或是C++),以下是參考教學文件實做出來的prototype:





1 沒學弟補進來的悲哀
2 昨天測試的結果,若是有其他微調的方法請告知
3 http://www.devx.com/cplus/Article/9857/0/page/1

Monday, July 14, 2008

Mount drive in dosbox

1. 在terminal下執行dosbox指令
2. 在dosbox的console下執行mount指令,例如:

mount c /home/user/tmp
mount d /media/cdrom
PS. dosbox的mount指令只能接受完整路徑,因此在Linux環境下用~是行不通的。

[心得] 說話的技巧

不論是在收看莒光園地(應該可以說是全國收視率最好的節目)或是在聽長官說話,最近常常聽到"大家都知道。。。所以。。。","我們都知道。。。所以。。。"或類似的說詞,既然大家都已經知道了還拿出來講幹嘛,直接切入主題把該講的重點講一講不就好了,這麼說只不過是在浪費彼此的時間而已。
有些人講話習慣會在句尾加上個"喔"(或諸如此類的壯聲詞),例如"可見國軍是多麼的精實",或是習慣重複剛剛說的話,也就是同樣的話講兩次,乍聽之下好像很體貼聽眾,特別重複讓大家都能聽到、聽懂。仔細想想,這些動作不過是用來掩護發言者根本不知道下一句該講什麼,也某種程度的暴露了發言者的頭腦其實不是很靈光,說不是很聰明也不為過!
所以下次在發言之前,例如於莒光課後進行第X次發言或於榮團會(榮譽團結會)進行第Y次發言之前先想好自己要講些什麼東西,稍稍不注意就很有可能犯了以上的錯誤。

Sunday, July 13, 2008

[Scheduler] More Detail In Scheduler

專門用語:

  • 使用打字機(Courier New)字體表示原始碼。
  • task/process即為Linux中的thread。
  • 文中的Linux與Linux kernel指的皆為作業系統核心。
  • 使用斜體字型標示Linux中的資料結構,macro,變數或函式名稱。
  • PID即為TID。

Kernel版本:
  • v2.6.24.3

我們在上一篇1提到2.6.24.3以後版本的scheduler永遠將cfs_rq rb-tree2中最左邊的node挑出來成為current task,而kernel是依據每個task的vruntime大小決定該task在cfs_rq rb-tree中的位置。最後發現不論是task的vruntime或是單次執行時間長度都與task的load欄位3有密切的關係,因此決定再花一篇的篇幅解釋load究竟是何方神聖。

之前所提到的load其實是個內含integer type的weightinv_weight的structure,一般狀況下weightinv_weight分別來自於prio_to_weightprio_to_wmult兩個integer array,也可以稱這兩個array為對照表(look-up table),若是遇到task為real-time task或是idle task,kernel便直接將weightinv_weight設定為某固定值,實際上替task取得load參數的函式為set_load_weight

static void set_load_weight(struct task_struct *p)
{
if (task_has_rt_policy(p)) {
p->se.load.weight = prio_to_weight[0] * 2;
p->se.load.inv_weight = prio_to_wmult[0] >> 1;
return;
}

/*
* SCHED_IDLE tasks get minimal weight:
*/
if (p->policy == SCHED_IDLE) {
p->se.load.weight = WEIGHT_IDLEPRIO;
p->se.load.inv_weight = WMULT_IDLEPRIO;
return;
}

p->se.load.weight = prio_to_weight[p->static_prio - MAX_RT_PRIO];
p->se.load.inv_weight = prio_to_wmult[p->static_prio - MAX_RT_PRIO];
}

當task為一般task時,它的load便取決於它的static priority,顧名思義static priority通常在建立task的時候就設定好,位於100與140之間4,一般預設為1205。在已知MAX_RT_PRIO被設定為100的情況下,得知task的static priority之後便可以利用對照表取得weightinv_weightweightinv_weight相乘後約為2^326prio_to_weight array中的數字則是新版scheduler中少見的magic number。

static const int prio_to_weight[40] = {
/* -20 */ 88761, 71755, 56483, 46273, 36291,
/* -15 */ 29154, 23254, 18705, 14949, 11916,
/* -10 */ 9548, 7620, 6100, 4904, 3906,
/* -5 */ 3121, 2501, 1991, 1586, 1277,
/* 0 */ 1024, 820, 655, 526, 423,
/* 5 */ 335, 272, 215, 172, 137,
/* 10 */ 110, 87, 70, 56, 45,
/* 15 */ 36, 29, 23, 18, 15,
};


static const u32 prio_to_wmult[40] = {
/* -20 */ 48388, 59856, 76040, 92818, 118348,
/* -15 */ 147320, 184698, 229616, 287308, 360437,
/* -10 */ 449829, 563644, 704093, 875809, 1099582,
/* -5 */ 1376151, 1717300, 2157191, 2708050, 3363326,
/* 0 */ 4194304, 5237765, 6557202, 8165337, 10153587,
/* 5 */ 12820798, 15790321, 19976592, 24970740, 31350126,
/* 10 */ 39045157, 49367440, 61356676, 76695844, 95443717,
/* 15 */ 119304647, 148102320, 186737708, 238609294, 286331153,
};

看完load的由來之後我們知道kernel schedules tasks與決定每個task單次執行時間長度的主要依據為task的static priority與目前在runqueue的task數(cfs_rq->nr_running)。當runqueue中task數小於或等於57時,task的priority與單次執行時間長度僅受task的static priority影響,當runqueue中的task數大於5時,則runqueue中的task數也成為影響因素之一。

static u64 __sched_period(unsigned long nr_running)
{
u64 period = sysctl_sched_latency;

unsigned long nr_latency = sched_nr_latency;

if (unlikely(nr_running > nr_latency)) {
period *= nr_running;
do_div(period, nr_latency);
}

return period;
}




1 http://memyselfandtaco.blogspot.com/2008/07/scheduler-behind-scene.html
2 red-black tree(紅黑樹)。
3 load其實是在task_structsched_entity裡的欄位。
4 數字越小優先權越高,0~99為real-time task的priority範圍。
5 nice的範圍為-20到20的典故便由此而來。
6 整數運算自動刪除小數位。
7 又是一個magic number。

Wednesday, July 09, 2008

[Scheduler] Behind The Scene

專門用語:

  • 使用打字機(Courier New)字體表示原始碼。
  • task/process即為Linux中的thread。
  • 文中的Linux與Linux kernel指的皆為作業系統核心。
  • 使用斜體字型標示Linux中的資料結構,macro,變數或函式名稱。
  • PID即為TID。

Kernel版本:
  • v2.6.24.3

本篇要討論的是scheduler背後幫助我們更容易了解scheduler如何運作的一些細節,主要以解釋下列兩個問題的方式進行:
  1. Kernel以什麼為依據決定將task放runqueue1中哪個位置?
  2. 在沒有preemption發生的時候kernel怎麼知道什麼時候該讓current task釋出CPU給其他task使用?

Kernel以什麼為依據決定將task放runqueue中哪個位置?
版本2.6.22之後的kernel在runqueue中以新增的cfs_rq紅黑樹結構存放schedule entity(SE),也就是紀錄與每個task執行相關的時間數據。Kernel在runqueue新增資料的時候以SE中的vruntime為比較依據,與樹中各SE的vruntime相比,帶有小vruntime的SE往左邊擺,反之帶有大vruntime的se則往右邊擺,直到新的SE本身成為最左邊或最右邊的node為止。SE中的vruntime的型態為64-bit integer代表的意義是在第vruntime個tick2之前該SE代表的task要取得CPU執行工作,是kernel用來確保schedule公平性的新做法。

當新task被產生出來以後task_new_fair函式先透過place_entity函式取得新task的vruntime

place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial)
{
u64 vruntime;

vruntime = cfs_rq->min_vruntime;

if (sched_feat(TREE_AVG)) {
struct sched_entity *last = __pick_last_entity(cfs_rq);
if (last) {
vruntime += last->vruntime;
vruntime >>= 1;
}
} else if (sched_feat(APPROX_AVG) && cfs_rq->nr_running)
vruntime += sched_vslice(cfs_rq)/2;

/*
* The 'current' period is already promised to the current tasks,
* however the extra weight of the new task will slow them down a
* little, place the new task so that it fits in the slot that
* stays open at the end.
*/
if (initial && sched_feat(START_DEBIT))
vruntime += sched_vslice_add(cfs_rq, se);

if (!initial) {
/* sleeps upto a single latency don't count. */
if (sched_feat(NEW_FAIR_SLEEPERS))
vruntime -= sysctl_sched_latency;

/* ensure we never gain time by being placed backwards. */
vruntime = max_vruntime(se->vruntime, vruntime);
}

se->vruntime = vruntime;
}

cfs_rq->min_vruntime其實就是整個紅黑樹中最左邊schedule entity的vruntime,在kernel呼叫__update_curr函式時更新,而整個紅黑樹中每個schedule entity的vruntime會隨著時間而增加。

取得新task的vruntime之後task_new_fair函式再間接透過enqueue_task_fair函式呼叫__enqueue_entity函式將新task的schedule entity放入cfs_rqvruntime正是在__enqueue_entity函式中被拿來當作決定新task該放在cfs_rq紅黑樹中的哪個位置。

static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
struct rb_node **link = &cfs_rq->tasks_timeline.rb_node;
struct rb_node *parent = NULL;
struct sched_entity *entry;
s64 key = entity_key(cfs_rq, se);
int leftmost = 1;

/*
* Find the right place in the rbtree:
*/
while (*link) {
parent = *link;
entry = rb_entry(parent, struct sched_entity, run_node);
/*
* We dont care about collisions. Nodes with
* the same key stay together.
*/
if (key < entity_key(cfs_rq, entry)) {
link = &parent->rb_left;
} else {
link = &parent->rb_right;
leftmost = 0;
}
}

/*
* Maintain a cache of leftmost tree entries (it is frequently
* used):
*/
if (leftmost)
cfs_rq->rb_leftmost = &se->run_node;

rb_link_node(&se->run_node, parent, link);
rb_insert_color(&se->run_node, &cfs_rq->tasks_timeline);
}

在已知kernel_init為kernel中第一支thread也是當runqueue中沒有其他user threads時所跑的thread的情況下,我們可以合理地推測kernel_initvruntime應該相當大。

在沒有preemption發生的時候kernel怎麼知道什麼時候該讓current task釋出CPU給其他task使用?
timer interrupt會輾轉透過update_process_times函式呼叫scheduler_tick函式,在2.6.22以前的版本中 scheduler_tick函式會遞減current task的time slice直到time_slice為零或是達到單次允許執行的長度(利用TIMESLICE_GRANULARITY macro取得)時便將current task的flag加上表示該讓scheduler重新挑選task讓CPU執行的數值(TIF_NEED_RESCHED)。但是在2.6.22之後版本的scheduler_tick函數中卻沒有這些機制,這讓我合理的懷疑新版的kernel完全摒棄time slice機制,新版scheduler_tick函式的原始碼如下。

新版的scheduler_tick函式輾轉透過task_tick_fair函式(scheduler_tick函式的第18行,row 3493)與entity_tick函式呼叫check_preempt_tick函式判斷current task是否該將CPU讓出以執行其他task。在版本2.6.22之後的kernel中的time slice成為如vruntime一般的抽象的概念,單純透過比較數字大小決定是否要將TIF_NEED_RESCHED加入task flag,因此kernel也不再花力氣在task_struct中maintain time slice。

sched_slice函式會根據目前runqueue中task的數量決定每個task的最佳執行時間長度4與current task使用CPU時間3相比較,當current task使用CPU時間已超過它的最佳執行時間長度kernel便會將current task標示為need reschedule。current task的sum_exec_runtime則是在每次scheduler_tick函式被timer interrupt觸發的時候更新。

看過code之後發現與2.6.22以前的原始碼比較起來新版的scheduler用了比較少magic number,至於效能上改進多少我也就無法得知,留給有緣人去研究。

剛才不論是在討論task的vruntime還是單次執行時間長度,計算過程中都使用到schedule entity中的load欄位,至於這個load究竟是什麼東西又是從何而來,我們就留給下篇去討論吧。



1 正確來說應該是runqueue中的cfs_rq,雖然說將task放入runqueue其實是將代表task的SE放入runqueue的cfs_rq欄位。
2 tick用於紀錄timer interrupt signals的次數,是Linux中最小的時間單位,決定於define值HZ,此版本中的預設值為250。
3 從最後一次被scheduler挑選出來開始使用CPU的時間開始計算。
4 sched_slice函式中也用到了一些magic numbers。

Access exploit 20080707

圖一:自作聰明的IE

圖二:行為正常的Firefox

Security Update @ 20080709
為了避免誤會,決定將剛才使用iframe開啟的文字檔拿掉,exploit code請參照圖二。
讓我不禁想問:明明都已經指名副檔名為.txt為什麼IE要這麼自作聰明的解析txt檔?

原文
當有人開啟位於192.168.0.106內含上述原始碼的網頁時MS Access便會將存放在192.168.0.106 web server下的notepad.exe另存為本機端的c:\test11.exe,若是將目錄設定為開機會自動執行的路徑則重新開機後剛剛開啟網頁的電腦便會開始執行test11.exe。這個exploit到目前為止還算是0-day exploit,在MS釋出patch之前請大家好自為之。另外,安裝程式的時候沒有用的功能千萬不要亂勾,例如我已經很久沒有裝MS Access了。

結論,雖然大家都知道,但是我還是要說:ActiveX還真是不安全阿!
原文結束

PS. 忽然發現syntaxhighlighter有支援html語法,試試看好了。



Tuesday, July 08, 2008

[Scheduler] Before Schedule Takes Place

專門用語:

  • 使用打字機(Courier New)字體表示原始碼。
  • task/process即為Linux中的thread。
  • 文中的Linux與Linux kernel指的皆為作業系統核心。
  • 使用斜體字型標示Linux中的資料結構,macro,變數或函式名稱。
  • PID即為TID。

Kernel版本:
  • v2.6.24.3

假設我們遇到的是一般task,在schedule暫停current task並讓原本暫停中的task成為current task之前會呼叫put_prev_task_fair函式將current task1讓出CPU的使用權,如果current task已執行完畢就會被從runqueue移除,否則重新放回runqueue等待下次輪到它使用CPU繼續執行。

static void put_prev_task_fair(struct rq *rq, struct task_struct *prev)
{
struct sched_entity *se = &prev->se;
struct cfs_rq *cfs_rq;

for_each_sched_entity(se) {
cfs_rq = cfs_rq_of(se);
put_prev_entity(cfs_rq, se);
}
}

這裡出現新的struct,一個名為sched_entity,這個struct跟sched_class一樣屬於task_struct的一個欄位,主要用來紀錄可以被schedule的task在執行時間方面的相關資訊與維持紅黑樹架構的rb_node;另一個名為cfs_rq2cfs_rq是runqueue中用來處理fairness的一個欄位。put_prev_entity函式主要是將原本指到current task的指標(cfs_rq->curr)設為NULL,在此之前如果發現current task還沒有執行完畢3的話便將代表current task的schedule entity重新放回runqueue等待下次被scheduler挑選出來執行。

當current task讓出CPU使用權之後kernel的工作便是從runqueue中挑選出一個task讓它成為下一個current task,這個動作透過pick_next_task函式呼叫pick_next_task_fair函式完成。pick_next_task_fair函式呼叫__pick_next_entity函式從cfs_rq中將最左邊的schedule entity挑選為下一個current task。

static struct sched_entity *pick_next_entity(struct cfs_rq *cfs_rq)
{
struct sched_entity *se = NULL;

if (first_fair(cfs_rq)) {
se = __pick_next_entity(cfs_rq);
set_next_entity(cfs_rq, se);
}

return se;
}

確認新選出的current task與原本的current task並不是同一個task之後便呼叫context_switch函式備份原task的狀態並還原新task的狀態,待備份還原狀態完成之後便直接開始執行新的current task。

看到這裡大家應該都會有一些共同的疑問,例如,kernel是以什麼原則為依據將task放入cfs_rq的樹狀結構中,以及在current task沒有遭到preempt的狀況下kernel怎麼知道該呼叫schedule函式讓其他task也有機會執行。我會在下一篇文章中試著以我理解的程度解釋這些疑點。



1 current task指的是CPU正在執行的task。
2 cfs為complete fair scheduler的縮寫。
3 schedule entity中on_rq欄位的值為1。

Saturday, July 05, 2008

Share folder with virtualbox -- just a short note

If you are running Linux as guest OS, use mount.vboxsf instead of mount -t vboxsf when you encounter the following error message:

mounting failed with the error: Protocol error
The complete command should look like this (ignore the brackets when you type in):
sudo mount.vboxsf [share folder's name] [your mount point]

Wednesday, July 02, 2008

[Scheduler] The Overview

專門用語:

  • 使用打字機(Courier New)字體表示原始碼。
  • task/process即為Linux中的thread。
  • 文中的Linux與Linux kernel指的皆為作業系統核心。
  • 使用斜體字型標示Linux中的資料結構,macro,變數或函式名稱。
  • PID即為TID。

Kernel版本:
  • v2.6.24.3

Linux schedules tasks的過程中會依據task_struct中紀錄1的schedule scheme決定如何操作2runqueue,每個task所屬的schedule scheme不是fair_sched_class就是rt_sched_class。Fair schedule class用於schedule所有一般的tasks,rt schedule class用於schedule所有real-time tasks3。我們先看看fair_sched_classrt_sched_class兩個struct的內容:

static const struct sched_class fair_sched_class = {
.next = &idle_sched_class,
.enqueue_task = enqueue_task_fair,
.dequeue_task = dequeue_task_fair,
.yield_task = yield_task_fair,

.check_preempt_curr = check_preempt_wakeup,

.pick_next_task = pick_next_task_fair,
.put_prev_task = put_prev_task_fair,

#ifdef CONFIG_SMP
.load_balance = load_balance_fair,
.move_one_task = move_one_task_fair,
#endif

.set_curr_task = set_curr_task_fair,
.task_tick = task_tick_fair,
.task_new = task_new_fair,
};


const struct sched_class rt_sched_class = {
.next = &fair_sched_class,
.enqueue_task = enqueue_task_rt,
.dequeue_task = dequeue_task_rt,
.yield_task = yield_task_rt,

.check_preempt_curr = check_preempt_curr_rt,

.pick_next_task = pick_next_task_rt,
.put_prev_task = put_prev_task_rt,

#ifdef CONFIG_SMP
.load_balance = load_balance_rt,
.move_one_task = move_one_task_rt,
#endif

.set_curr_task = set_curr_task_rt,
.task_tick = task_tick_rt,
};

再來看看取自schedule函式的範例:

prev->sched_class->put_prev_task(rq, prev);

若prev為一般task,則實際呼叫的函式為put_prev_task_fair,否則呼叫put_prev_task_rt函式。
改版後的schedule函式將schedule scheme切成兩個部分,一般task與real-time task,一般task使用紅黑樹的方式排序,real-time task則延續使用2.6.22版本以前的O(1)方式排序,code與之前的版本相比也更為精簡,由2.6.22的158行減少為2.6.24的63 行。新版schedule函式的重點落在下面幾行code上:

prev->sched_class->put_prev_task(rq, prev);
next = pick_next_task(rq, prev);

sched_info_switch(prev, next);

if (likely(prev != next)) {
rq->nr_switches++;
rq->curr = next;
++*switch_count;

context_switch(rq, prev, next); /* unlocks the rq */
} else
spin_unlock_irq(&rq->lock);




1 紀錄於task_structsched_class欄位中。
2 在runqueue中新增移除task,或者改變task在runqueue中的位置(優先順序)。
3 PC環境中很難做到真的real-time,因此這裡的real-time task指的是擁有高執行優先權要儘早執行完畢的task。