2018年11月25日 星期日

Note for LTE Frequency bands and channel bandwidths


  • Band 33 - Band 52為TDD,其餘為FDD。如果要簡單了解TDD/FDD的運作,可以看這段簡介Duplex in LTE Fundamentals of 4G LTE,在FDD的Downlink與Uplink中間會有Guard band,它將兩個頻率分開。這確保了同時使用的通信信道不會受到干擾,這將導致兩種傳輸的質量降低。TDD與FDD在Duplex in LTE Fundamentals of 4G LTE有簡單易懂的解釋。

  • LTE中定義了幾種不同的bandwidth,分別為1.4MHz、3MHz、5MHz、10MHz、15MHz與20MHz。不同的bandwidth會有不同大小的Guard band(概略約10%,實際算法可以參考LTE guard band calculation)。







2018年11月4日 星期日

AWS IoT Testing with MQTT.FX


AWS IoT基本上就是MQTT,這篇文章簡介如何Create一個AWS IoT device並使用MQTT.FX測試。

Create a IoT Device on AWS




Create a thing




Create a CERT

Enable CERT


Link CERT to instance


Download CERT
[Learn] -> [Connect to AWS IoT] -> [Configuring a device] -> [Get Start] -> [Choose a platform] / [Choose a AWS IoT Device SDK]


Check/Show your URL


Testing with MQTT.FX









2018年10月28日 星期日

Table Of Content for tag "IPv6 RFC"







2018年9月22日 星期六

note for autoconf - CH1 Introduction


Autoconf是一個用於生成shell script的工具(我們稱這個script為“configure”),可自動配置source code以適應Posix系統。“configure”可獨立運行(用戶不需要使用Autoconf)。“configure”在運行時不需要手動用戶干預; 甚至不需要指定係統類型的參數。 而且“configure”會測試source code/package所需的環境。Autoconf的目標是讓每個user都能夠輕鬆的執行“configure”。Autoconf在其目標方面非常成功,多數抱怨都是關於編寫Autoconf,而不是由此產生的“configure”的行為。


    參考資料:
  • https://www.gnu.org/software/autoconf/manual/index.html, Autoconf documentation
  • https://bootlin.com/pub/conferences/2016/elc/petazzoni-autotools-tutorial/petazzoni-autotools-tutorial.pdf, GNU Autotools: a tutorial




2018年9月9日 星期日

Mount UBI with nandsim


UBI/UBIFS無法使用loop-back方式mount, 不過我們可以使用NAND simulator將UBI燒入到nandsim中.
root@vista:~# mkfs.ubifs -m 2KiB -e 129024 -c 2048 -r ubifs-root -x zlib ubifs.img
root@vista:~# cat ubi.ini
[ubi_rfs]
mode=ubi
image=ubifs.img
vol_id=0
vol_type=dynamic
vol_name=ubi_rfs
vol_alignment=1
vol_flags=autoresize

root@vista:~# ubinize -o my.ubi -p 128KiB -m 2KiB -O 512 ubi.ini
ubinize: volume size was not specified in section "ubi_rfs", assume minimum to fit image "ubifs.img"1806336 bytes (1.7 MiB)
root@vista:~# modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=0x15
root@vista:~# ubiformat /dev/mtd0 -f my.ubi
ubiformat: mtd0 (nand), size 268435456 bytes (256.0 MiB), 2048 eraseblocks of 131072 bytes (128.0 KiB), min. I/O size 2048 bytes
libscan: scanning eraseblock 2047 -- 100 % complete
ubiformat: 2048 eraseblocks have valid erase counter, mean value is 3
ubiformat: flashing eraseblock 15 -- 100 % complete
ubiformat: formatting eraseblock 2047 -- 100 % complete
ubiformat: formatting eraseblock 2047 -- 100 % complete
root@vista:~# ubiattach -m 0
UBI device number 0, total 2048 LEBs (264241152 bytes, 252.0 MiB), available 0 LEBs (0 bytes), LEB size 129024 bytes (126.0 KiB)
root@vista:~# ubinfo -a
UBI version:                    1
Count of UBI devices:           1
UBI control device major/minor: 10:55
Present UBI devices:            ubi0

ubi0
Volumes count:                           1
Logical eraseblock size:                 129024 bytes, 126.0 KiB
Total amount of logical eraseblocks:     2048 (264241152 bytes, 252.0 MiB)
Amount of available logical eraseblocks: 0 (0 bytes)
Maximum count of volumes                 128
Count of bad physical eraseblocks:       0
Count of reserved physical eraseblocks:  40
Current maximum erase counter value:     9
Minimum input/output unit size:          2048 bytes
Character device major/minor:            245:0
Present volumes:                         0

Volume ID:   0 (on ubi0)
Type:        dynamic
Alignment:   1
Size:        2002 LEBs (258306048 bytes, 246.3 MiB)
State:       OK
Name:        ubi_rfs
Character device major/minor: 245:1

root@vista:~# mount -t ubifs /dev/ubi0_0 www

Usage: mkfs.ubifs [OPTIONS] target
Make a UBIFS file system image from an existing directory tree
Options:
-r, -d, --root=DIR       build file system from directory DIR
-m, --min-io-size=SIZE   minimum I/O unit size
-e, --leb-size=SIZE      logical erase block size
-c, --max-leb-cnt=COUNT  maximum logical erase block count
-x, --compr=TYPE         compression type - "lzo", "favor_lzo", "zlib" or
                         "none" (default: "lzo")

# mkfs.ubifs -F -r </path/to/your/rootfs/tree> -m <min io size> -e <LEB size> -c 
        <Erase Blocks count> -o </path/to/output/Image.ubifs>
<min io size> Equals the page-size of the used NAND-Flash
<LEB size> Logical Erase Block size
<Erase Blocks count> maximum logical erase block count
mkfs.ubifs -m 2KiB -e 129024 -c 2048 -r ubifs-root -x zlib ubifs.img這些相關參數是先用ubiformat確認的, 不過-e是在mount failed的時候看dmesg的


Usage: ubinize [-o filename] [-p ] [-m ] [-s ] [-O ] [-e ]
                [-x ] [-Q ] [-v] [-h] [-V] [--output=] [--peb-size=]
                [--min-io-size=] [--sub-page-size=] [--vid-hdr-offset=]
                [--erase-counter=] [--ubi-ver=] [--image-seq=] [--verbose] [--help]
                [--version] ini-file
-p, --peb-size=       size of the physical eraseblock of the flash
                             this UBI image is created for in bytes,
                             kilobytes (KiB), or megabytes (MiB)
                             (mandatory parameter)
-m, --min-io-size=    minimum input/output unit size of the flash
                             in bytes
-O, --vid-hdr-offset=   offset if the VID header from start of the
                             physical eraseblock (default is the next
                             minimum I/O unit or sub-page after the EC
                             header)

# ubinize -vv -o <output image> -m <min io size> -p <PEB size>KiB 
        -s <subpage-size> -O <VID-hdr-offset> <configuration file>
<min io size> Equals the page-size of the used NAND-Flash
<PEB size> Physical Erase Block size (in KiB) - Equals the block size of
        the NAND-Flash
<Erase Blocks count> Count of the available Erase Blocks
<subpage-size> Subpage size in bytes. Default value is the minimum 
        input/output size (page-size)
<VID-hdr-offset> offset if the VID header from start of the PEB 
        (default is the next min I/O unit or sub-page after the EC header)
<configuration file> Path to a configuration file.

ubinize -o my.ubi -p 128KiB -m 2KiB -O 512 ubi.ini這些參數也是在ubiformat修正的, 如-O 512


    參考資料:
  • http://www.linux-mtd.infradead.org/faq/ubifs.html#L_loop_mount, Can UBIFS mount loop-back devices?
  • https://elinux.org/UBIFS, UBIFS
  • http://www.linux-mtd.infradead.org/faq/nand.html#L_nand_nandsim, How do I use NAND simulator?




