歡迎您光臨本站 註冊首頁

Unix編程/應用問答中文版 ---8.Solaris內核編程相關問題

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0
本文出自:http://www.nsfocus.com 維護:小四

8. Solaris內核編程相關問題
8.1 Solaris內核模塊中如何getcwd
8.2
8.3 如何避免一個套接字進入TIME_WAIT狀態
8.4 結構在優化編譯中的對齊問題
8.5
8.6 如何得到非局部變數列表
8.7
8.8 如何單獨獲得Solaris編譯環境
8.9 如何獲取Solaris內核可調參數列表
8.10
8.11 如何頁邊界對齊式分配內存
8.12
8.13 compile()和step()怎麼用

--------------------------------------------------------------------------

8. Solaris內核編程相關問題

8.1 Solaris內核模塊中如何getcwd

Q: 在Solaris 7 64-bit內核模塊中如何獲知一個進程的當前工作目錄(cwd),getcwd
並不是一個系統調用

A: Rich Teer

最好通過u->u_cdir獲取當前工作目錄(cwd)的vnode(v節點).但這依賴於內核當前上
下文,curproc可能並不對應你期望的進程.

/usr/include/sys/user.h

typedef struct user
{
... ...
/*
* protected by p_lock
*/
struct vnode * u_cdir; /* current directory */
struct vnode * u_rdir; /* root directory */

8.3 如何避免一個套接字進入TIME_WAIT狀態

Q: 我正在寫一個unix server程序,不是daemon,經常需要在命令行上重啟它,絕大
多數時候工作正常,但是某些時候會報告"bind: address in use",於是重啟失
敗.

A: Andrew Gierth
server程序總是應該在調用bind()之前設置SO_REUSEADDR套接字選項.至於
TIME_WAIT狀態,你無法避免,那是TCP協議的一部分.

Q: 如何避免等待60秒之後才能重啟服務

A: Erik Max Francis

使用setsockopt,比如

--------------------------------------------------------------------------


int option = 1;

if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof( option ) ) < 0 )
{
die( "setsockopt" );
}
--------------------------------------------------------------------------

Q: 編寫 TCP/SOCK_STREAM 服務程序時,SO_REUSEADDR到底什麼意思?

A: 這個套接字選項通知內核,如果埠忙,但TCP狀態位於 TIME_WAIT ,可以重用
埠.如果埠忙,而TCP狀態位於其他狀態,重用埠時依舊得到一個錯誤信息,
指明"地址已經使用中".如果你的服務程序停止后想立即重啟,而新套接字依舊
使用同一埠,此時 SO_REUSEADDR 選項非常有用.必須意識到,此時任何非期
望數據到達,都可能導致服務程序反應混亂,不過這只是一種可能,事實上很不
可能.

一個套接字由相關五元組構成,協議、本地地址、本地埠、遠程地址、遠程端
口.SO_REUSEADDR 僅僅表示可以重用本地本地地址、本地埠,整個相關五元組
還是唯一確定的.,重啟后的服務程序有可能收到非期望數據.必須慎重使
用 SO_REUSEADDR 選項.

Q: 在客戶機/伺服器編程中(TCP/SOCK_STREAM),如何理解TCP自動機 TIME_WAIT 狀
態?

A: W. Richard Stevens <1999年逝世,享年49歲>

下面我來解釋一下 TIME_WAIT 狀態,這些在<>
中2.6節解釋很清楚了.

MSL(最大分段生存期)指明TCP報文在Internet上最長生存時間,每個具體的TCP實現
都必須選擇一個確定的MSL值.RFC 1122建議是2分鐘,但BSD傳統實現採用了30秒.

TIME_WAIT 狀態最大保持時間是2 * MSL,也就是1-4分鐘.

IP頭部有一個TTL,最大值255.儘管TTL的單位不是秒(根本和時間無關),我們仍需


假設,TTL為255的TCP報文在Internet上生存時間不能超過MSL.

