Tag

2015年5月13日 星期三

Rust Cargo

佈署是現代程式設計遇到的一個問題,雖然網路的出現讓大家可以快速的流通成品,同時也帶來各種版本混亂。
這個問題在C/C++ 上不嚴重,主因C/C++的跟底層黏著度高,演化速度也慢,都是透過作業系統的套件更新。相對的我們可以看到無論python 的pip、Ruby的RubyGems、Golang 支援從github 取得project、NodeJS的npm,都是要建立一個統一的套件佈署管道,方便設計師開發。

今天要提的,就是Rust 的解決方案: Cargo,用來管理rust project,當然如果不用cargo,就算像之前的嵌入式系統一樣,直接寫一個rust檔案並用Makefile + rustc 編譯也是沒有問題的。

安裝好cargo使用cargo new即可生成一個新的rust project資料;跟先前提到的一樣,Cargo new 時即會生成一個git repository和它的.gitignore,方便套件版本控制。
project的資料會使用Cargo.toml這個檔案來管理,toml 是一款極簡的設定格式,相關文件格式可見:
https://github.com/toml-lang/toml

Cargo.toml 裡面會定義這個package 的資料:
[package]
name = "package name"
version = "0.1.0"
authors = ["author <author@xxxxx.com>"]

Cargo 有一系列可用的指令,用cargo --help 就可以看到
build Compile the current project
clean Remove the target directory
doc Build this project's and its dependencies' documentation
new Create a new cargo project
run Build and execute src/main.rs
test Run the tests
bench Run the benchmarks
update Update dependencies listed in Cargo.lock

一般最常用的組合,大概就是new, build, run三個基本指令,用來初始、編譯、執行,預設會用src/main.rs當作預設的編譯目標,並建構在target資料夾內,下面是其他的功能:

相依套件:

如果要用到其他的套件,把相依的套件名字填入Cargo.toml裡面:
[dependencies]
package “package version”

[dependencies.package]
git = “url”

[dependencies.package]
path = “path”

並在原始碼用extern指定它即可:
//main.rs
extern package
use package::{};

Cargo build 的時候會自動去檢查相依性套件,從它的git repository裡面簽出最新的master版本放到家目錄的.cargo 中,並用它進行建構;簽出的版本會寫進Cargo.lock,如果把Cargo.lock 傳給別人,他們就只之後就能用這個版本建構,如果要更新Cargo.lock 的話,就要用cargo update 來更新相依的套件,或用cargo update -p package指定更新某套件。

使用本地套件:

如果需要修相依套件裡面的bug,cargo 可以指定用本地的套件,而非簽出在.cargo 裡面的套件,只要在project 往上到根目錄的任一個地方,產生一個.cargo 的目錄,並在裡面建立config 檔,標示local project 的Cargo.toml 所在:
paths = ['path to local project']

測試:

cargo test 會執行在src 跟tests 裡面的測試,也就是有#[test] attribute 的,算是一個不錯小工具。

如果拿servo 當例子:
https://github.com/servo/servo/blob/master/components/servo/Cargo.toml
一開始先定義package:
[package]
name = "servo"
version = "0.0.1"
authors = ["The Servo Project Developers"]

servo要編出的library
[lib]
name = "servo"
path = "lib.rs"
crate-type = ["rlib"]

下面有一大排dependency,都是servo project 內的子專案,所以都是用相對路徑的方式來定義,而這些子專案的Cargo.toml內又會定義相依套件,例如外部相依大部分定義在util 裡面,這就會用git 的方式來引用:
[dependencies.azure]
git = "https://github.com/servo/rust-azure"

有一次有寫到一個issue 就是要更動相依的rust-mozjs的套件,再更新servo 內Cargo.lock 檔,相關的更動可見:
https://github.com/servo/mozjs/pull/29
https://github.com/servo/servo/pull/4998

參考資料:

相關的cargo內容可見:
http://doc.crates.io/guide.html

可取用的rust 的套件庫以在這裡找到:
https://crates.io/

2015年5月10日 星期日

二十一世紀資本論

這本書是先前作者皮凱提來台演講時,隨演講票一起買的,花了一段時間把它讀完,在這裡寫一下有關它的心得,其實先前朱敬一已經有寫過這本書的導論了,推薦大家看一看。
http://www.storm.mg/article/23089