2018年9月2日 星期日

How to clone MDM9207 codeaurora/openembedded


筆記一下How to clone MDM9207 codeaurora/openembedded
brook@vista:~/oe-9x07$ git clone https://gerrit.googlesource.com/git-repo
Cloning into 'git-repo'...
remote: Counting objects: 139, done
remote: Total 4069 (delta 2714), reused 4069 (delta 2714)
Receiving objects: 100% (4069/4069), 1.21 MiB | 0 bytes/s, done.
Resolving deltas: 100% (2714/2714), done.
Checking connectivity... done.
brook@vista:~/oe-9x07$ ln -s git-repo/repo
brook@vista:~/oe-9x07$ ./repo init -u git://codeaurora.org/quic/le/le/manifest.git -b release -m LE.UM.1.0.2-34100-9x07.xml
Get https://gerrit.googlesource.com/git-repo/clone.bundle
Get https://gerrit.googlesource.com/git-repo
Get git://codeaurora.org/quic/le/le/manifest.git
remote: Counting objects: 4009, done.
remote: Compressing objects: 100% (191/191), done.
remote: Total 4009 (delta 84), reused 0 (delta 0)        KiB/s
Receiving objects: 100% (4009/4009), 1.07 MiB | 706.00 KiB/s, done.
Resolving deltas: 100% (2191/2191), done.
From git://codeaurora.org/quic/le/le/manifest
 * [new branch]      IMM.LE.1.0 -> origin/IMM.LE.1.0
 * [new branch]      release    -> origin/release
...
...
Your identity is: Brook Kuo <rene3210 at gmail.com>
If you want to change this, please re-run 'repo init' with --config-name

repo has been initialized in /home/brook/oe-9x07

brook@vista:~/oe-9x07$ ./repo sync
Fetching project platform/vendor/qcom/titanium_32
remote: Counting objects: 5758, done.
remote: Compressing objects: 100% (375/375), done.
remote: Total 5758 (delta 305), reused 380 (delta 177)
Receiving objects: 100% (5758/5758), 1.02 MiB | 868.00 KiB/s, done.
Resolving deltas: 100% (3639/3639), done.
From git://codeaurora.org/platform/vendor/qcom/titanium_32
 * [new branch]      LA.BR.1.3.7_rb1.11 -> caf/LA.BR.1.3.7_rb1.11
...
...
Fetching projects: 100% (58/58), done.
Checking out files: 100% (52804/52804), done. files:  23% (12539/52804)
Checking out files: 100% (622/622), done. out files:  26% (167/622)
Syncing work tree: 100% (58/58), done.



    參考資料
  • https://gerrit.googlesource.com/git-repo/, repo - The Multiple Git Repository Tool
  • https://wiki.codeaurora.org/xwiki/bin/QLBEP/, Open Embedded for MSM




note - Blue Ocean Strategy


藍海策略提供了一個清晰的4步驟流程(買方效益→價格→成本→推行),協助企業擺脫競爭對手,並且建立起一片獲利強勁成長的藍海。
這套4步驟流程是環繞著藍海策略和公平程序(fair process)的概念和分析工具所設計的,透過這套流程,經理人和他們的團隊可以在研擬嚴謹和具體的策略的同時,不至於見樹不見林、掌握大願景。
    參考資料:
  • https://www.managertoday.com.tw/articles/view/51294, 8個要點,一次讀懂藍海策略

2018年9月1日 星期六

note - The Balanced ScoreCard


平衡計分卡不能為企業創造策略,但能幫組織有效執行策略。平衡計分卡以組織的共同願景與戰略為內核,將公司的願景與戰略轉化為下屬各責任部門(如各事業部)在財務(Financial)顧客(Customer)內部流程(Internal Processes)創新與學習(Innovation&Learning)等四個方面的系列具體目標(即成功的因素),並設置相應的四張計分卡,其基本框架見下圖:

優點:
企業向來以財務數字做為績效衡量的標準,但無論是業績目標或營收、投資報酬率等,都是反映過去行動所獲致的成果;簡單說,財務數字衡量的是「過去的績效」,是一種「落後指標」,而非創造未來績效的指引。
平衡計分卡的理念在於:在財務衡量(落後指標)之外,積極找出能夠創造未來財務成果的「績效驅動因素」(performance driver),也就是相較財務成果而言的「領先指標」,例如顧客滿意度、高效率的流程、員工能力、士氣等,讓「績效衡量制度」能與「策略」配合一致。
平衡計分卡不僅是一種管理手段,也體現了一種管理思想,就是:只有量化的指標才是可以考核的,必須將要考核的指標進行量化

缺點:
平衡計分卡中有一些條目是很難解釋清楚或者是衡量出來的。財務指標當然不是問題,而非財務指標往往很難去建立起來。



    參考資料:
  • http://wiki.mbalib.com/zh-tw/%E5%B9%B3%E8%A1%A1%E8%AE%A1%E5%88%86%E5%8D%A1, 平衡計分卡
  • https://www.managertoday.com.tw/articles/view/2489, 5分鐘!了解「平衡計分卡」





2018年8月25日 星期六

Using gdbserver to debug MDM9xxx


openembedded提供了一個良好的gdb環境,設定步驟如下。
brook@vista:/home/brook/apps_proc/poky/build$ tar zxvf tmp-glibc/deploy/images/mdm9607/machine-image-mdm9607-dbg.tar.gz
./
./var/
./var/lib/
./var/lib/opkg/
./var/lib/opkg/info/
...

