內核當然可用用c++來些,但是相對c來說有許多要注意的地方,我們不得不關掉一些特性。這里只提一些gcc相關的內容,Microsoft VC我沒有嘗試。轉載請注明原創:天衣有縫(http://www.shnenglu.com/jinglexy),MSN: jinglexy at yahoo dot com dot cn
1)g++選項-nostartfiles:用戶環境的在main之前調用的代碼,當然不能使用了
2)全局對象:每種類型都有自己的構造函數,如果不自己編寫代碼調用,它們不會執行。
這包括所有全局對象和局部static對象,建議的做法是在內核棧建立后,c++運行代碼執行之前
調用構造函數,如果構造函數沒有運行(假設里面有分配內存之類的操作),后果很嚴重:)
我們可以這樣做:
先修改gnu-ld鏈接腳本
.data : {
start_ctors = .;
*(.ctor*)
end_ctors = .;
start_dtors = .;
*(.dtor*)
end_dtors = .;
*(.data)
}
這樣構造函數的指針就都保存在start_ctors 和end_ctors之間的內存中了,構造函數其實就是void foo(void);形式的函數,編寫一個for循環
調用它即可;析構函數也是一樣的。當每個構造函數調用完后,gcc會自動調用一個函數:
int __cxa_atexit(void (* f)(void *), void *p, void *d);
當內核退出時,會執行一個函數:
void __cxa_finalize(void *d);
這兩個函數必須按上面格式定義,g++是這樣規定的。看看下面的代碼就明白了:
extern "C"
{
int __cxa_atexit(void (*f)(void *), void *p, void *d);
void __cxa_finalize(void *d);
};
void *__dso_handle; /*only the address of this symbol is taken by gcc*/
struct object
{
void (*f)(void*);
void *p;
void *d;
} object[32] = {0};
unsigned int iObject = 0;
int __cxa_atexit(void (*f)(void *), void *p, void *d)
{
if (iObject >= 32) return -1;
object[iObject].f = f;
object[iObject].p = p;
object[iObject].d = d;
++iObject;
return 0;
}
/* This currently destroys all objects */
void __cxa_finalize(void *d)
{
unsigned int i = iObject;
for (; i > 0; --i)
{
--iObject;
object[iObject].f(object[iObject].p);
}
}
3)new和delete:在完成內存管理后,重載類的new和delete函數
4)-nostdlib:把標準庫禁用掉,最近有了移植stl到內核的想法
5)RTTI:最好是禁止它,這樣不能用typeid 和 dynamic_cast了
6)禁用異常:-fno-exceptions,這個和操作系統太緊密了
7)純虛函數,如果子類沒有實現父類中的純虛函數,鏈接到下面默認例程:
extern "C" void __cxa_pure_virtual()
{
// print error message
}
雖然不是為了定義純虛類的對象,但是鏈接時編譯器會抱怨,所以定義上面函數使編譯通過。
8)如果一定要使用異常,rtti,new/delete,gcc中提供了靜態庫:libgcc/libsupc++,
還得寫這個庫的一些基礎函數,覺得它應該是在上層抽象出接口,將底層實現空出來給用戶實現。
而且代碼本身非常復雜,網絡上也沒有任何中文資料。
指令:
readelf -a `gcc -print-libgcc-file-name`
里面定義了很多的函數。
1)g++選項-nostartfiles:用戶環境的在main之前調用的代碼,當然不能使用了
2)全局對象:每種類型都有自己的構造函數,如果不自己編寫代碼調用,它們不會執行。
這包括所有全局對象和局部static對象,建議的做法是在內核棧建立后,c++運行代碼執行之前
調用構造函數,如果構造函數沒有運行(假設里面有分配內存之類的操作),后果很嚴重:)
我們可以這樣做:
先修改gnu-ld鏈接腳本
.data : {
start_ctors = .;
*(.ctor*)
end_ctors = .;
start_dtors = .;
*(.dtor*)
end_dtors = .;
*(.data)
}
這樣構造函數的指針就都保存在start_ctors 和end_ctors之間的內存中了,構造函數其實就是void foo(void);形式的函數,編寫一個for循環
調用它即可;析構函數也是一樣的。當每個構造函數調用完后,gcc會自動調用一個函數:
int __cxa_atexit(void (* f)(void *), void *p, void *d);
當內核退出時,會執行一個函數:
void __cxa_finalize(void *d);
這兩個函數必須按上面格式定義,g++是這樣規定的。看看下面的代碼就明白了:
extern "C"
{
int __cxa_atexit(void (*f)(void *), void *p, void *d);
void __cxa_finalize(void *d);
};
void *__dso_handle; /*only the address of this symbol is taken by gcc*/
struct object
{
void (*f)(void*);
void *p;
void *d;
} object[32] = {0};
unsigned int iObject = 0;
int __cxa_atexit(void (*f)(void *), void *p, void *d)
{
if (iObject >= 32) return -1;
object[iObject].f = f;
object[iObject].p = p;
object[iObject].d = d;
++iObject;
return 0;
}
/* This currently destroys all objects */
void __cxa_finalize(void *d)
{
unsigned int i = iObject;
for (; i > 0; --i)
{
--iObject;
object[iObject].f(object[iObject].p);
}
}
3)new和delete:在完成內存管理后,重載類的new和delete函數
4)-nostdlib:把標準庫禁用掉,最近有了移植stl到內核的想法
5)RTTI:最好是禁止它,這樣不能用typeid 和 dynamic_cast了
6)禁用異常:-fno-exceptions,這個和操作系統太緊密了
7)純虛函數,如果子類沒有實現父類中的純虛函數,鏈接到下面默認例程:
extern "C" void __cxa_pure_virtual()
{
// print error message
}
雖然不是為了定義純虛類的對象,但是鏈接時編譯器會抱怨,所以定義上面函數使編譯通過。
8)如果一定要使用異常,rtti,new/delete,gcc中提供了靜態庫:libgcc/libsupc++,
還得寫這個庫的一些基礎函數,覺得它應該是在上層抽象出接口,將底層實現空出來給用戶實現。
而且代碼本身非常復雜,網絡上也沒有任何中文資料。
指令:
readelf -a `gcc -print-libgcc-file-name`
里面定義了很多的函數。