TCP報文在傳送過程中可能路由故障被迫緩衝延遲、選擇非最優路徑等等,結果
發送方TCP機制開始超時重傳.前一個TCP報文可以稱為"漫遊TCP重複報文",后一個
TCP報文可以稱為"超時重傳TCP重複報文",作為面向連接的可靠協議,TCP實現必須
正確處理這種重複報文,二者可能最終都到達.

一個通常的TCP連接終止可以用圖描述如下:

client server
FIN M
close --------------- (被動關閉)
ACK M 1
<-----------------
FIN N
<----------------- close
ACK N 1
---------------

為什麼需要 TIME_WAIT 狀態?

假設最終的ACK丟失,server將重發FIN,client必須維護TCP狀態信息以便可以重發
最終的ACK,否則會發送RST,結果server認為發生錯誤.TCP實現必須可靠地終止連
接的兩個方向(全雙工關閉),client必須進入 TIME_WAIT 狀態,client可能面
臨重發最終ACK的情形.

{
scz 2001-08-31 13:28

先調用close()的一方會進入TIME_WAIT狀態
}

此外,考慮一種情況,TCP實現可能面臨先後兩個同樣的相關五元組.如果前一個連
接處在 TIME_WAIT 狀態,而允許另一個擁有相同相關五元組的連接出現,可能處理
TCP報文時,兩個連接互相干擾.使用 SO_REUSEADDR 選項就需要考慮這種情況.

為什麼 TIME_WAIT 狀態需要保持 2MSL 這麼長的時間?

如果 TIME_WAIT 狀態保持時間不足夠長(比如小於2MSL),第一個連接就正常終止了.
第二個擁有相同相關五元組的連接出現,而第一個連接的重複報文到達,干擾了第二
個連接.TCP實現必須防止某個連接的重複報文在連接終止后出現,讓TIME_WAIT


狀態保持時間足夠長(2MSL),連接相應方向上的TCP報文要麼完全響應完畢,要麼被
丟棄.建立第二個連接的時候,不會混淆.

A: 小四

在Solaris 7下有內核參數對應 TIME_WAIT 狀態保持時間

# ndd -get /dev/tcp tcp_time_wait_interval
240000
# ndd -set /dev/tcp tcp_time_wait_interval 1000

預設設置是240000ms,也就是4分鐘.如果用ndd修改這個值,最小隻能設置到1000ms,
也就是1秒.顯然內核做了限制,需要Kernel Hacking.

# echo "tcp_param_arr/W 0t0" | adb -kw /dev/ksyms /dev/mem
physmem 3b72
tcp_param_arr: 0x3e8 = 0x0
# ndd -set /dev/tcp tcp_time_wait_interval 0

我不知道這樣做有什麼災難性後果,參看<>的聲明.

Q: TIME_WAIT 狀態保持時間為0會有什麼災難性後果?在普遍的現實應用中,好象也
就是伺服器不穩定點,不見得有什麼災難性後果吧?

D: [email protected]

Linux 內核源碼 /usr/src/linux/include/net/tcp.h 中

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully
* close the socket, about 60 seconds */

最好不要改為0,改成1.埠分配是從上一次分配的埠號 1開始分配的,一般
不會有什麼問題.埠分配演算法在tcp_ipv4.c中tcp_v4_get_port中.

8.4 結構在優化編譯中的對齊問題

Q: 我正在寫一個流模塊,其中用到了#pragma pack(),當使用

gcc -D_KERNEL -c abc.c
ld -r -o abc abc.o

編譯鏈接時,一切正常.為了獲得64-bit模塊,我必須使用Sun Workshop 5.0,
結果導致系統崩潰.訪問

http://docs.sun.com/htmlcoll/coll.32.8/iso-8859-1/CPPPG/Pragmas.html#15434

上面說必須在編譯鏈接應用程序的時候指定"-misalign",我用了如下命令編譯



/opt/SUNWspro/bin/cc -D_KERNEL -misalign -c abc.c
/usr/ccs/bin/ld -r -o abc abc.o