brook@vista:/home/brook/apps_proc/poky/build$ mv usr/lib/.debug/* usr/lib/
brook@vista:/home/brook/apps_proc/poky/build$ mv lib/.debug/* lib

brook@vista:/home/brook/apps_proc/poky/build$ ln -s libconfig.so.9.2.0 usr/lib/libconfig.so.9
...

brook@vista:/home/brook/apps_proc/poky/build$ ln -s libpthread-2.22.so lib/libpthread.so.0
brook@vista:/home/brook/apps_proc/poky/build$ ln -s librt-2.22.so lib/librt.so.1
brook@vista:/home/brook/apps_proc/poky/build$ ln -s libm-2.22.so lib/libm.so.6
brook@vista:/home/brook/apps_proc/poky/build$ ln -s libc-2.22.so lib/libc.so.6
brook@vista:/home/brook/apps_proc/poky/build$ ln -s ld-2.22.so lib/ld-linux.so.3
brook@vista:/home/brook/apps_proc/poky/build$ ln -s libdl-2.22.so lib/libdl.so.2
...

brook@vista:/home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/build/src$ adb push myprog /usr/bin/myprog

brook@vista:/home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/build/src$ adb shell
sh-3.2# gdbserver :2345 /usr/bin/myprog
Process /usr/bin/myprog created; pid = 9035
Listening on port 2345


接著host端就可以開啟GDB來debug了
brook@vista:/home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/build/src$ arm-oe-linux-gnueabi-gdb myprog 
GNU gdb (GDB) 7.10.1
Copyright (C) 2015 Free Software Foundation, Inc.
...
Reading symbols from myprog...done.
(gdb) set sysroot /home/brook/apps_proc/poky/build/
(gdb) target remote 10.0.0.1:2345
Remote debugging using 10.0.0.1:2345
Reading symbols from /home/brook/apps_proc/poky/build//lib/ld-linux.so.3...done.
0xb6fceac0 in ?? ()
(gdb) b main
Breakpoint 1 at 0x7f5595ac: file /home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/src/main.c, line 207.
(gdb) c
Continuing.

Breakpoint 1, main (argc=1, argv=0xbefffdf4)
    at /home/brook/apps_proc/poky/build/tmp-glibc/work/armv7a-vfp-neon-oe-linux-gnueabi/myprog/git-r0/src/main.c:207
207     {
(gdb) set sysroot /home/brook/apps_proc/poky/build
warning: .dynamic section for "/home/brook/apps_proc/poky/build/lib/libpthread.so.0" is not at the expected address (wrong library or version mismatch?)
warning: .dynamic section for "/home/brook/apps_proc/poky/build/lib/libm.so.6" is not at the expected address (wrong library or version mismatch?)
warning: .dynamic section for "/home/brook/apps_proc/poky/build/lib/libc.so.6" is not at the expected address (wrong library or version mismatch?)
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
Reading symbols from /home/brook/apps_proc/poky/build/usr/lib/libconfig.so.9...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/libpthread.so.0...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/librt.so.1...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/libm.so.6...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/libc.so.6...done.
Reading symbols from /home/brook/apps_proc/poky/build/lib/libdl.so.2...done.
(gdb) info share
From        To          Syms Read   Shared Object Library
0x7473e7c0  0x7475a328  Yes         /home/brook/apps_proc/poky/build/lib/ld-linux.so.3
0xb6fc42d0  0xb6fca6e0  Yes         /home/brook/apps_proc/poky/build/usr/lib/libconfig.so.9
0xb6ebc210  0xb6ecb4e8  Yes         /home/brook/apps_proc/poky/build/lib/libpthread.so.0
0xb6ea2720  0xb6ea6034  Yes         /home/brook/apps_proc/poky/build/lib/librt.so.1
0xb6e28bf0  0xb6e5ac20  Yes         /home/brook/apps_proc/poky/build/lib/libm.so.6
0xb6ced280  0xb6dee638  Yes         /home/brook/apps_proc/poky/build/lib/libc.so.6
0xb6cc3928  0xb6cc4870  Yes         /home/brook/apps_proc/poky/build/lib/libdl.so.2
確認所有symbol都有找到之後, 就可以開始debug之旅了.




2018年8月11日 星期六

note for "The Art of Readable Code" - CH13 Writing Less Code


The most readable code is no code at all.
Programmer最重要的技能之一就是知道哪些code不用寫,因為寫出來就要測試&維護,越小的程式碼越容易被維護,coupling程度越低越好,最好彼此獨立,有幾個方向:
  1. Create as much generic “utility” code as possible to remove duplicated code. (See Chapter 10, Extracting Unrelated Subproblems.)
  2. Remove unused code or useless features. (See the following sidebar.)
  3. Keep your project compartmentalized into disconnected subprojects.
  4. Generally, be conscious of the “weight” of your codebase. Keep it light and nimble.

Be Familiar with the Libraries Around You

每隔一段時間應該要花一下時間,讀一下你的 standard library,目的在於能對standard library的API有概念,以便在coding時能聯想到,並大量且反覆地使用這些library。


    參考資料:
  • The Art of Readable Code



2018年7月29日 星期日

note for "The Art of Readable Code" - CH12 Turning Thoughts into Code


You do not really understand something unless you can explain it to your grandmother. 
— Albert Einstein

先用口語描述演算法後,再轉成程式碼,能讓programmer寫出更自然的code,也有助於找出可以分解的子問題。
 We are reading three row iterators in parallel.
 Whenever the rows' times don't line up, advance the rows so they do line up.
 Then print the aligned rows, and advance the rows again.
 Keep doing this until there are no more matching rows left.

上述口語轉成的code為
def PrintStockTransactions(): 
    stock_iter = ...
    price_iter = ... 
    num_shares_iter = ...

    while True:
        time = AdvanceToMatchingTime(stock_iter, price_iter, num_shares_iter) 
        if time is None:
            return
        # Print the aligned rows.
        print "@", time,
        print stock_iter.ticker_symbol,
        print price_iter.price,
        print num_shares_iter.number_of_shares
        stock_iter.NextRow()
        price_iter.NextRow()
        num_shares_iter.NextRow()


    參考資料:
  • The Art of Readable Code



note for "The Art of Readable Code" - CH11 One Task at a Time


Code should be organized so that it’s doing only one task at a time.舉個投票例子,投UP則+1,Down則-1,結果為所有投票總和,如果依據該rule,則code應該是
var vote_changed = function (vote) {
    var score = get_score();
    score += vote_value(vote);
    set_score(score);
};

基本上這個章節的概念跟前一章節差不多"將子問題抽離,讓function專注在處理問題本身上面"。其餘細節就不贅述了。

    參考資料:
  • The Art of Readable Code



2018年7月28日 星期六

note for "The Art of Readable Code" - CH10 Extracting Unrelated Subproblems


將子問題抽離,讓function專注在處理問題本身上面,首先要先了解這個function的"目的",接著抽離與"目的"無關的子問題,將這些子問題一一寫成獨立的function,如:(簡單說就主程式專注在邏輯上,細節部分就一一寫成sub function處理)
// Return which element of 'array' is closest to the given latitude/longitude.
// Models the Earth as a perfect sphere.
var findClosestLocation = function (lat, lng, array) {
    var closest;
    var closest_dist = Number.MAX_VALUE;
    for (var i = 0; i < array.length; i += 1) {
        // Convert both points to radians.
        var lat_rad = radians(lat);
        var lng_rad = radians(lng);
        var lat2_rad = radians(array[i].latitude);
        var lng2_rad = radians(array[i].longitude);
        // Use the "Spherical Law of Cosines" formula.
        var dist = Math.acos(Math.sin(lat_rad) * Math.sin(lat2_rad) +
                             Math.cos(lat_rad) * Math.cos(lat2_rad) *
                             Math.cos(lng2_rad - lng_rad));
        if (dist < closest_dist) {
             closest = array[i];
             closest_dist = dist;
        }
    }
    return closest;
};

抽離後的code
var spherical_distance = function (lat1, lng1, lat2, lng2) {
    var lat1_rad = radians(lat1);
    var lng1_rad = radians(lng1);
    var lat2_rad = radians(lat2);
    var lng2_rad = radians(lng2);
    // Use the "Spherical Law of Cosines" formula.
    return Math.acos(Math.sin(lat1_rad) * Math.sin(lat2_rad) +
                     Math.cos(lat1_rad) * Math.cos(lat2_rad) *
};

var findClosestLocation = function (lat, lng, array) {
    var closest;
    var closest_dist = Number.MAX_VALUE;
    for (var i = 0; i < array.length; i += 1) {
        var dist = spherical_distance(lat, lng, array[i].latitude, array[i].longitude);
        if (dist < closest_dist) {
            closest = array[i];
            closest_dist = dist;
        }
    }
    return closest;
};
抽離這些子問題的另一個好處容易優化,比如有更好的方式去運算spherical_distance()或是變更findClosestLocation()的運算邏輯。建立通用的function也是一個很好也很重要的習慣,當你開發一個新的program時,就可以運用這些通用function,快速建立一個prototype。

Simplifying an Existing Interface & Reshaping an Interface to Your Needs

如果既有的API不好用,那就包裝他或改寫他吧,我在A Wrap for service/thread也包裝了一些API,讓自己易於開發,有必要也會進行Reshaping/Re-factor等等步驟。

    參考資料:
  • The Art of Readable Code



2018年7月8日 星期日

Table Of Content for tag "Design Patterns with C "






2018年6月17日 星期日

Linux Kernel(15.3)- The Linux usage model for device tree data


這篇基本上就是"Documentation/devicetree/usage-model.txt", The Linux usage model for device tree data的筆記

DT(Device Tree,或稱Open Firmware Device Tree)是一個資料結構(data structure)用於讓OS讀取硬體周邊訊息動態執行,因此OS就不用hard code硬體驅動。

所謂的"bindings"就是一組通用的DTS設定,用來描述其硬體,包含了data busses, interrupt lines, GPIO connections, and peripheral devices等等。

盡可能使用現有的binding來描述硬體,以最大限度地利用現有的代碼,但由於property和node名稱只是字串,因此通過定義新property和node可以輕鬆擴展現有binding。 不要沒做功課就自己建立新的binding,i2c busses就因為沒有先確認是否已經有人定義了相關的binding,就建立了新的binding,導致現在有兩套不相容的binding發生。

DT所做的只是提供一種language將硬體設定從device driver分離開來,如此可以透過傳入不同的DT給OS,以適應不同的硬體設定裝置. 進而減少一些重複的code。

DT在Linux底下有三個主要目的
1) platform identification,
2) runtime configuration, and
3) device population.


platform identification

首先kernel會先使用DT來辨識特定的機器,並且執行相關的初始化,比如ARM會在setup_arch()呼叫setup_machine_fdt()尋找適合的DT(比對DT root底下的"compatible" property。
    compatible = "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";
上面的例子定義了"ti,omap3-beagleboard-xm",也宣稱相容"OMAP 3450 SOC"與"OMAP3"系列的SoC,你會注意到這樣的宣稱會從最具體的board到SoC的家族,關於compatible的值必須進行記錄說明其含義。

runtime configuration

一般來說,DT是firmware傳遞資料給kernel的唯一方法,PowerPC呼叫of_scan_flat_dt(early_init_dt_scan_root, NULL)執行early init,ARM則是呼叫mdesc = setup_machine_fdt(__atags_pointer)

device population

在early configuration之後,kernel會用 unflatten_device_tree()將DT轉成device node tree,讓之後的init_early(), init_irq() and init_machine()等等使用,init_early()用於任何需要在啟動過程中儘早執行的設置,init_irq()用於設置中斷處理,而init_machine()負責建立Linux platform device,這裡主要呼叫of_platform_populate()建構platform device。之後的driver也是透過of_platform_populate()建構platform device。
i2c_add_driver( ) @ i2c.h  
  |-> i2c_register_driver() @i2c-core.c /* 將device_driver中的bus_type設成i2c_bus_type */  
    |--> driver_register() @driver.c  
      |--> bus_add_driver() @bus.c /* 建立sysfs file node 與 attr */  
        |--> driver_attach() @dd.c  
          |--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) @bus.c  
            |--> __driver_attach @dd.c  
              |--> driver_match_device(drv, dev) @base.h  
                |--> i2c_device_match() @i2c-core.c  
                  /***********************************************  
                   如果是device tree, 會透過 of_driver_match_device()去做匹配的動作
                   如果不是device tree就改用 i2c_match_id()去完成匹配的動作
                  ***************************************************/ 
                  /* Attempt an OF style match */
                  if (of_driver_match_device(dev, drv))
                    return 1;

                  /* Then ACPI style match */
                  if (acpi_driver_match_device(dev, drv))
                    return 1;
                  
                  driver = to_i2c_driver(drv);
                  /* match on an id table if there is one */
                  if (driver->id_table)
                    return i2c_match_id(driver->id_table, client) != NULL;
              |--> driver_probe_device() @dd.c  
                /*如果匹配成功, 接下來就要準備 call driver 中的 probe function*/
                |--> really_probe() @dd.c  
                  |--> i2c_device_probe() @i2c-core.c  
                    |--> rt5627_i2c_probe() @rt5627.c  