第一部分很大的篇幅,剖析法國、英國、美國、德國等歐洲國家的歷史資料,分析各國的資本歷史,解釋各種資本的消長;並說明有史以來,低度成長、低通膨與不會流動的社會才是常態,極小部分人可能因為獨門技術或是透過原有資本投資,向上晉升到富有階級,但對大多數人來說是極不可能的事,靠著繼承而來的資本可以勝過其他人一輩子工作的所得,書中也引用不少19世紀的小說,來做為作者想法的佐證。而後隨之而來的兩次大戰大抵將原有的資本階級消滅迨盡,戰後科學技術高度成長帶來勞務所得的高成場,作者也詳加解釋其中各種要素間的消長。

第二部分進入分配不均的討論,解釋作者最主要的想法:就是長期來看資本所得會超過勞務所得,導致持有資本的人財富累積的速度快於無資本的人,舉例而言,擁有兩棟房子的人(資本持有者),可以選擇將一棟房子出租賺取報酬。
這些話口說無憑,作者最大的貢獻,就是援引歐洲-特別是法國-的歷史資料:人口普查、報稅資料來佐證他的想法,即便富豪的所得資料缺乏統計,作者仍會試圖引用富士比雜誌的統計,全球財富報告的統計去估值,並一一說明不同的統計的缺陷。
至於為何分析的多半是法國等歐陸國家,純粹是因為歐陸國家的稅收和人口財產統計開始得比較早,近來作者開始和其他國家的學者合作,試圖將它的收入統計拓展到其他國家。

這部分說明他用的統計模型,給予討論基準,並說明資本的分配不均與勞務的分配不均,檢視高所得族群投資報酬率差距;並比較個人才能和財富繼承的財富流量,警示現今富有國家的財富繼承已經接近十九世紀的水準,必須要有手段來解決資本趨異的問題。

最後一個部分,作者試圖提出他對於資本不斷趨異的解法:針對資本的累進稅率。

資本會生出資本這件事,不是什麼經濟制度的缺陷,正好相反,透過租金、股利、地產增值來賺錢,是資本完全自由流通的結果,資本制度完全無法解決這樣的問題。

面對所得重分配的要求,必須要有政府機關的介入,而和強制性的通膨、長期的通貨緊縮、利用移民進行資本重分配比起來,累進稅率會是比較符合公平正義、能夠精確針對資本階級,又能將重分配的過程置於民主討論和監督的解法。
這樣的政策需要國際間的通力合作,才能確實的打擊資本利得的成長,就我看來,作者有些一廂情願,期待國際間突然攜手合作,難度大概近於登天,更何況資本階級還能運用對政治的影響力來反對這類政策的推行,只要國際間有了些許缺口就會使整體成效不彰,但或許,當國際間受夠了通膨造成的熱錢流竄與不斷拉開的貧富差距,公共債務的不斷膨脹,會真的坐下來好好檢視資本累進稅的可能性?

個人是覺得這本書詳細歸詳細,知識密度卻略嫌不足,或許是求謹慎,作者詳實的介紹每筆資料的來由,每一個模型的假設,在開頭的歷史資料中,美國黑奴是否應該算在「資本」當中,他用了相當篇幅解釋奴隸被歸為「資本」或「勞力」時,會出現什麼不同的結果,但這幾個百分點的差距對讀者來說沒什麼意義。
但撇開理論細節和背後的數學不提,這本書仍然值得一看,可以把他的前半部當成經濟史來看,可以看到一些有趣的觀點;從最後一部分的資本利得稅的建言,也能學習到各種政策工具的影響和優劣,中央銀行在政治上的定位,現今金融監理制度的缺陷;反正遇到太多詳細的解釋,跳過去就是。

這本書只是在財富重分配上開了一個頭,相關的議題還有待更多的討論,對裡頭的論點也不必照單全收;資本與勞務間的論戰不會就此塵埃落定,甚至根本不可能塵埃落定,本書終將是一場長期論戰中的第一棒,但正如作者所說:「社會科學研究者就像所有的知識分子和公民一樣,應該參與公共論辯……必須做出抉擇,並針對特定制度與政策表明立場,無論是關於社會國家、賦稅制度或公共債務的問題」