但是我不知道該如何在鏈接時指定"-misalign".使用的是"/usr/ccs/bin/ld".

A: Casper H.S. Dik - Network Security Engineer

"-misalign"僅僅用於應用程序,無法應用到內核編程中."-misalign"是的編譯
獲得的代碼增加了一些runtime glue,它們將指示內核模擬unaligned load(慢).
作為內核編程,沒有等效技術.

Q: 使用#pragma pack()是需要讀取來自Windows客戶端的報文,對端使用
#pragma pack(1)壓縮了所使用的數據結構

#pragma pack(1)
typedef struct pkt_hdr_struct
{
uint8_t pkt_ver;
uint32_t pkt_type;
uint32_t pkt_len;
} pkt_hdr_t;
#pragma pack()

為了採用這個結構讀取網路數據,Solaris端的服務程序需要強制轉換匹配該結構,
但是一旦企圖讀取緊接在pkt_ver成員之後的pkt_type成員,崩潰了.嘗試過其他
辦法,用一個字元指針讀取第一個位元組,然後指針增一,把該指針強制類型
轉換成( uint32_t * ),然後讀取數據,依然崩潰.

此外,是否意味著無法在內核模塊編程中使用#pragma pack()

A: Ed L Cashin

我想你可以單獨寫一個pkt_header_read()函數,單位元組讀取然後拼裝成相應的數
據類型.如果你想避免函數調用,可以使用"inline"關鍵字.

A: Casper H.S. Dik - Network Security Engineer

你是否意識到pkt_hdr_t結構是的你必須自己轉換位元組序(對端是x86平台)
我不認為#pragma pack()是最好的解決辦法,考慮定義如下結構

struct phs
{
char ver;
char type[4];


char len[4];
}

採用memcpy()讀取數據

memcpy( &phs.type[0], &pkt.pkt_type, 4 );

A: Andrew Gabriel

採用字元指針是正確的,但是你犯了個錯誤,編寫如下函數

int read_misaligned_int ( int * iptr )
{
int i;
int value;
char * ptr = ( char * )iptr;
char * vptr = ( char * )&value;

for ( i = 0; i < sizeof( int ); i )
{
*vptr = *ptr ;
}

return( value );
}

此外,既然你提到對端是x86平台,可能還需要考慮位元組序轉換的問題

A: W. Richard Stevens <1999年逝世,享年49歲>

/*
* return value:
* 1 big-endian
* 2 little-endian
* 3 unknow
* 4 sizeof( short ) != 2
*/
static int byte_order ( void )
{
union
{
short s;
char c[ sizeof( short ) ];
} un;

un.s = 0x0201;
if ( 2 == sizeof( short ) )
{
if ( ( 2 == un.c[0] ) && ( 1 == un.c[1] ) )
{
puts( "big-endian" );
return( 1 );
}
else if ( ( 1 == un.c[0] ) && ( 2 == un.c[1] ) )
{
puts( "little-endian" );
return( 2 );
}
else
{
puts( "unknow" );
return( 3 );
}
}
else
{
puts( "sizeof( short ) = %d", sizeof( short ) );
return( 4 );
}
return( 3 );
} /* end of byte_order */

D: CERNET 華中地區網路中心 程序設計版 集體討論匯總

為了解決Unix自定義結構在GCC優化編譯中對齊問題,一般解決辦法是用如下宏封裝
自定義結構

#pragma pack(1)



struct my_arphdr
{
};

#pragma pack()

如果是SPARC/Solaris,還可以這樣

struct my_arphdr
{
} __attribute__ ((packed));

兩種辦法其實都可以用在Unix系統/GCC編譯器中.

D: [email protected]

關於結構中位元組對齊問題,相應編譯器選項為

GCC/G : -fpack-struct
Sun Workshop cc/CC: -misalign

最好不這樣做,會大大降低程序效率,特別在某些架構中.應該嘗試用位操作來處理.

D: [email protected]

GCC可以這麼解決

#ifdef __GCC__
#define PACKED __attribute__((__packed__))
#else
#define PACKED
#endif