from http://janbarry0914.blogspot.com/2014/08/device-tree-i2c-device-driver-match.html



2018年4月21日 星期六

A pattern for state machine II - SM framework


我將A pattern for state machine改寫成framework形式,使用者需要先使用sm_alloc()分配一個struct sm,再使用sm_fp_reg()將每個state的callback function掛上,最後有event要執行時,只要呼叫sm_run(sm, new_event)即可。
state的callback function的撰寫邏輯,大致與之前一樣,return下一個state,進入下一個state要做的動作,我都用do_state_x()包起來,而do_state_x()會return 該state。

sm.h

#ifndef SM_H
#define SM_H

struct sm {
    int cur_state;
    int prv_event;
    void *v;
};

/**
 * state function
 * @return next state
 */
typedef int (*sm_st_fp)(struct sm *sm, int new_event);
struct sm *sm_alloc(int num_of_state);
int sm_run(struct sm *s, int new_event);
int sm_fp_reg(struct sm *s, int state, sm_st_fp fp);

#endif


sm.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sm.h"

struct _sm {
    struct sm s;
    int num_of_st;
    sm_st_fp fp[0];
};

struct sm *sm_alloc(int num_of_state)
{
    struct _sm *_s;
    int sz;

    sz = sizeof(struct _sm) + sizeof(sm_st_fp) * num_of_state;
    _s = (struct _sm*) malloc(sz);
    if (!_s) {
        return NULL;
    }
    memset(_s, 0, sz);
    _s->num_of_st = num_of_state;
    return (struct sm*)_s;
}

int sm_run(struct sm *s, int new_event)
{
    struct _sm *_s = (struct _sm*) s;
    if (s->cur_state > _s->num_of_st) {
        printf("out of st\n");
        return -1;
    }

    if (!_s->fp[s->cur_state]) {
        printf("null fp\n");
        return -1;
    }

    s->cur_state = _s->fp[s->cur_state](s, new_event);
    s->prv_event = new_event;
    return 0;
}

int sm_fp_reg(struct sm *s, int state, sm_st_fp fp)
{
    struct _sm *_s = (struct _sm*) s;
    if (state > _s->num_of_st) {
        printf("out of st\n");
        return -1;
    }

    _s->fp[state] = fp;
    return 0;
}


main.c

因為是framework,所以,我把state/event都拉出來,
因此每個State Machine都應該定義自己的event與state。
enum state {
    STATE_1,
    STATE_2,
    STATE_3,
    STATE_4,
};

enum event {
    E1 = 1,
    E2,
    E3,
    E4,
};

int main(int argc, char *argv[])
{
    struct sm *s;
    char ch;
    s = sm_alloc(3);
    if (!s) {
        return -1;
    }
    sm_fp_reg(s, STATE_1, in_state_1);
    sm_fp_reg(s, STATE_2, in_state_2);
    sm_fp_reg(s, STATE_3, in_state_3);
    sm_fp_reg(s, STATE_4, in_state_4);
    while (1) {
        while (((ch = getc(stdin)) == '\n') || (ch < '0') || (ch > '4'));
        sm_run(s, ch - '0');
    }

    return 0;
}


refine callback function

因為是framework,所以,callback function的定義要改成return int。
int do_s1(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_1;
}

int do_s2(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_2;
}

int do_s3(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_3;
}

int do_s4(void)
{
    printf("%s(#%d)\n", __FUNCTION__, __LINE__);
    return STATE_4;
}


int in_state_1(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E1:
            printf("change to S2\n");
            return do_s2();
        case E2:
            printf("change to S3\n");
            return do_s3();
        default:
            printf("keep the same STATE && do nothing\n");
            return STATE_1;
    }
}

int in_state_2(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E3:
            printf("change to S3\n");
            return do_s3();
        default:
            printf("keep the same STATE && do s2 again\n");
            return do_s2();     // do s2 again
    }
}

int in_state_3(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E2:
            printf("change to S4\n");
            return do_s4();
        default:
            printf("keep the same STATE && do nothing\n");
            return STATE_3;
    }
}

int in_state_4(struct sm *s, int new_event)
{
    printf("%s(#%d): pre_evt:%d, new_evt:%d\n", __FUNCTION__, __LINE__, s->prv_event, new_event);
    switch (new_event) {
        case E1:
            printf("change to S2\n");
            return do_s2();
        case E3:
            printf("change to S1\n");
            return do_s1();
        default:
            printf("keep the same STATE && do again\n");
            return do_s4();
    }
}



2018年4月15日 星期日

赫茲伯格的雙因素激勵理論 - 筆記