為政治提供建言,是學者的責任,因為社會科學說到底都是政治,我想不止經濟學如此,任何學科都是;從這裡,我看得到一位身為知識份子,在他的專業上樹立的典範。

Javascript Worker

最近在寫一些前端的東西,深覺前.端.超.難.(yay)

接觸了HTM5 javascript 的worker ,在這裡筆記一下:

簡單來說,一般javascript的指令,都是存在一個執行緒裡執行,而worker 則可以開另一個執行緒在背景執行,就不會block住前端的UI的script,一般。
使用方法也很簡單,宣告一個worker並傳入一個檔案路徑,這個worker 就會負責執行檔案內的javascript。
要和worker 溝通,一律要經過postmessage跟onmessage 介面,postmessage會代入一個參數,可以用json寫得超級複雜,包括File, Blob, ArrayBuffer都可以傳遞;這時在對方的onmessage就會被呼叫,並包含這個物件的複製版本。
我看到的設計是這樣,在主要文件內 main.js 中,宣告worker
var worker = new Worker(“workerFile”);

在workerFile 內要定義onmessage 函式,並且用參數的command判斷執行哪個對應的函式:
this.onmessage = function(e) {
switch(e.data.command) {
case 'act1':
fun_act1(e.data.payload);
break;
}

這時候main.js就可以利用postmessage 叫worker 作事:
worker.postMessage({
command: 'act1',
payload: blahblahblah
})
worker.onmessage = function(e) {
data = e.data;
}

同樣我們在worker 裡面也可以呼叫postmessage,對方接口就是main.js 裡的onmessage,這樣就能做到雙方互相傳遞資料。
如果不想要以複製的方式傳遞資料,可以改用transferable object 的方式,減少複製成本,另外因為是多執行緒的關係,在worker 裡面,不應該去動到DOM,window, document, parent,這些都不是thread-safe。

至於更多詳細的內容,可見:
http://www.html5rocks.com/en/tutorials/workers/basics/

或者可以看worker 的spec:
https://html.spec.whatwg.org/multipage/workers.html

2015年5月6日 星期三

使用 rust 來寫極簡的嵌入式系統

最近看到一些有趣的東西:
https://github.com/JinShil/rust_arm_cortex-m_semihosted_hello_world
https://github.com/neykov/armboot

用rust 來寫嵌入式系統,感覺相當生猛,正好最近在上傳說中的jserv 大神的嵌入式系統,就想把嵌入式系統作業用到東西,用rust 實作出來,主要參考的內容包括上面的armboot,跟作業的mini-arm-os:
https://github.com/embedded2015/mini-arm-os
這裡不細講mini-arm-os的內容,自行參考當時寫的潦草的筆記:
https://yodaleeembedded2015.hackpad.com/Lab42-Note-YKuTRvCMYYx

本篇相關的原始碼請見:
https://github.com/yodalee/rust-mini-arm-os

跟armboot 類似,我用了libarm/stm32f4xx.rs(變體)跟zero/std_types這兩個lib,不使用rust std的lib:
mod zero {
  pub mod std_types;
  pub mod zero;
}
#[macro_use]
mod libarm {
  #[macro_use] pub mod stm32f1xx;
}

zero比較簡單,定義一些C 裡面用到的型態應該對應到rust 什麼型態,和rust 基本的trait 如Sized, Copy 等等,不寫的話rustc 會抱怨找不到這些trait;就像在rust_hello_world這個實作裡面,也是把這些trait 寫在main裡面,我猜這些內容和Rust 的基本設計有關,目前還不是很清楚。
libarm就比較複雜,這感覺像是作者自己寫的,針對的是stm32f4, arm-cortex m4 的硬體。理論上這裡應該是先研究stm32f4要怎麼初始化,不過我偷懶,先直接把部分的stm32f4 硬體位址修改成stm32f1 的,這樣舊的code 就可以直接搬過來XD。

Stm32f4xx.rs的內容,簡單來說就是大量的直接定義,例如裡面的RCCType,直接對應RCC register的位址,每個位址會是32 bits 的空間,可以和stm32的文件內容一一對應,下面是我針對STM32f1修改後的RCC內容:
http://www.st.com/web/en/resource/technical/document/reference_manual/CD00171190.pdf
pub struct RCCType {
  pub CR: uint32_t,
  pub CFGR: uint32_t,
  pub CIR: uint32_t,
  pub APB2RSTR: uint32_t,
  pub APB1RSTR: uint32_t,
  pub AHB1ENR: uint32_t,
  pub APB2ENR: uint32_t,
  pub APB1ENR: uint32_t,
  pub BDCR: uint32_t,
  pub CSR: uint32_t,
}

在最後面,它用函式宣告傳回對應的Type struct:
#[inline(always)]
pub fn RCC() -> &'static mut RCCType {
  unsafe {
    &mut *(RCC_BASE!() as *mut RCCType)
  }
}

