The Art of Computer Programming, Volume 1 1.4.2 coroutines 這節就是在討論 coroutine。
test env:
debian/x86 32 bit
我對 coroutine 很有興趣, 想把這東西搞懂。練習這個作業時花了不少時間, 每次解開一個 bug, 後面的 bug 又接踵而來。
作業 (B)
- 移植 coroutine 到 STM32 平台 (p103)
- 必須修改 SAVE_STACK_POINTER_ASM 與 RESTORE_STACK_ASM 這兩個 macro
- 系統最少要有兩個 routine,一者傾聽 UART 端來自使用者的輸入,另一者將輸入透過 ROT13 編碼演算法印列在 UART 上
- 注意:ROT13 應以 inline assembly 實做
- 倘若使用者按下 button 時,系統結束運作,不再畫面作輸出,同時也該清掉任何 buffer,直到使用者再次按下 button 時,才如同之前描述般繼續運作
- 可參考 demo 的 Makefile,其已存在 ‘emu’ 的 target,可接受來自 stdio 的輸入,自動控制 QEMU 的執行。於是應該在 coroutine/Makefile 加入 ‘emu’ target
- make emu
- emulate.sh
.. code-block:: prettyprint
emulate () {
$QEMU_STM32 \
-M stm32-p103 \
-kernel $1 \
-serial stdio \
-parallel none \
-monitor tcp:localhost:4444,server,nowait <&0 & pid=$!
}
- telnet localhost 4444
- 提示:可修改 emulate.sh 與 test.script
這篇並不是在介紹 coroutine, 我還沒有搞懂, 只是紀錄我做這個作業遇到的問題。
難關一:
在使用 register variable 時, 用我自己實作的 setjmp/longjmp 會有錯誤 (segment fault), 但是標準程式庫的 setjmp/longjmp 不會有錯。
這個 register unsigned long savedstack; 可害死我, 花了兩個晚上才找出來為什麼會讓我的 setjmp/longjmp 當掉, 在我的系統中 savedstack 會用 ebx, 結果我的 setjmp/longjmp 沒有保留好 ebx, 就發生了 ebx 的值被改掉, 也就是 savedstack 這變數被在執行 setjmp 之後被改了。
https://github.com/descent/progs/blob/master/coroutine/my_setjmp.S 我已經做了一點修正。
cm3 的 setjmp/longjmp 沒遇到這樣的問題。
難關二:
movl (%esp), %eax 改成 movl 4(%esp), %eax
因為 %esp 保存的函式返回值變成在 4(%esp), 因為我多了 push %ebx 的動作。
這個 bug 造成我用 setjmp 存下來的位址無法讓 longjmp 順利跳回。
難關三:
還有一個問題, 為何 Routine 1 count 是 1, 2 , 4 的值, 而不是 1, 2, 3 ?
[Routine 1] pass 1
[Routine 2] pass 4
[Routine 1] pass 2
[Routine 2] pass 2
[Routine 1] pass 4
[Routine 2] pass 0
要追這問題可讓我大傷腦筋, 使用 gdb single step 到 longjmp 會有問題, 直接設定 b __longjmp 在這裡觀察 __longjmp 的動作即可。
將
void routine1()
void routine2()
的 count 宣告改成 static 就符合預期了。
//volatile unsigned count = 0;
static unsigned count = 0;
執行結果:
[Routine 1] pass 1
[Routine 2] pass 4
[Routine 1] pass 2
[Routine 2] pass 3
[Routine 1] pass 3
[Routine 2] pass 2
[Routine 1] pass 4
[Routine 2] pass 1
[Routine 1] pass 5
[Routine 2] pass 0
不過原因是什麼呢? 似乎是:
L19 ~ 21, L37 ~ 39 的 macro 有錯, 改成 L74 ~ 83, L88 ~ 92 就正常了。
這兩個 macro 沒有正確的將 stack 轉移到 malloc 出來的記憶體區塊, 造成兩個 count 變數其實是同一個, 會互相影響。
執行結果:
[Routine 1] pass 1
[Routine 2] pass 4
[Routine 1] pass 2
[Routine 2] pass 3
[Routine 1] pass 3
[Routine 2] pass 2
[Routine 1] pass 4
[Routine 2] pass 1
[Routine 1] pass 5
[Routine 2] pass 0
呼 ... 結束了嗎? 還沒, 我要 port 到 stm32f4Discovery 版子上, 這就簡單不少了, 將之前的努力集合起來, 把 usart 的 code 貼過來, 做些小修改, 從 minicom 就可以得到和 x86 上相同的結果。
source code:
https://github.com/descent/stm32f4_prog/tree/master/coroutine
程式碼沒有用到標準程式庫的東西, 所有的 c 檔案就是所有的程式碼了。
這樣結束了嗎? not yet, 作業題目可不是這樣 ..., rot13 我用了 c 語言而不是組合語言, 先饒了我吧!
ref:
- Coroutines in C
- Coroutines in C 簡體中文翻譯: C 协程
- 一个“蝇量级” C 语言协程库 下面的回文提供了更多資料:
- boost coroutine
- Contiki:Protothread切换机制理解
- Protothread机制文档 (contiki-2.6\doc\pt-doc.txt)
- http://en.wikipedia.org/wiki/Duff%27s_device
- Thinking Asynchronously in C++
- Protothreads
- [程式] Coroutine: 入門篇
- sunneo (艾斯寇德) [分享] Coroutine (有其自己實作的 coroutine by c)
- 使用 Coroutine 改寫狀態機
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。