雙因素理論(Two Factor Theory)又叫激勵保健理論(Motivator-Hygiene Theory),是美國的行為科學家弗雷德里克·赫茨伯格(Fredrick Herzberg)提出來的。
赫茨伯格著手研究哪些事情使人們在工作中快樂和滿足,哪些事情造成不愉快和不滿足。結果他發現,使員工感到滿意的都是屬於工作本身或工作內容方面的;使員工感到不滿的,都是屬於工作環境或工作關係方面的。他把前者叫做激勵因素,後者叫做保健因素。
那些能帶來積極態度、滿意和激勵作用的因素就叫做“激勵因素”,這是那些能滿足個人自我實現需要的因素,包括:具挑戰性的工作、自主權、嘉獎和升遷。

根據赫茨伯格的理論,在調動員工積極性方面,可以分別採用以下兩種基本做法:
(一)直接滿足
直接滿足,又稱為工作任務以內的滿足。它是一個人通過工作所獲得的滿足,這種滿足是通過工作本身和工作過程中人與人的關係得到的。它能使員工學習到新的知識和技能,產生興趣和熱情,使員工具有光榮感、責任心和成就感。
(二)間接滿足
間接滿足,又稱為工作任務以外的滿足。這種滿足不是從工作本身獲得的,而是在工作以後獲得的。例如晉升、授銜、嘉獎或物質報酬和福利等,就都是在工作之後獲得的。在使用這種激勵因素時,必須與個人的工作績效掛鉤。否則一味的“吃大鍋飯”,久而久之,獎金就會變成保健因素,再多也起不了激勵作用。




2018年4月7日 星期六

note for "The Art of Readable Code" - CH9 Variables and Readability


減少不必要的變數可以增加閱讀性, 比如
root_message.last_view_time = datetime.datetime.now()
會比下面code更容易理解
now = datetime.datetime.now()
root_message.last_view_time = now

下面還有幾個需要優化的的例子, 可以好好思考如何改善.
boolean done = false;
while (/* condition */ && !done) {
    ...
    if (...) {
        done = true;
        continue;
    }
}
與
var remove_one = function (array, value_to_remove) {
    var index_to_remove = null;
    for (var i = 0; i < array.length; i += 1) {
        if (array[i] === value_to_remove) {
            index_to_remove = i;
            break;
        }
    }
    if (index_to_remove !== null) {
        array.splice(index_to_remove, 1); 
    }
};


programmer都知道要盡量限縮變數的範圍, 因為可視範圍小, 要記住的變數數量也會減少, 也且可以避免global/local variable用錯的窘境.
submitted = false; // Note: global variable
var submit_form = function (form_name) {
    if (submitted) {
        return;  // don't double-submit the form
    }
    ...
    submitted = true;
};
可以被修改成
var submit_form = (function () {
    var submitted = false; // Note: can only be accessed by the function below
    return function (form_name) {
        if (submitted) {
            return;  // don't double-submit the form
        }
        ...
        submitted = true; 
    };
}());


    參考資料:
  1. The Art of Readable Code




Table Of Content for tag "The Art of Readable Code"


這是一本好書, 建議每個programmer都應該買來翻一翻




2018年3月31日 星期六

An Sample Code for notification-chain


這個pattern設計是根據kernel的notification chain做一些修改, 主要應用於眾多subscriber註冊於某個有興趣的event ID, 會根據註冊時的權重由大到小串成link-list, 當publisher執行blocking_notif_call(event_id, int val, void *v)時, 就會依序call這些subscriber註冊的callback function, 達到通知的效果.

notification-chain.h

#ifndef NOTIFICATION_CHAIN_H
#define NOTIFICATION_CHAIN_H

enum notif_id {
    /* 新的ID請加在這裡 */
    notif_id_network,
    notif_id_lte,
    notif_id_max /* must be last */
};

int notif_init(void);

typedef int (*notif_call_fp)(char const * caller, unsigned long val, void *v);

int _notif_reg(enum notif_id nid,
                notif_call_fp notif_call,
                char *notif_call_name,
                int priority);
#define notif_reg(nid, notif_call, priority) _notif_reg(nid, notif_call, #notif_call, priority);
int notif_unreg(enum notif_id nid, notif_call_fp notif_call);
char * notif_dump(char *buf, int sz);

/* call in the caller thread */
int _blocking_notif_call(enum notif_id nid,
                        unsigned long val, /* Value passed to notifier function */
                        void *v, /* Pointer passed to notifier function */
                        char const * caller);

#define blocking_notif_call(nid, val, v) _blocking_notif_call(nid, val, v, __FUNCTION__)

/* call by another thread */
int async_notif_call(enum notif_id nid,
                    unsigned long val /* Value passed to notifier function */,
                    void *v /* Pointer passed to notifier function */,
                    void (*free_v)(void *v));

#endif


notification-chain.c

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>
#include "notification-chain.h"

struct notif_block {
    notif_call_fp notif_call;
    char *notif_call_name;
    struct notif_block *next;
    int priority;
    int called;
};

struct notif_chain_head {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    struct notif_block *next;
};

struct notif_statistical {
    unsigned int alloc;
    unsigned int alloced;
    unsigned int freed;
};

struct notif_statistical notif_statistical = {};

static struct notif_chain_head *_notif_chain_tab = NULL;


static int _init_notif_chain(struct notif_chain_head **h)
{
    int i;

    *h = (struct notif_chain_head *) malloc(sizeof(struct notif_chain_head) * notif_id_max);
    if (!*h) {
        return -1;
    }

    for (i = 0; i < notif_id_max; i++) {
        pthread_mutex_init(&((*h)[i].mutex), NULL);
        pthread_cond_init(&((*h)[i].cond), NULL);
        (*h)[i].next = NULL;
    }
    return 0;
}

int notif_init(void)
{
    int ret = 0;

    if ((ret = _init_notif_chain(&_notif_chain_tab)) < 0) {
        return ret;
    }
    return ret;
}

static int _notif_check_parms(enum notif_id nid,
                            notif_call_fp notif_call,
                            char *notif_call_name)
{
    if (!_notif_chain_tab) {
        return -1;
    }

    if ((nid < 0) || (nid >= notif_id_max)) {
        return -1;
    }

    if (!notif_call) {
        return -1;
    }

    if (!notif_call_name) {
        return -1;
    }

    return 0;
}

static int _notif_block_alloc(struct notif_block **n)
{
    notif_statistical.alloc++;
    *n = (struct notif_block*) malloc(sizeof(struct notif_block));
    if (!(*n)) {
        return -1;
    }
    memset(*n, 0, sizeof(struct notif_block));
    notif_statistical.alloced++;
    return 0;
}

static int _notif_block_free(struct notif_block *n)
{
    notif_statistical.freed++;
    if (n->notif_call_name) {
        free(n->notif_call_name);
    }
    if (n) {
        free(n);
    }
    return 0;
}

int _notif_reg(enum notif_id nid,
    notif_call_fp notif_call,
    char *notif_call_name,
    int priority)
{
    struct notif_block **n, *nb;

    if (_notif_check_parms(nid, notif_call, notif_call_name) < 0) {
        return -1;
    }

    if (_notif_block_alloc(&nb) < 0) {
        return -1;
    }

    /* fill info */
    nb->notif_call = notif_call;
    nb->notif_call_name = strdup(notif_call_name);
    if (!nb->notif_call_name) {
        _notif_block_free(nb);
        return -1;
    }
    nb->priority = priority;

    /* insert into tab */
    pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
    n = &(_notif_chain_tab[nid].next);
    while ((*n) && ((*n)->priority > nb->priority)) {
        n = &((*n)->next);
    }
    nb->next = *n;
    *n = nb;
    pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));


    return 0;
}

