2017年2月24日 星期五

Linux內核驅動程序初始化順序的調整


今天在做一個驅動的時候要用到另一個驅動(I2C)提供的API,在內核初始化時碰到了一個依賴問題。

我的驅動在I2C初始化之前就運行起來了,而這時I2C提供的API還處於不可用狀態。查了很多資料,網上有人說所有使用module_init這個宏的驅動程序的起動順序都是不確定的(我沒有查到權威的資料)。

所有的__init函數在區段.initcall.init中還保存了一份函數指針,在初始化時內核會通過這些函數指針調用這些__init函數指針,並在整個初始化完成後,釋放整個init區段(包括.init.text,.initcall.init等)。

注意,這些函數在內核初始化過程中的調用順序只和這裡的函數指針的順序有關,和1)中所述的這些函數本身在.init.text區段中的順序無關。在2.4內核中,這些函數指針的順序也是和鏈接的順序有關的,是不確定的。在2.6內核中,initcall.init區段又分成7個子區段,分別是
?
1
2
3
4
5
6
7
.initcall1.init
.initcall2.init
.initcall3.init
.initcall4.init
.initcall5.init
.initcall6.init
.initcall7.init
當需要把函數fn放到.initcall1.init區段時,只要聲明
?
1
core_initcall(fn);
即可。
其他的各個區段的定義方法分別是:
?
1
2
3
4
5
6
7
core_initcall(fn) --->.initcall1.init
postcore_initcall(fn) --->.initcall2.init
arch_initcall(fn) --->.initcall3.init
subsys_initcall(fn) --->.initcall4.init
fs_initcall(fn) --->.initcall5.init
device_initcall(fn) --->.initcall6.init
late_initcall(fn) --->.initcall7.init


而與2.4兼容的initcall(fn)則等價於device_initcall(fn)。各個子區段之間的順序是確定的,即先調用.initcall1.init中的函數指針,再調用.initcall2.init中的函數指針,等等。而在每個子區段中的函數指針的順序是和鏈接順序相關的,是不確定的。

在內核中,不同的init函數被放在不同的子區段中,因此也就決定了它們的調用順序。這樣也就解決了一些init函數之間必須保證一定的調用順序的問題。按照include/Linux/init.h文件所寫的,我在驅動裡償試了這樣兩種方式:

?
1
2
__define_initcall("7", fn);
late_initcall(fn);
都可以把我的驅動調整到最後調用。實際上上面兩個是一回事:
?
1
#define late_initcall(fn) __define_initcall("7", fn)

Busybox的halt, poweroff, reboot指令,以及driver內可以作這些動作的signal

指令 halt、poweroff、跟 reboot 的動作過程類似,都是 kill pid=1 的行程,一般是 init,pid=1 的行程會去執行 inittab 的 shutdown 動作後,呼叫函式庫函式 reboot()。reboot() 再執行系統呼叫進到 kernel 執行 sys_reboot() ,去做相關動作。

Signal的表格如下所示:


signal to kill initreboot() 參數kernel動作
haltSIGUSR1RB_HALT_SYSTEM (0xcdef0123)kernel_halt()停止所有程式
poweroffSIGUSR2RB_POWER_OFF (0x4321fedc)kernel_power_off()停止所有程式後關電 (關電未必有實作)
rebootSIGTERMRB_AUTOBOOT (0x01234567)kernel_restart()停止所有程式後再開機


與busybox的道理相同,假設我在driver內寫一個 kill_cad_pid(SIGTERM, 1);
接下來就會執行reboot()的動作

假設我是寫 kill_cad_pid(SIGUSR2, 1);
則接下來會進入poweroff的狀態

給不同的Signal會有不同的結果!



如果下這三個busybox指令的其中一個,再加上 -f 參數的話,就直接執行 reboot(),不做 inittab 的 shutdown 動作。

2017年2月21日 星期二

Init Script for OpenWrt,新增一個自己的init script

在OpenWrt的系統中,一開機會被執行的Script都會放在/etc/init.d/ 裡面,而當系統開機過程當中,會先去檢查/etc/rc.d/ 裡面,裡面便是告訴系統要做哪些process,而/etc/rc.d/ 裡的檔案都只是個link檔,連結到../init.d/中的process,從/etc/rc.d/裡面的link我們可以知道有哪些開機script會去做

接著我們來實作一個每隔20秒就印出一個 "Hello"的程式(Daemon?)

首先我們要實作的這個題目,我們無法將while loop寫在開機叫起來的這個script裡面,我嘗試過這樣寫,但是開機過程會導致系統開不到console的部分。




所以我們分程兩部分寫:
1. 先寫一個開機script叫做Hello_script.sh,我們在這個script再去呼叫另外一個有while loop 的Hello.sh

2. Hello.sh就負責每20秒印一次Hello訊息到console




以下是實際的範例:(注意第一行一定要有,才有辦法在OpenWrt裡順利執行)
==================  Hello_script.sh =====================
#!/bin/sh /etc/rc.common

START=99
STOP=99

start()
{
          /sbin/Hello.sh &
}

stop()
{
         killall Hello.sh
         echo "===== stop Hello.sh ======"
}
========================================================

講解:
首先當你寫完此檔案後,可以先執行 ./etc/init.d/Hello_scrip.sh enable(如果沒辦法做start請先確定上面範例的第一行程式碼有寫 /etc/rc.common嗎)
這樣表示告訴OpenWrt下次開機就要自動執行此script,當enable完之後,你也可以發現在/etc/rc.d/裡面產生了一個link檔叫做K99Hello_script.sh

OpenWrt 每支 Init Script 都有下列指令可以使用:

/etc/init.d/Hello_script.sh
/etc/init.d/Hello_script.sh  enable //在開機時就自動執行
/etc/init.d/Hello_script.sh  boot
/etc/init.d/Hello_script.sh  start //啟動Hello_script.sh
/etc/init.d/Hello_script.sh  restart //重新啟動
/etc/init.d/Hello_script.sh  stop //停止
/etc/init.d/Hello_script.sh  disable //取消開機時就自動執行

而START=99 意思是給此script在開機時的優先順序,99是一個最低的優先權限,STOP也是相同的意思


另外一個Hello.sh內容如下:
====================== Hello.sh ======================
#!/bin/sh
        while [ 1 ]
        do
             echo "Hello!!"
             sleep 20
        done
=====================================================


這樣就可以讓OpenWrt開機時,自動去做你寫的Daemon了~


此印訊息的範例似乎舉例的不是很好,實際執行起來terminal看不到訊息,但是我們用ps -aux可以確實知道我們的Hello.sh Daemon是一直在執行的!!!




2017年2月14日 星期二

openssl 1.1.0d版本 交叉編譯成arm的

今天剛好需要做到,先在此做個紀錄

1.首先要做config的動作,指令如下:
./config no-asm shared --prefix=/home/danny/extern_lib

prefix的路徑,就看你到時候做make install的時候,要安裝在哪個資料夾

2.手動修改Makefile,config之後進入Makefile會發現CROSS_COMPILE參數是空的,我們手動加入自己的toolchain,ex: CROSS_COMPILE=/home/danny/toolchain/bin/arm-linux-gnueabihf-

3.make
   make install

ps.
1. 我make的時候會遇到 "-m64"的錯誤訊息,目前是到Makefile把"-m64"的參數都先刪除,就       可以順利編譯完成

2. 若是遇到這樣的錯誤訊息 ==> openssl libcrypto.so: undefined reference to `getcontext' ...
     請在第一步驟再加個參數 "no-async" 就可以編譯完成!