而RCC_BASE!() 這個Macro,又會照上一篇提到的Macro_rule展開為:AHB1PERIPH_BASE!() + 0x3000u32
一路展開,最後得到一個32 bits integer,再轉型成RCCType 的mutable pointer,我做的修改就是把RCC, USART2, GPIO的位址換成STM32f1的。
在main 裡面就可以使用let rcc = RCC() 的方式,取得RCC的pointer,並用像C一樣的操作手法來操作對應的register位址。
例如要修改APB1ENR,啟動週邊的clock:
let rcc = RCC();
rcc.APB1ENR |= 0x00020000u32;

或是對usart2 的位址取值都沒問題:
let usart2 = USART2();
while(true) {
  while(usart2.SR & 0x0080u16 == 0) {}
  usart2.DR = 'x' as uint16_t;
}

這裡我們只輸出'x',這是因為要把text 印出來,我們需要對str 作iterate,而這個東西是定義在rust 的core lib 裡面,一般安裝後在/usr/lib/rustlib裡面只會有x86_64_unknown_linux_gnu,如果要跑arm要先自己編譯arm的lib,相關的內容可見:
http://spin.atomicobject.com/2015/02/20/rust-language-c-embedded/
這步比較麻煩先跳過,之後研究出來再另文介紹。

另一個要解決的問題則是isr_vector,這裡可以看到一種很謎樣的寫法,用link_section這個attribute,定義區段名為 .isr_vector,並設定為一個array,內含一個extern “c” fn(),如果要需要其他的ISR,則可以在後面寫更多的function,並把1改為需要的數量。
#[link_section=".isr_vector"]
pub static ISRVECTORS: [unsafe extern "C" fn(); 1] = [
  main,
];

linker script裡面,先保留一個LONG 的寬度指向初始化stack pointer,接著放isr_vector的reset handler,再放其他的.text,這樣裝置一上電就會執行main裡的內容。
.text :
{
  LONG(_stackStart); /* Initial stack pointer */
  KEEP(*(.isr_vector)) /* ISR vector entry point to main */
  *(.text)
  *(.text*)
} > FLASH

如果我們把最終執行檔反組譯,會看到其中的位址配置,0x0指向stack start,0x4 reset_handler指向位在0x08 的main。:
Disassembly of section .text:
00000000 <_ZN10ISRVECTORS20h538ad2a8e3805addk6aE-0x4>:
0: 10010000 .word 0x10010000

00000004 <_ZN10ISRVECTORS20h538ad2a8e3805addk6aE>:
4: 00000009 ....

00000008 <main>:

執行結果,會印出滿坑滿谷的 'x',我加一個條件讓它只print 100個: 

在編譯時,要向rustc 指定target,這裡有一大篇文章講相關的內容,之後分另一篇文出來介紹:
https://doc.rust-lang.org/rustc_back/target/
同樣這份rust code 裡面有用到大量的rust attribute,也就是function 前的#[attribute],這也可以另外寫一篇文……
啊感覺挖了自己一堆坑,要趕快填坑了OAO。

結語:

在這篇文我試著解釋如何用rust 撰寫嵌入式系統程式,結論當然是做得到的,但整體比C寫的原始碼複雜得多,也不像C這麼直覺,編輯libarm 也是非常麻煩的工作。
由於嵌入式系統的code 通常都不會複雜到哪裡去(唔…大概吧),發揮不出Rust的優勢,我認為比起來還是寫C會有效率得多。
Related Posts Plugin for WordPress, Blogger...