int notif_unreg(enum notif_id nid, notif_call_fp notif_call)
{
    struct notif_block **n, *sn = NULL;

    if (_notif_check_parms(nid, notif_call, (void *) 0xFF /* ignore check */) < 0) {
        return -1;
    }

    pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
    n = &(_notif_chain_tab[nid].next);
    while ((*n)) {
        if ((*n)->notif_call == notif_call) {
            sn = *n;
            *n = (*n)->next;
            break;
        }
        n = &((*n)->next);
    }
    pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));

    if (!sn) {
        // not found
        return -1;
    }

    _notif_block_free(sn);

    return 0;
}
/* call in the caller thread */
int _blocking_notif_call(enum notif_id nid,
                        unsigned long val, /* Value passed to notifier function */
                        void *v, /* Pointer passed to notifier function */
                        char const * caller)
{
    struct notif_block *n;

    if (_notif_check_parms(nid, (void*) 0xFF /* just ignore check */, (void *) 0xFF /* just ignore check */) < 0) {
        return -1;
    }

    /* insert into tab */
    pthread_mutex_lock(&(_notif_chain_tab[nid].mutex));
    n = _notif_chain_tab[nid].next;
    while (n) {
        n->notif_call(caller, val, v);
        n->called++;
        n = n->next;
    }
    pthread_mutex_unlock(&(_notif_chain_tab[nid].mutex));

    return 0;
}
char * notif_dump(char *buf, int sz)
{
    int i;
    char *p = buf;
    struct notif_block *n;

    memset(buf, 0, sz);
    p += snprintf(p, sz - (buf - p), "\tnotif_fp/notif_fp_name/pri called\n");
    for (i = 0; i < notif_id_max; i++) {
        pthread_mutex_lock(&(_notif_chain_tab[i].mutex));
        n = _notif_chain_tab[i].next;
        p += snprintf(p, sz - (buf - p), "notif id: %d\n", i);
        while (n) {
            p += snprintf(p, sz - (buf - p), "\t%p/%s/%d %d\n",
                            n->notif_call, n->notif_call_name, n->priority, n->called);
            n = n->next;
        }
        pthread_mutex_unlock(&(_notif_chain_tab[i].mutex));
    }
    return buf;
}


main.c

#include <stdio.h>
#include <unistd.h>
#include "notification-chain.h"
/* 這裡有用service-reg作為publisher */
#include "service-reg.h"

#define LINK_DOWN   0
#define LINK_UP     1

void *publisher(void *v)
{
    int *link_state = (int *) v;
    ++(*link_state);
    (*link_state) %= 2;
    blocking_notif_call(notif_id_network, *link_state, NULL);
}