struct msg
{
u_int16_t PACKED first;
...
};

還是 VC 簡單,#include 就搞定了

A: gfh_nuaa

DEC : #pragma pack(1)
SUN : #pragma pack(1)
AIX : 編譯時 -q align=packed
HP-UX : #pragma pack 1

D: Joe Durusau

在 Visual C 中,使用 "-ZP1" 就可以讓編譯器對自定義結構進行單位元組對齊,實
際就是取消了對齊優化.

A: [email protected] 2001-12-20 13:09

1) 結構內部成員的pack

struct foo
{
char a;
int b __attribute__ ((packed));
};

2) 整個結構的pack

struct foo
{
char a;
int b;
}__attribute__ ((packed));

3) 文件範圍的pack

#pragma pack(1)

struct foo
{
char a;
int b;
};

... ...

4) 編譯選項的pack

-fpack-struct

但這是最危險的做法,這樣做可能會使庫函數和你的程序對結構內成員的偏移理
解不一致.

Q: 小四

#pragma pack(push)
#pragma pack(n)
... ...
#pragma pack(pop)

push/pop這個用法都誰支持啊



A: [email protected]

這個寫法我沒見過,VC和GCC都是這樣寫的

#pragma (push, N) // 把原來align設置壓棧,並設新的pack為N
#pragma (pop) // align設置彈棧

8.6 如何得到非局部變數列表

Q: 什麼工具可以從目標文件中提取非局部變數列表

A: Donald McLachlan

最簡單的就是nm,假設你有一個目標文件(或者已鏈接過的可執行文件),nm -g將顯
示所有"全局"變數.下面是一個Solaris的例子:

--------------------------------------------------------------------------
/* gcc -o junk junk.c */

int var1;
static int var2;

int main ( void )
{
int var3;

return( 0 );
} /* end of main */
--------------------------------------------------------------------------

$ nm -g junk

junk:

[Index] Value Size Type Bind Other Shndx Name

[66] | 133640| 0|OBJT |GLOB |0 |15 |_DYNAMIC
[61] | 133496| 0|OBJT |GLOB |0 |13 |_GLOBAL_OFFSET_TABLE_
[71] | 133528| 0|OBJT |GLOB |0 |14 |_PROCEDURE_LINKAGE_TABLE_
[69] | 0| 0|NOTY |WEAK |0 |UNDEF |__deregister_frame_info
[60] | 0| 0|NOTY |WEAK |0 |UNDEF |__register_frame_info
[70] | 133836| 0|OBJT |GLOB |0 |19 |_edata
[59] | 133872| 0|OBJT |GLOB |0 |20 |_end
[58] | 133864| 4|OBJT |GLOB |0 |20 |_environ
[72] | 67960| 0|OBJT |GLOB |0 |12 |_etext
[67] | 133600| 0|FUNC |GLOB |0 |UNDEF |_exit
[75] | 67936| 20|FUNC |GLOB |0 |11 |_fini
[64] | 67908| 28|FUNC |GLOB |0 |10 |_init
[73] | 67956| 4|OBJT |GLOB |0 |12 |_lib_version
[57] | 67380| 116|FUNC |GLOB |0 |9 |_start


[62] | 133576| 0|FUNC |GLOB |0 |UNDEF |atexit
[68] | 133864| 4|OBJT |WEAK |0 |20 |environ
[63] | 133588| 0|FUNC |GLOB |0 |UNDEF |exit
[74] | 67784| 24|FUNC |GLOB |0 |9 |main
[65] | 133868| 4|OBJT |GLOB |0 |20 |var1
$

注意到var2這樣的"靜態全局變數",由於僅僅在單個源文件中有效,nm -g並未顯示
它.如果不指定-g選項,將顯示var2(當然會顯示更多垃圾信息).

$ nm junk

junk:

[Index] Value Size Type Bind Other Shndx Name

... ...
[65] | 133868| 4|OBJT |GLOB |0 |20 |var1
[46] | 133860| 4|OBJT |LOCL |0 |20 |var2
$

