摘要:
Linux系統區分32/64位,相應地,應用程序、共享庫和內核模塊也區分32/64位。
本文以Ubuntu系統爲例,介紹如何編譯和使用32/64位的應用程序、共享庫和內核模塊。html
要點:linux
在64位系統上,gcc默認編譯成64位程序,但能夠編譯32位程序,須要安裝32位庫。shell
安裝32位庫 :sudo apt-get install lib32readline-gplv2-dev
編譯32位程序:gcc -m32 t1.c編程
在32位系統上,gcc默認編譯32位程序,但能夠編譯64位程序,須要安裝64位庫。數據結構
安裝64位庫:sudo apt-get install lib64readline-gplv2-dev
編譯64位程序:gcc -m64 t1.capp
要點:函數
參考:http://blog.csdn.net/wsl888444/article/details/8289056ui
// test.h 共享庫接口文件 #ifndef _TEST_H_ #define _TEST_H_ void test( int x ); #endif
// test.c 共享庫實現文件 #include <stdio.h> void test( int x ) { printf( "hello, I'm libtest.so %d\n", x ); return; }
gcc test.c -fPIC -shared -o libtest.so [ -m32 | -m64 ].net
選項 | 說明 |
---|---|
-fPIC | 表示編譯爲位置獨立的代碼。若是不用此選項,編譯後的代碼是位置相關的,則動態載入時是經過代碼拷貝的方式來知足不一樣進程的須要,不能達到代碼段共享的目的。 |
-shared | 表示編譯成共享庫 |
-m32 或 -m64 | 選擇編譯32位或64位共享庫 |
// t1.c 應用程序 #include "test.h" int main( ) { test( 99 ); return 0; }
編譯程序:
gcc t1.c -L . -l test -o t1 [ -m32 | -m64 ]調試
選項 | 說明 |
---|---|
-L | 指明共享庫所在的目錄 |
-l | 指明共享庫的名稱,該名稱是處在頭lib 和後綴.so 中的名稱。例如上面共享庫libtest.so,則參數爲 -l test 。 |
查看應用程序依賴的共享庫: ldd t1
查看目標文件中定義的符號: nm t1
執行程序:
// t2.c 應用程序 int main( ) { void *handle = NULL; void (*test)( int x ); handle = dlopen( "./libtest.so", RTLD_LAZY ); if ( handle == NULL ) { printf( "dll loading error.\n" ); return 0; } test = ( void(*)( int ) )dlsym( handle, "test" ); if ( test == NULL ) { printf( "%s: dlsym: '%s'\n", "test", dlerror() ); return 0; } test( 99 ); return 0; }
編譯程序:
gcc -ldl test1.c –o test [ -m32 | -m64 ]
選項 | 說明 |
---|---|
-ldl | 使用共享庫相關函數需使用該參數 |
程序說明:
使用C++時,so的頭文件中聲明前要加 extern "C",才能正確獲取函數地址。不然,在dlsym可能產生錯誤:找不到函數(undefined symbol)。 test.h 中聲明如: extern "C" void test( int x );
問題: 32/64位應用程序是否能夠調用64/32位共享庫?
回答: 64位應用程序只能調用64位共享庫,32位應用程序只能調用32位共享庫。
參考:
http://cboard.cprogramming.com/linux-programming/113856-loading-32-bit-library-into-64-bit-linux-program.html
http://stackoverflow.com/questions/10039401/use-32bit-shared-library-from-64bit-application
解決方法:
要點:
參考:http://blog.csdn.net/gavin_dinggengjia/article/details/6307080
內核模塊的全稱是 Loadable Kernel Module(LKM, 動態可加載內核模塊),它是Linux內核向外部提供的一個插口。
內核模塊是具備獨立功能的程序,一般由一組函數和數據結構組成,用來實現一種文件系統、一個驅動程序,或其它內核上層的功能。
內核模塊能夠被單獨編譯,運行時被連接到內核,做爲內核的一部分在內核空間運行,這與運行在用戶空間的進程不一樣。下表比較了應用程序與內核模塊的差異。
項目 | C語言應用程序 | 內核模塊 |
---|---|---|
使用函數 | libc庫 | 內核函數 |
運行空間 | 用戶空間 | 內核空間 |
入口函數 | main() | module_init() |
出口函數 | exit() | module_exit() |
編譯 | gcc -c | Makefile |
鏈接 | gcc | insmod |
運行 | 直接運行 | insmod |
調試 | gdb | kdbug、kdb、kgdb |
從表中能夠看出,內核模塊不能調用 libc 庫函數,它運行在內核空間,只有超級用戶能夠對其運行。
另外,內核模塊程序必須經過 module_init() 和 module_exit() 函數來告訴內核「我來了」和「我走了」。
內核模塊和內核都在內核空間運行,內核模塊編程在必定意義上說就是內核編程。
由於內核版本的每次變化,其中的某些函數名也會相應地發生變化,所以內核模塊編程與內核版本密切相關。
1. 內核模塊代碼
// hello.c 內核模塊代碼 #include "linux/init.h" // 包含宏_init和_exit #include "linux/kernel.h" // 包含經常使用的內核函數 #include "linux/module.h" // 全部模塊都要用到 static int __init hello_init( void ) { printk( KERN_ALERT "Hello world!\n" ); return 0; } static void __exit hello_exit( void ) { printk(KERN_ALERT "Goodbye!\n"); } module_init( hello_init ); module_exit( hello_exit ); MODULE_LICENSE( "GPL" ); MODULE_AUTHOR( "ddk" ); MODULE_DESCRIPTION( "hello" );
函數 | 說明 |
---|---|
module_init | 內核模塊初始化的入口點 |
module_exit | 註銷由內核模塊提供的全部功能 |
hello_init | 內核模塊初始化函數 |
hello_exit | 內核模塊的退出清理函數,可作終止該內核模塊相關的清理工做。 |
printk | 內核定義的函數,功能與printf相似,printk把要打印的信息輸出到終端或系統日誌。 |
2. Makefile
# Makefile obj-m:=hello.o KERNELBUILD:=/lib/modules/$(shell uname -r)/build default: make -C $(KERNELBUILD) M=$(shell pwd) modules clean: rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions
選項 | 說明 |
---|---|
obj-m | 決定過了內核模塊的名稱和生成的ko文件名 |
KERNELBUILD | 編譯內核模塊須要的內核源代碼文件目錄 |
M | 內核模塊代碼目錄 |
make -C ...... | 編譯內核模塊。-C 將工做目錄轉到KERNELBUILD,調用該目錄下的Makefile,並向這個Makefile傳遞參數M的值是$(shell pwd) modules。 |
關於KERNELBUILD的說明:
(1)若是爲Linux發行版(例如Ubuntu、CentOS等)編譯內核模塊,則能夠直接從發行版目錄 /usr/src 中獲取這個目錄,通常是 /usr/src/linux-headers-* 。這個目錄區分32位和64位。
(2)若是爲Linux標準內核(從 https://www.kernel.org/pub/linux/kernel/ 獲取)編譯內核模塊,則須要先編譯Linux標準內核,而後使用 /usr/src 中的編譯後對應目錄。Linux標準內核源代碼不區分32位和64位,但編譯時區分編譯爲32位或64位。編譯和安裝Linux標準內核,請參考:http://blog.csdn.net/ddk3001/article/details/50276347
(3)編譯出的內核模塊ko是32位或64位由下面決定:一、使用的內核源代碼目錄是32位或64位;二、編譯時make命令中指定 ARCH=i386 或 ARCH=x86_64 。
(4)cat hello.ko能夠查看內核模塊要在哪一個Linux內核版本中運行。
3. 編譯和使用內核模塊
功能 | 命令 | 說明 |
---|---|---|
編譯模塊 | make | 執行第一個目標default,生成hello.ko,這個就是咱們須要的內核模塊。 |
編譯清理 | make clean | 清理編譯產生的文件,hello.ko 也會清理掉。 |
插入模塊 | sudo insmod ./hello.ko | 用dmesg就能夠看到產生的內核信息,Hello world! 內核消息也會輸出到日誌文件/var/log/kern.log中。 |
卸載模塊 | sudo rmmod hello | 用dmesg能夠看到Goodbye! |
modutils是管理內核模塊的一個軟件包,安裝後在/sbin目錄下就會有insomod、rmmod、lsmod等實用程序。一般在加載Linux內核時,modutils已經被載入。
命令 | 說明 | |
---|---|---|
insmod | 調用insmod程序把須要插入的模塊以目標代碼的形式插入到內核中。在插入的時候,insmod自動調用module_init()函數運行。 | |
rmmod | 調用rmmod程序將已經插入內核的模塊從內核中移出。rmmod會自動運行module_exit()函數。 | |
lsmod | 調用lsmod程序將顯示當前系統中正在使用的模塊信息。實際上這個程序的功能就是讀取/proc/modules中的信息。 | |
modinfo | 顯示一個模塊的相關信息。 | |
depmod | 生成可載入模塊的依賴性文件,供modprobe在安裝模塊時使用。 | |
modprobe | modprobe 和 insmod 都是載入內核模塊,差異是 modprobe 可以自動處理模塊載入的依賴問題。modprobe 使用depmod生成的依賴性文件,從預約義目錄樹的一套模塊中自動載入相關模塊。 |