int link_notif_call1(char const *caller, unsigned long val, void *v)
{
    printf("%s(#%d): caller:%s, %s\n",
                    __FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
    return 0;
}

int link_notif_call2(char const *caller, unsigned long val, void *v)
{
    printf("%s(#%d): caller:%s, %s\n",
                    __FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
    return 0;
}

int link_notif_call3(char const *caller, unsigned long val, void *v)
{
    printf("%s(#%d): caller:%s, %s\n",
                    __FUNCTION__, __LINE__, caller, val == LINK_UP ? "UP" : "DOWN");
    return 0;
}

int main(int argc, char *argv[])
{
    int link_state = 0;
    char buf[1024];
    /* 請先call notif_init()做初始化 */ 
    notif_init();

    srv_reg("publisher", publisher, &link_state);
    srv_start_periodical("publisher", 1, 0);
    sleep(2);
    printf("reg 1, 10\n");
    notif_reg(notif_id_network, link_notif_call1, 10);

    sleep(2);
    printf("dump\n");
    printf("%s\n", notif_dump(buf, sizeof(buf)));

    sleep(2);
    printf("reg 3, 30\n");
    notif_reg(notif_id_network, link_notif_call3, 30);

    sleep(2);
    printf("reg 2, 20\n");
    notif_reg(notif_id_network, link_notif_call2, 20);

    sleep(2);
    printf("dump\n");
    printf("%s\n", notif_dump(buf, sizeof(buf)));

    sleep(2);
    printf("unreg 3\n");
    notif_unreg(notif_id_network, link_notif_call3);

    sleep(2);
    printf("dump\n");
    printf("%s\n", notif_dump(buf, sizeof(buf)));

    sleep(2);
    printf("unreg 1 lte \n");
    notif_unreg(notif_id_lte, link_notif_call1);

    sleep(2);
    printf("dump\n");
    printf("%s\n", notif_dump(buf, sizeof(buf)));

    return 0;
}


result

brook@vista:~/notification-chain$ ./demo
reg 1, 10
link_notif_call1(#20): caller:publisher, UP
link_notif_call1(#20): caller:publisher, DOWN
dump
        notif_fp/notif_fp_name/pri      called
notif id: 0
        0x400fa4/link_notif_call1/10    2
notif id: 1

link_notif_call1(#20): caller:publisher, UP
link_notif_call1(#20): caller:publisher, DOWN
reg 3, 30
link_notif_call3(#34): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
reg 2, 20
link_notif_call3(#34): caller:publisher, UP
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
        notif_fp/notif_fp_name/pri      called
notif id: 0
        0x401046/link_notif_call3/30    4
        0x400ff5/link_notif_call2/20    2
        0x400fa4/link_notif_call1/10    8
notif id: 1

link_notif_call3(#34): caller:publisher, UP
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call3(#34): caller:publisher, DOWN
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
unreg 3
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
        notif_fp/notif_fp_name/pri      called
notif id: 0
        0x400ff5/link_notif_call2/20    6
        0x400fa4/link_notif_call1/10    12
notif id: 1

link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
unreg 1 lte
link_notif_call2(#27): caller:publisher, UP
link_notif_call1(#20): caller:publisher, UP
link_notif_call2(#27): caller:publisher, DOWN
link_notif_call1(#20): caller:publisher, DOWN
dump
        notif_fp/notif_fp_name/pri      called
notif id: 0
        0x400ff5/link_notif_call2/20    10
        0x400fa4/link_notif_call1/10    16
notif id: 1



note for "The Art of Readable Code" - CH8 Breaking Down Giant Expressions


Explaining Variables

coding主要的主軸是readable, 其餘都是次要的, 而利用額外的變數, 來解釋某個較小的表示式, 就被稱為"explaining variable". 比如
username = line.split(':')[0].strip() if username == "root":
會比下面這行容易理解
if line.split(':')[0].strip() == "root":
上面的username就被稱為"explaining variable"


Summary Variables

利用額外的變數, 來解釋某個表示式, 稱為"Summary Variables", 比如
final boolean user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document) {
// user can edit this document...
}
這裡的user_owns_document就被稱為Summary Variables, 當然你也可以不斷的使用(request.user.id == document.owner_id)


Using De Morgan’s Laws

1) not (a or b or c) ⇔ (not a) and (not b) and (not c)
2) not (a and b and c) ⇔ (not a) or (not b) or (not c)
我們可以多利用Morgan’s Laws簡化一些判斷式, 當然一切還是以readable為前提, 比如
if (!file_exists || is_protected) Error("Sorry, could not read file.");
會比下面這行容易理解
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");


Finding a More Elegant Approach

有時候我們反向思考可以找到更好的
// Check if 'begin' or 'end' falls inside 'other'.
bool Range::OverlapsWith(Range other) {
    return (begin >= other.begin && begin lt; other.end) || 
        (end > other.begin && end lt;= other.end)
}
換個方式寫會更好閱讀
bool Range::OverlapsWith(Range other) {
    if (other.end <= begin) return false;  // They end before we begin
    if (other.begin >= end) return false;  // They begin after we end
    return true;  // Only possibility left: they overlap
}


Simplify Expressions

有時候我們可以用MARCO增加閱讀性, 比如
void AddStats(const Stats& add_from, Stats* add_to) {
    #define ADD_FIELD(field) add_to->set_##field(add_from.field() + add_to->field())
    ADD_FIELD(total_memory);
    ADD_FIELD(free_memory);
    ADD_FIELD(swap_memory);
    ADD_FIELD(status_string);
    ADD_FIELD(num_processes);
    ...
    #undef ADD_FIELD
}
比下面這例子容易閱讀
void AddStats(const Stats& add_from, Stats* add_to) {
    add_to->set_total_memory(add_from.total_memory() + add_to->total_memory());
    add_to->set_free_memory(add_from.free_memory() + add_to->free_memory());
    add_to->set_swap_memory(add_from.swap_memory() + add_to->swap_memory());
    add_to->set_status_string(add_from.status_string() + add_to->status_string());
    add_to->set_num_processes(add_from.num_processes() + add_to->num_processes());
     ...
}



coding主要的主軸是readable, 其餘都是次要的, 而利用額外的變數, 來解釋某表示式, 或利用MACRO更容易閱讀, 以及換個方式寫, 都是手段


    參考資料:
  1. The Art of Readable Code

2018年3月17日 星期六

An Sample Code for threads control - A Wrap for service/thread start/stop/periodical run


multi-thread programming設計很常見, 寫法上與功能上也都很相似, 最基本的幾個操作就是, create/start/stop/periodical run/destroy等等. 最好還能monitor這些thread狀況, 比如被執行幾次, 執行時間多久, 是否有dead lock等等, 於是就寫了這個pattern, 提供這類功能.

service-reg.h

#ifndef SERVICE_REG_H
#define SERVICE_REG_H
#include <sys/time.h>

typedef void *(*srv_fp) (void *);

char * srv_dump(char *buf, int sz);

int _srv_reg(char *srv_name, srv_fp fp, char *fp_name, void *srv_data);
#define srv_reg(srv_name, fp, srv_data) _srv_reg(srv_name, fp, #fp, srv_data)
int srv_start(char *srv_name); // run forever without any delay
int srv_stop(char *srv_name);
int srv_unreg(char *srv_name);
int srv_start_periodical(char *srv_name, unsigned long sec, unsigned long nsec); // run forever with delay

#define SRV_NO_MEM -1

#endif


service-reg.c

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <stdint.h>
#include <unistd.h>
#include "service-reg.h"
#include "lookup_table.h"

#define SEC2NSEC 1000000000.0

struct srv_info {
    void *srv_data;
    char *srv_name;
    srv_fp fp;
    int in_fp;
    char *fp_name;
    unsigned run_cnt;
    pthread_t thread_id; /* ID returned by pthread_create() */
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    struct srv_info *next;
    volatile int running; /* 1 for running, 0 for stop */
    struct timespec active_time;
    struct timespec last_enter;
    struct timespec last_exit;
    int tm_fd; /* used to delay */
};

struct srv_tab {
    pthread_mutex_t mutex;
    struct srv_info *list;
    unsigned int num_srv;
} srv_tab = {
    .mutex = PTHREAD_MUTEX_INITIALIZER,
    .list = NULL,
    .num_srv = 0,
};

static void _srv_update_active_time(struct srv_info *s)
{
    clock_gettime(CLOCK_MONOTONIC, &(s->last_exit));

    s->active_time.tv_sec += s->last_exit.tv_sec - s->last_enter.tv_sec;
    if (s->last_exit.tv_nsec > s->last_enter.tv_nsec) {
        s->active_time.tv_nsec += s->last_exit.tv_nsec - s->last_enter.tv_nsec;
    } else {
        s->active_time.tv_sec--;
        s->active_time.tv_nsec += SEC2NSEC + s->last_exit.tv_nsec - s->last_enter.tv_nsec;
    }

    if (s->active_time.tv_nsec >= SEC2NSEC) {
        s->active_time.tv_nsec -= SEC2NSEC;
        s->active_time.tv_sec++;
    }
}

static void *_srv_wrap(void *v)
{
    struct srv_info *s = (struct srv_info *) v;
    while (1) {
recheck:
        if (s->running == 0) {
stoprun:
            pthread_cond_wait(&(s->cond), &(s->mutex));
            goto recheck;
        }

        /* if timer is enabled, than wait */
again:
        if (s->tm_fd != -1) {
            uint64_t exp;
            ssize_t sz;
            sz = read(s->tm_fd, &exp, sizeof(exp));
            if (sz != sizeof(exp)) {
                printf("timerfd_settime failed: s->tm_fd:%d, errno:%d/%s\n",
                            s->tm_fd, errno, strerror(errno));
                if (errno == EINTR) {
                    goto again;
                }
            }

            if (s->running == 0) {
                goto stoprun;
            }
        }

        clock_gettime(CLOCK_MONOTONIC, &(s->last_enter));
        s->in_fp = 1;
        s->fp(s->srv_data);
        s->in_fp = 0;
        _srv_update_active_time(s);
        s->run_cnt++;
    }
    return NULL;
}

int srv_init(void)
{
    return 0;
}

char * srv_dump(char *buf, int sz)
{
    struct srv_info *s;
    char *p = buf;
    int i = 0;
    struct timespec cur_clk;
    struct itimerspec cur_timer;

    clock_gettime(CLOCK_MONOTONIC, &cur_clk);

    if (buf == NULL) {
        return NULL;
    }

    memset(buf, 0, sz);
    pthread_mutex_lock(&(srv_tab.mutex));
    s = srv_tab.list;

    p += snprintf(p, sz - (buf - p), "cur-clk: %f\n", cur_clk.tv_sec + cur_clk.tv_nsec/SEC2NSEC);
    p += snprintf(p, sz - (buf - p), "id   srv_name    running/in_fp\t\tfp/fname/dp\n");
    p += snprintf(p, sz - (buf - p), "\t\t\trun-cnt/run-time\tenter-clk/leave-clk\n");
    p += snprintf(p, sz - (buf - p), "\t\t\ttm_fd/next_expir/perodic\n");
    while (s != NULL) {
        p += snprintf(p, sz - (buf - p), "%-8d%-16s%2d/%-2d\t\t%16p/%s/%-16p\t\t\t\n",
                        i++, s->srv_name, s->running, s->in_fp, s->fp, s->fp_name, s->srv_data);
        p += snprintf(p, sz - (buf - p), "\t\t   %10d/%-12f\t%12f/%-12f\n",
                        s->run_cnt,
                        s->active_time.tv_sec + s->active_time.tv_nsec/SEC2NSEC,
                        s->last_enter.tv_sec + s->last_enter.tv_nsec/SEC2NSEC,
                        s->last_exit.tv_sec + s->last_exit.tv_nsec/SEC2NSEC);
        if (s->tm_fd != -1) {
            if (timerfd_gettime(s->tm_fd, &cur_timer) < 0) {
                printf("timerfd_get failed: errno:%d/%s\n", errno, strerror(errno));
                p += snprintf(p, sz - (buf - p), "\t\t   %6d\n", s->tm_fd);
            } else {
                p += snprintf(p, sz - (buf - p), "\t\t   %6d/%f/%-16f\n",
                                s->tm_fd, cur_timer.it_value.tv_sec + cur_timer.it_value.tv_nsec/SEC2NSEC,
                                cur_timer.it_interval.tv_sec + cur_timer.it_interval.tv_nsec/SEC2NSEC);
            }
        }
        s = s->next;
    }
    pthread_mutex_unlock(&(srv_tab.mutex));
    return buf;
}

int _srv_reg(char *srv_name, srv_fp fp, char *fp_name, void *srv_data)
{
    struct srv_info *s = (struct srv_info *) malloc(sizeof(struct srv_info));
    if (s == NULL) {
        return SRV_NO_MEM;
    }
    memset(s, 0, sizeof(struct srv_info));
    s->fp = fp;
    s->srv_data = srv_data;
    s->srv_name = strdup(srv_name);
    s->fp_name = strdup(fp_name);
    pthread_cond_init(&s->cond, NULL);
    pthread_mutex_init(&s->mutex, NULL);
    s->tm_fd = -1;

    if (!s->srv_name) {
        free(s);
        return SRV_NO_MEM;
    }

    /* insert into tab */
    pthread_mutex_lock(&(srv_tab.mutex));
    s->next = srv_tab.list;
    srv_tab.list = s;
    srv_tab.num_srv++;
    pthread_mutex_unlock(&(srv_tab.mutex));

    /* create the thread */
    pthread_create(&s->thread_id, NULL , _srv_wrap, s);
    return 0;
}

static int _srv_update_timer(struct srv_info *s, unsigned long sec, unsigned long nsec)
{
    struct timespec now_tm;
    struct itimerspec new_tm;

    if (s->tm_fd < 0) {
        s->tm_fd = timerfd_create(CLOCK_MONOTONIC, 0);
        if (s->tm_fd < 0) {
            printf("timerfd_create failed: errno:%d/%s\n", errno, strerror(errno));
            return -1;
        }
    }

    if (clock_gettime(CLOCK_MONOTONIC, &now_tm) == -1) {
        printf("timerfd_create failed: errno:%d/%s\n", errno, strerror(errno));
        return -1;
    }

    new_tm.it_value.tv_sec = now_tm.tv_sec + sec;
    new_tm.it_value.tv_nsec = now_tm.tv_nsec + nsec;

    new_tm.it_interval.tv_sec = sec;
    new_tm.it_interval.tv_nsec = nsec;

    if (timerfd_settime(s->tm_fd, TFD_TIMER_ABSTIME, &new_tm, NULL) < 0) {
        printf("timerfd_settime failed: errno:%d/%s\n", errno, strerror(errno));
        return -1;
    }

    return 0;
}

inline static int _srv_name_equal(struct srv_info *s, char *srv_name)
{
    return strlen(s->srv_name) == strlen(srv_name) && !strcmp(s->srv_name, srv_name);
}

static int _srv_set_running(char *srv_name, int running, unsigned long sec, unsigned long nsec)
{
    struct srv_info *s;
    int ret = 0;

    pthread_mutex_lock(&(srv_tab.mutex));
    s = srv_tab.list;
    while (s != NULL) {
        if (_srv_name_equal(s, srv_name)) {
            s->running = running;
            if (sec || nsec) {
                ret = _srv_update_timer(s, sec, nsec);
            } else {
                /* reset timer to zero */
                _srv_update_timer(s, 0, 0);
                /* clear timer */
                close(s->tm_fd);
                s->tm_fd = -1;
            }
            pthread_cond_signal(&s->cond);
            pthread_mutex_unlock(&(srv_tab.mutex));
            return ret;
        }
        s = s->next;
    }
    pthread_mutex_unlock(&(srv_tab.mutex));

    return -1;
}

int srv_start(char *srv_name)
{
    return _srv_set_running(srv_name, 1 /* running */, 0 /* delay.sec */, 0 /* delay.nsec */);
}

int srv_stop(char *srv_name)
{
    return _srv_set_running(srv_name, 0 /* running */, 0 /* delay.sec */, 0 /* delay.nsec */);
}

static int _srv_free(struct srv_info *s)
{
    int ret;
    s->running = 0;

    /* cancel the thread */
    ret = pthread_cancel(s->thread_id);
    if (ret != 0) {
        printf("pthread_cancel failed: %d/%d\n", ret, errno);
        /* FIXME */
    }

    ret = pthread_join(s->thread_id, NULL);
    if (ret != 0) {
        printf("pthread_join failed: %d/%d\n", ret, errno);
        /* FIXME */
    }

    /* free resouce */
    pthread_cond_destroy(&(s->cond));
    pthread_mutex_destroy(&(s->mutex));
    free(s->srv_name);
    free(s->fp_name);
    if (s->tm_fd != -1) {
        close(s->tm_fd);
    }
    free(s);
    return ret;
}

int srv_unreg(char *srv_name)
{
    struct srv_info **s, *sn = NULL;
    int ret;
    s = &(srv_tab.list);
    pthread_mutex_lock(&(srv_tab.mutex));
    while ((*s) != NULL) {
        if (_srv_name_equal(*s, srv_name)) {
            sn = *s;
            *s = (*s)->next;
            srv_tab.num_srv--;
            break;
        }
        s = &((*s)->next);
    }
    pthread_mutex_unlock(&(srv_tab.mutex));
    if (sn) {
        ret = _srv_free(sn);
    }

    return 0;
}

int srv_start_periodical(char *srv_name, unsigned long sec, unsigned long nsec)
{
    return _srv_set_running(srv_name, 1 /* running */, sec /* delay.sec */,  nsec /* delay.nsec */);
}




main.c (test program)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include "service-reg.h"

int pr(const char *fmt, ...)
{
    va_list ap;
    int ret;
    struct timespec cur_clk;
    clock_gettime(CLOCK_MONOTONIC, &cur_clk);

    printf("***CLK: %f\n", cur_clk.tv_sec + cur_clk.tv_nsec/1000000000.0);
    va_start(ap, fmt);
    ret = vprintf(fmt, ap);
    va_end(ap);
    return ret;
}

void *sms_service(void *v)
{
    int *i = (int *) v;
    pr("   ->hello, %p/%d, srv_start_periodical %d\n", v, (*i)++, 2);
    pr("   ->%d\n", srv_start_periodical("brook", 2, 0));
}

int main(int argc, char *argv[])
{
    int i = 0, data = 0, ret;
    char name[] = "brook", buf[1024];

    pr("srv_reg %s\n", name);
    srv_reg(name, sms_service, (void*)&data);

    pr("srv_start %s\n", name);
    srv_start(name);

    pr("sleep 3\n");
    sleep(3);
    sleep(3);

    pr("srv_stop %s\n", name);
    srv_stop(name);

    pr("srv_start_periodical %s with %dsec\n", name, 1);
    srv_start_periodical(name, 1, 0);

    pr("srv_dump %s\n", name);
    pr("%s\n", srv_dump(buf, sizeof(buf)));

    pr("sleep 2\n");
    sleep(2);

    pr("srv_unreg %s\n", name);
    srv_unreg(name);

    pr("over\n");
    return 0;
}



執行結果

brook@vista:~$ ./service-reg
***CLK: 7914669.498393
srv_reg brook
***CLK: 7914669.498469
srv_start brook
***CLK: 7914669.498481
sleep 3
***CLK: 7914669.498531
        ->hello, 0x7ffc24e80df8/0, srv_start_periodical 2
***CLK: 7914669.498541
        ->0
***CLK: 7914671.498595
        ->hello, 0x7ffc24e80df8/1, srv_start_periodical 2
***CLK: 7914671.498647
        ->0
***CLK: 7914672.498576
srv_stop brook
***CLK: 7914672.498599
srv_start_periodical brook with 1sec
***CLK: 7914672.498606
srv_dump brook
***CLK: 7914672.498621
        ->hello, 0x7ffc24e80df8/2, srv_start_periodical 2
***CLK: 7914672.498662
        ->0
***CLK: 7914672.498625
cur-clk: 7914672.498611
id      srv_name        running/in_fp           fp/fname/dp
                        run-cnt/run-time        enter-clk/leave-clk
                        tm_fd/next_expir/perodic
0       brook            1/0                    0x40106f/sms_service/0x7ffc24e80df8
                            2/0.000082          7914671.498594/7914671.498656
                        3/0.999982/1.000000

***CLK: 7914672.498680
sleep 2
***CLK: 7914674.498698
        ->hello, 0x7ffc24e80df8/3, srv_start_periodical 2
***CLK: 7914674.498730
        ->0
***CLK: 7914674.498742
srv_unreg brook
***CLK: 7914674.498997
over




熱門文章