8.8 如何單獨獲得Solaris編譯環境

Q: 我需要安裝哪些包

A: Seán Boran

需要下列Solaris安裝包:
SUNWbtool、SUNWsprot、SUNWtoo、SUNWhea、SUNWarc、SUNWlibm、SUNWlibms

可以用pkginfo [-l]檢查是否安裝了這些包

$ pkginfo SUNWbtool SUNWsprot SUNWtoo SUNWhea SUNWarc SUNWlibm SUNWlibms
system SUNWarc Archive Libraries
system SUNWbtool CCS tools bundled with SunOS
system SUNWhea SunOS Header Files
system SUNWlibm Sun WorkShop Bundled libm
system SUNWlibms Sun WorkShop Bundled shared libm
system SUNWsprot Solaris Bundled tools
system SUNWtoo Programming Tools
$

可以從Solaris CD中單獨安裝缺少的包(pkgadd)

象make這樣的工具安裝在/usr/ccs/bin,增加到$PATH環境變數中.但是這個make和
某些工具相衝突,比如BIND,此時應該安裝GNU make,確認GNU make的搜索路徑位於
/usr/ccs/bin/make之前.另外,$PATH環境變數中/usr/ccs/bin應該位於/usr/ucb之
前.

8.9 如何獲取Solaris內核可調參數列表



Q: 誰有Solaris內核可調參數列表

A: Andrew Garman

執行

/usr/xpg4/bin/nm /platform/sun4u/kernel/unix | egrep 'OBJT |GLOB' | more

顯示結果中部分為Solaris內核可調參數,另外一些非可調內核參數.可以用ndd獲取、
設置網路相關參數.

D: scz

可以考慮

/usr/ccs/bin/nm -nx /dev/ksyms | egrep 'OBJT |GLOB' | more

不知道二者區別何在?第二個報告內容應該包含了後來動態載入內核模塊輸出的符號,
第一個才對應基本內核輸出的符號.

8.11 如何頁邊界對齊式分配內存

Q: 我希望在頁邊界上分配大塊內存,要求普通用戶、非特權進程亦能使用此技術.
在mmap(2)手冊頁中沒有明確表明返回地址邊界對齊.它提到可以指定起始地址以
保證頁邊界對齊,但沒有說明如果由系統選定起始地址時是否也是頁邊界對齊的.

MAP_ANON並非所有系統都支持,我需要在Solaris 2.x上運行.

A: Andrew Gierth

mmap(2)即可滿足要求.某些系統提供了valloc或者memalign,但它們的實現機制是,
分配超過請求大小的內存,然後調整之,這相當浪費.

mmap(2)應該始終是頁邊界對齊的.

在那些不支持 MAP_ANON 的系統上,打開/dev/zero獲取句柄,傳遞給mmap(2),效果
是一樣的.

mmap(2)的可移植性足夠好,不過"分配超過請求大小的內存並調整之"可能更具有可
移植性.

8.13 compile()和step()怎麼用

Q: 我知道這兩個函數是Solaris對正則表達式的支持函數,可到底怎麼用呢?

A: microcat

--------------------------------------------------------------------------
/* gcc -Wall -O3 -o reg reg.c -lgen */
#include
#include


#include

int main ( int argc, char * argv[] )
{
char * expbuf = NULL;

if ( ( expbuf = compile( argv[1], NULL, NULL ) ) == NULL )
{
exit( EXIT_FAILURE );
}
if ( step( argv[2], expbuf ) )
{
printf( "Match at: %sn", loc1 );
}
else
{
printf( "No match.n" );
}
free( expbuf );
exit( EXIT_SUCCESS );
} /* end of main */
--------------------------------------------------------------------------

$ ./reg '^.*inetd$' '/usr/sbin/inetd'
Match at: /usr/sbin/inetd
$


[火星人 ] Unix編程/應用問答中文版 ---8.Solaris內核編程相關問題已經有465次圍觀

http://coctec.com/docs/linux/show-post-57691.html