2007年7月2日 星期一

[轉]監看網路封包程式設計概說

[本文轉載來源為: http://blog.roodo.com/thinkingmore/archives/554037.html]

監看網路封包程式設計概說.

作者: 邵文琳

前一個禮拜花了不少時間在看網路封包方面的程式設計 ..覺得十分有興趣.
感謝charles 和 ellery 的幫助 . 讓我觀念比較清楚 ...
在這裡我把我這一個星期的學習心得寫下來.
讓大家以後若有機會寫到類似程式時, 可是事半功倍 ..
網路程式設計方面相關資料很多 . 但是都是對比較上層的tcp,或udp的應用在做介紹.
對於網路層和datalink層面則著墨較少 .
所以"對我而言" 學習起來比較困難. 希望寫下這個對大家會有幫助.

一個網路封包監看程式 . 說穿了 只有四個重點:

第一 . 開啟一個可接收raw packet 的socket .
一般我們開始socket接收tcp或udp的packet 時, 我們收到的內容就直接是資料內容 .
kernel 己經幫我們把ethernet header 和 ip 或 arp header 都拿掉了.
但是在寫網路封包程式時, 我們需要header . 因此我們在開啟socket 的時候,就可以針對我們要收集的封包總類做過濾. 要求接收到的資料裡要包涵完整的封包標頭檔...

第二 . 有條件的接收封包和無條件的接收封包.
網路卡只會接收到ethernet header 的target mac 是自己的mac 時, 或接收到TARGET MAC 為 ff:ff:ff:ff:ff 的封包, 或是multicasting 的封包(也就是target ip是224開頭的封包 . 這種情況叫做 nonPromiscuous . 當我們要監看網路封包時,希望收到的不只是broadcast 或是 multicasting 或是自己的封包時, 我就要把網路卡設定成 Promiscuous . 如此一來我們就可以接到實體網路上的所有封包了...

第三 . filter 的設定.
當我們把網路卡設成 Promiscuous , 而且看到了所有封包header 時, 我們就可以依照我們自己的意思去過濾出我們想要的封包 . 可是一個問題來了 .. 網路上的封包那麼多.我們全都要一個個的去把它的標頭打開, 看看是不是自己要看的封包. 這樣做花的時間太長.以致於後面進來的封包都塞在buffer 裡, 等你一個個的檢查..我們需要一個filter 建立在硬體層和聯結層中建立一個filter , 先過濾出我們要的封包,再交由程式處理 . 這就是filter的功用.這種filter 是由bpf code 寫出來的! 不會寫bpf code ? 沒關係. 我們還是有別的方法把它變出來 ,,,,,

第四 . multiplexing I/O (blocking VS noblocking)
就如我們所知道的當我們在receving packet 時. 如果沒有資料進來, 程式就會等在那裡 ,這種情況叫做blocking , 但是有時候我們不想要無止限的等在那裡時, 郤又沒法把break 出來時,怎麼辦呢?使用signal 嗎? 另一種解決方式就是把資料流做unblocking.但unblocking 郤不能完成解決我的需求, 於是我利用到了 multiplexing I/O.接下來我在對以上四項做詳細解釋和範例:

第一 socket .
要如何開一個可以接收raw_data 的socket呢 ?


int sock;

// 開一個接收所有走ip protocol 封包的完整封包
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));

// 開一個接收所有走arp protocol 封包的完整封包
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));

// 開一個接收所有 protocol 封包的完整封包
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_All));

// 在linux 下接收datalink 層的網路封包
sock = socket(PF_INET, SOCK_PACKET, htons(ETH+P_ALL));



PF_PACKET 支援兩個socket type:SOCK_RAW 和 SOCK_DGRAM, SOCK_RAW 會留下完整的 ethernet 標頭.SOCK_DGRAM 會去掉ETHERNET 標頭.但在linux 下使用PF_SOCK , SOCK_RAW 會有問題.最好是使用 PF_INET, SOCKET_PACKET 也可以留下完整的實體層的標頭.

第二 Promiscuous.
網路卡預設當然都是nonPromiscuous , 要變成 Promiscuous 必須對網卡做設定.在socket 己經open 下


#include
#include
struct ifreq ifrq;

strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
ioctl(sock,SIOCGIFFLAGS,&ifrq);
ifrq.ifr_flags|=IFF_PROMISC;
ioctl(sock,SIOCSIFFLAGS,&ifrq);


若在打開之後,沒有在程式結束前關閉. 它就會一直打開哦 . 所以記得要關閉它.



strncpy(ethreq.ifr_name,"eth0",IFNAMSIZ);
ioctl(sock,SIOCGIFFLAGS,&ifrq);
ifrq.ifr_flags&=~IFF_PROMISC;
ioctl(sock,SIOCSIFFLAGS,&ifrq);



第三. 設filter
filter 的設定主要是使用在實體層和連接層中間 . 用一種近似組語的bpf code 組成.寫起來挺麻煩的 . 所以我們直接使用tcpdump -dd 來產生的binary code 來用 .


for example:
tcpdump -dd ip dst 172.16.0.3
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000800 },
{ 0x20, 0, 0, 0x0000001e },
{ 0x15, 0, 1, 0xac100003 },
{ 0x6, 0, 0, 0x00000044 },
{ 0x6, 0, 0, 0x00000000 },
tcpdump -dd ip src 172.16.0.3
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000800 },
{ 0x20, 0, 0, 0x0000001a },
{ 0x15, 0, 1, 0xac100003 },
{ 0x6, 0, 0, 0x00000044 },
{ 0x6, 0, 0, 0x00000000 },
tcpdump -dd arp dst 172.16.0.3
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000806 },
{ 0x2 0, 0, 0, 0x00000026 },
{ 0x15, 0, 1, 0xac100003 },
{ 0x6, 0, 0, 0x00000044 },
{ 0x6, 0, 0, 0x00000000 },


其實這些code 的產生是有跡可找的..我就把下面程式碼的binary code 原始bpf pesude code 列出來,可以相對應比較看看


/*
udp and host 192.168.9.10 and src port 5000
(000) ldh [12]
(001) jeq #0x800 jt 2 jf 14
(002) ldb [23]
(003) jeq #0x11 jt 4 jf 14
(004) ld [26]
(005) jeq #0xc0a8090a jt 8 jf 6
(006) ld [30]
(007) jeq #0xc0a8090a jt 8 jf 14
(008) ldh [20]
(009) jset #0x1fff jt 14 jf 10
(010) ldxb 4*([14]&0xf)
(011) ldh [x + 14]
(012) jeq #0x1388 jt 13 jf 14
(013) ret #68
(014) ret #0
*/



我們可以依我們的需要去下command 給tcpdump -dd 幫我們產生binary code. tcpdump, 不但區分不同的protocol. 來源,目的地, even 標頭的第幾個byte,和要接收封包的大小, 都可以變化. 實在是太神奇了.

接下來我們要把code 整合到程式裡去 ...

第四 . blocking VS noblocking multiplexing I/O
unblocking 最重要的意思是當有資料流進來時立刻就傳回. 當沒有資料流進來時, 也傳回0.於是我們就可以利用unblocking 和loop 來控制我們要的時間.
以下是程式的寫法:


(fcntl(fd ,F_GETFL) & O_NONBLOCKING) // 1 成功 0 失敗



但是把資料一點一滴的傳進來也並不是我們要的 . 我們還是希望傳進來的資料是一個個block 過的資料,只是當沒有資料進來時, 能夠不要永遠的等在那而己.這就是multiplexing I/O 的概念.當我們read 和recvfrom 一個 file handle 時, 我們可以設定如何沒有資料進來時我要等多久,就能回到程式繼續執行我們要做的動作.
以下是程式的寫法:


#include "sys/time.h"
#include "sys/select.h"

int sock;
struct timeval tv;
fd_set readfds;

sock = socket(...);
tv.tv_sec=1;
tv.tv_usec=0;
FD_ZERO(&readfds); // 清空file handle set.
FD_SET(sock,&readfds); // 加入一個file handle 到file handle set
FD_SET(xxx,&readfds); // 如果還有別的file handle 也要加入同一個
set做控制的話 ..
select(sock+1,&readfds,NULL,NULL,&tv); // 對 file handle set 設
定時間控制
if(FD_ISSET(sock,&readfds)){ // 依readfds的設定來看是否為
true , 依我們的
例出來說,程式會在這裡等上一秒鐘. 如果在這一秒裡有data流進來,
傳回true, 再交給recvfrom把data 接下來, 否則一秒鐘後傳回false
!
// recvfrom(....)
}



基本上把握這幾個主要的重點, 要變化出自己的sniffer 不是問題..剩下來比較討厭的就是把ip 轉成network address to host address..大小印地安,和 inetaddr, inet_addr, inet_aton, inet_ntoa ...這些變來變去的死東西了.

2007年6月26日 星期二

[Function]File

clearerr(清除檔案流的錯誤旗標)
相關函數 feof

表頭檔案 #include

定義函數 void clearerr(FILE * stream);

函數說明 clearerr()清除參數stream指定的檔案流所使用的錯誤旗標。

返回值

 




fclose(關閉檔案)
相關函數 close,fflush,fopen,setbuf

表頭檔案 #include

定義函數 int fclose(FILE * stream);

函數說明 fclose()用來關閉先前fopen()打開的檔案。此動作會讓緩沖區內的數據寫入檔案中,並釋放系統所提供的檔案資源。

返回值 若關檔案動作成功則返回0,有錯誤發生時則返回EOF並把錯誤代碼存到errno。

錯誤代碼 EBADF表示參數stream非已打開的檔案。

範例 請參考fopen()。

 




fdopen(將檔案描述詞轉為檔案指標)
相關函數 fopen,open,fclose

表頭檔案 #include

定義函數 FILE * fdopen(int fildes,const char * mode);

函數說明 fdopen()會將參數fildes 的檔案描述詞,轉換為對應的檔案指標後返回。參數mode 字元串則代表著檔案指標的流形態,此形態必須和原先檔案描述詞讀寫模式相同。關於mode 字元串格式請參考fopen()。

返回值 轉換成功時返回指向該流的檔案指標。失敗則返回NULL,並把錯誤代碼存在errno中。

範例 #include
main()
{
FILE * fp =fdopen(0,”w+”);
fprintf(fp,”%s\n”,”hello!”);
fclose(fp);
}

執行 hello!

 




feof(檢查檔案流是否讀到了檔案尾)
相關函數 fopen,fgetc,fgets,fread

表頭檔案 #include

定義函數 int feof(FILE * stream);

函數說明 feof()用來偵測是否讀取到了檔案尾,尾數stream為fopen()所返回之檔案指標。如果已到檔案尾則返回非零值,其他情況返回0。

返回值 返回非零值代表已到達檔案尾。

 




fflush(更新緩沖區)
相關函數 write,fopen,fclose,setbuf

表頭檔案 #include

定義函數 int fflush(FILE* stream);

函數說明 fflush()會強迫將緩沖區內的數據寫回參數stream指定的檔案中。如果參數stream為NULL,fflush()會將所有打開的檔案數據更新。

返回值 成功返回0,失敗返回EOF,錯誤代碼存於errno中。

錯誤代碼 EBADF 參數stream 指定的檔案未被打開,或打開狀態為只讀。其它錯誤代碼參考write()。

 




fgetc(由檔案中讀取一個字符)
相關函數 open,fread,fscanf,getc

表頭檔案 include

定義函數 nt fgetc(FILE * stream);

函數說明 fgetc()從參數stream所指的檔案中讀取一個字符。若讀到檔案尾而無數據時便返回EOF。

返回值 getc()會返回讀取到的字符,若返回EOF則表示到了檔案尾。

範例 #include
main()
{
FILE *fp;
int c;
fp=fopen(“exist”,”r”);
while((c=fgetc(fp))!=EOF)
printf(“%c”,c);
fclose(fp);
}

 




fgets(由檔案中讀取一字元串)
相關函數 open,fread,fscanf,getc

表頭檔案 include

定義函數 har * fgets(char * s,int size,FILE * stream);

函數說明 fgets()用來從參數stream所指的檔案內讀入字符並存到參數s所指的內存空間,直到出現換行字符、讀到檔案尾或是已讀了size-1個字符為止,最後會加上NULL作為字元串結束。

返回值 gets()若成功則返回s指標,返回NULL則表示有錯誤發生。

範例 #include
main()
{
char s[80];
fputs(fgets(s,80,stdin),stdout);
}

執行 this is a test /*輸入*/
this is a test /*輸出*/

 




fileno(返回檔案流所使用的檔案描述詞)
相關函數 open,fopen

表頭檔案 #include

定義函數 int fileno(FILE * stream);

函數說明 fileno()用來取得參數stream指定的檔案流所使用的檔案描述詞。

返回值 返回檔案描述詞。

範例 #include
main()
{
FILE * fp;
int fd;
fp=fopen(“/etc/passwd”,”r”);
fd=fileno(fp);
printf(“fd=%d\n”,fd);
fclose(fp);
}

執行 fd=3

 




fopen(打開檔案)
相關函數 open,fclose

表頭檔案 #include

定義函數 FILE * fopen(const char * path,const char * mode);

函數說明 參數path字元串包含欲打開的檔案路徑及檔案名,參數mode字元串則代表著流形態。
mode有下列幾種形態字元串:
r 打開只讀檔案,該檔案必須存在。
r+ 打開可讀寫的檔案,該檔案必須存在。
w 打開只寫檔案,若檔案存在則檔案長度清為0,即該檔案內容會消失。若檔案不存在則建立該檔案。
w+ 打開可讀寫檔案,若檔案存在則檔案長度清為零,即該檔案內容會消失。若檔案不存在則建立該檔案。
a 以附加的模式打開只寫檔案。若檔案不存在,則會建立該檔案,如果檔案存在,寫入的數據會被加到檔案尾,即檔案原先的內容會被保留。
a+ 以附加模式打開可讀寫的檔案。若檔案不存在,則會建立該檔案,如果檔案存在,寫入的數據會被加到檔案尾後,即檔案原先的內容會被保留。
上述的形態字元串都可以再加一個b字符,如rb、w+b或ab+等組合,加入b 字符用來告訴函數庫打開的檔案為二進製檔案,而非純文字檔案。不過在POSIX系統,包含Linux都會忽略該字符。由fopen()所建立的新檔案會具有S_IRUSRS_IWUSRS_IRGRPS_IWGRPS_IROTHS_IWOTH(0666)權限,此檔案權限也會參考umask值。

返回值 檔案順利打開後,指向該流的檔案指標就會被返回。若果檔案打開失敗則返回NULL,並把錯誤代碼存在errno 中。

附加說明 一般而言,開檔案後會作一些檔案讀取或寫入的動作,若開檔案失敗,接下來的讀寫動作也無法順利進行,所以在fopen()後請作錯誤判斷及處理。

範例 #include
main()
{
FILE * fp;
fp=fopen(“noexist”,”a+”);
if(fp= =NULL) return;
fclose(fp);
}

 




fputc(將一指定字符寫入檔案流中)
相關函數 fopen,fwrite,fscanf,putc

表頭檔案 #include

定義函數 int fputc(int c,FILE * stream);

函數說明 fputc 會將參數c 轉為unsigned char 後寫入參數stream 指定的檔案中。

返回值 fputc()會返回寫入成功的字符,即參數c。若返回EOF則代表寫入失敗。

範例 #include
main()
{
FILE * fp;
char a[26]=”abcdefghijklmnopqrstuvwxyz”;
int i;
fp= fopen(“noexist”,”w”);
for(i=0;i<26;i++)
fputc(a[i],fp);
fclose(fp);
}

 




fputs(將一指定的字元串寫入檔案內)
相關函數 fopen,fwrite,fscanf,fputc,putc

表頭檔案 #include

定義函數 int fputs(const char * s,FILE * stream);

函數說明 fputs()用來將參數s所指的字元串寫入到參數stream所指的檔案內。

返回值 若成功則返回寫出的字符個數,返回EOF則表示有錯誤發生。

範例 請參考fgets()。

 




fread(從檔案流讀取數據)
相關函數 fopen,fwrite,fseek,fscanf

表頭檔案 #include

定義函數 size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream);

函數說明 fread()用來從檔案流中讀取數據。參數stream為已打開的檔案指標,參數ptr 指向欲存放讀取進來的數據空間,讀取的字符數以參數size*nmemb來決定。Fread()會返回實際讀取到的nmemb數目,如果此值比參數nmemb 來得小,則代表可能讀到了檔案尾或有錯誤發生,這時必須用feof()或ferror()來決定發生什麼情況。

返回值 返回實際讀取到的nmemb數目。

附加說明

範例 #include
#define nmemb 3
struct test
{
char name[20];
int size;
}s[nmemb];
main()
{
FILE * stream;
int i;
stream = fopen(“/tmp/fwrite”,”r”);
fread(s,sizeof(struct test),nmemb,stream);
fclose(stream);
for(i=0;iprintf(“name[%d]=%-20s:size[%d]=%d\n”,i,s[i].name,i,s[i].size);
}

執行 name[0]=Linux! size[0]=6
name[1]=FreeBSD! size[1]=8
name[2]=Windows2000 size[2]=11

 




freopen(打開檔案)
相關函數 fopen,fclose

表頭檔案 #include

定義函數 FILE * freopen(const char * path,const char * mode,FILE * stream);

函數說明 參數path字元串包含欲打開的檔案路徑及檔案名,參數mode請參考fopen()說明。參數stream為已打開的檔案指標。Freopen()會將原stream所打開的檔案流關閉,然後打開參數path的檔案。

返回值 檔案順利打開後,指向該流的檔案指標就會被返回。如果檔案打開失敗則返回NULL,並把錯誤代碼存在errno 中。

範例 #include
main()
{
FILE * fp;
fp=fopen(“/etc/passwd”,”r”);
fp=freopen(“/etc/group”,”r”,fp);
fclose(fp);
}

 




fseek(移動檔案流的讀寫位置)
相關函數 rewind,ftell,fgetpos,fsetpos,lseek

表頭檔案 #include

定義函數 int fseek(FILE * stream,long offset,int whence);

函數說明 fseek()用來移動檔案流的讀寫位置。參數stream為已打開的檔案指標,參數offset為根據參數whence來移動讀寫位置的位移數。

參數 whence為下列其中一種:
SEEK_SET從距檔案開頭offset位移量為新的讀寫位置。SEEK_CUR 以目前的讀寫位置往後增加offset個位移量。
SEEK_END將讀寫位置指向檔案尾後再增加offset個位移量。
當whence值為SEEK_CUR 或SEEK_END時,參數offset允許負值的出現。
下列是較特別的使用模式:
1) 欲將讀寫位置移動到檔案開頭時:fseek(FILE *stream,0,SEEK_SET);
2) 欲將讀寫位置移動到檔案尾時:fseek(FILE *stream,0,0SEEK_END);

返回值 當調用成功時則返回0,若有錯誤則返回-1,errno會存放錯誤代碼。

附加說明 fseek()不像lseek()會返回讀寫位置,因此必須使用ftell()來取得目前讀寫的位置。

範例 #include
main()
{
FILE * stream;
long offset;
fpos_t pos;
stream=fopen(“/etc/passwd”,”r”);
fseek(stream,5,SEEK_SET);
printf(“offset=%d\n”,ftell(stream));
rewind(stream);
fgetpos(stream,&pos);
printf(“offset=%d\n”,pos);
pos=10;
fsetpos(stream,&pos);
printf(“offset = %d\n”,ftell(stream));
fclose(stream);
}

執行 offset = 5
offset =0
offset=10

 




ftell(取得檔案流的讀取位置)
相關函數 fseek,rewind,fgetpos,fsetpos

表頭檔案 #include

定義函數 long ftell(FILE * stream);

函數說明 ftell()用來取得檔案流目前的讀寫位置。參數stream為已打開的檔案指標。

返回值 當調用成功時則返回目前的讀寫位置,若有錯誤則返回-1,errno會存放錯誤代碼。

錯誤代碼 EBADF 參數stream無效或可移動讀寫位置的檔案流。

範例 參考fseek()。

 




fwrite(將數據寫至檔案流)
相關函數 fopen,fread,fseek,fscanf

表頭檔案 #include

定義函數 size_t fwrite(const void * ptr,size_t size,size_t nmemb,FILE * stream);

函數說明 fwrite()用來將數據寫入檔案流中。參數stream為已打開的檔案指標,參數ptr 指向欲寫入的數據位址,總共寫入的字符數以參數size*nmemb來決定。Fwrite()會返回實際寫入的nmemb數目。

返回值 返回實際寫入的nmemb數目。

範例 #include
#define set_s (x,y) {strcoy(s[x].name,y);s[x].size=strlen(y);}
#define nmemb 3
struct test
{
char name[20];
int size;
}s[nmemb];
main()
{
FILE * stream;
set_s(0,”Linux!”);
set_s(1,”FreeBSD!”);
set_s(2,”Windows2000.”);
stream=fopen(“/tmp/fwrite”,”w”);
fwrite(s,sizeof(struct test),nmemb,stream);
fclose(stream);
}

執行 參考fread()。

 




getc(由檔案中讀取一個字符)
相關函數 read,fopen,fread,fgetc

表頭檔案 #include

定義函數 int getc(FILE * stream);

函數說明 getc()用來從參數stream所指的檔案中讀取一個字符。若讀到檔案尾而無數據時便返回EOF。雖然getc()與fgetc()作用相同,但getc()為宏定義,非真正的函數調用。

返回值 getc()會返回讀取到的字符,若返回EOF則表示到了檔案尾。

範例 參考fgetc()。

 




getchar(由標準輸入設備內讀進一字符)
相關函數 fopen,fread,fscanf,getc

表頭檔案 #include

定義函數 int getchar(void);

函數說明 getchar()用來從標準輸入設備中讀取一個字符。然後將該字符從unsigned char轉換成int後返回。

返回值 getchar()會返回讀取到的字符,若返回EOF則表示有錯誤發生。

附加說明 getchar()非真正函數,而是getc(stdin)宏定義。

範例 #include
main()
{
FILE * fp;
int c,i;
for(i=0li<5;i++)
{
c=getchar();
putchar(c);
}
}

執行 1234 /*輸入*/
1234 /*輸出*/

 




gets(由標準輸入設備內讀進一字元串)
相關函數 fopen,fread,fscanf,fgets

表頭檔案 #include

定義函數 char * gets(char *s);

函數說明 gets()用來從標準設備讀入字符並存到參數s所指的內存空間,直到出現換行字符或讀到檔案尾為止,最後加上NULL作為字元串結束。

返回值 gets()若成功則返回s指標,返回NULL則表示有錯誤發生。

附加說明 由於gets()無法知道字元串s的大小,必須遇到換行字符或檔案尾才會結束輸入,因此容易造成緩沖溢出的安全性問題。建議使用fgets()取代。

範例 參考fgets()

 




mktemp(產生唯一的臨時檔案名)
相關函數 tmpfile

表頭檔案 #include

定義函數 char * mktemp(char * template);

函數說明 mktemp()用來產生唯一的臨時檔案名。參數template所指的檔案名稱字元串中最後六個字符必須是XXXXXX。產生後的檔案名會借字元串指標返回。

返回值 檔案順利打開後,指向該流的檔案指標就會被返回。如果檔案打開失敗則返回NULL,並把錯誤代碼存在errno中。

附加說明 參數template所指的檔案名稱字元串必須聲明為數組,如:
char template[ ]=”template-XXXXXX”;
不可用char * template=”template-XXXXXX”;

範例 #include
main()
{
char template[ ]=”template-XXXXXX”;
mktemp(template);
printf(“template=%s\n”,template);
}

 




putc(將一指定字符寫入檔案中)
相關函數 fopen,fwrite,fscanf,fputc

表頭檔案 #include

定義函數 int putc(int c,FILE * stream);

函數說明 putc()會將參數c轉為unsigned char後寫入參數stream指定的檔案中。雖然putc()與fputc()作用相同,但putc()為宏定義,非真正的函數調用。

返回值 putc()會返回寫入成功的字符,即參數c。若返回EOF則代表寫入失敗。

範例 參考fputc()。

 




putchar(將指定的字符寫到標準輸出設備)
相關函數 fopen,fwrite,fscanf,fputc

表頭檔案 #include

定義函數 int putchar (int c);

函數說明 putchar()用來將參數c字符寫到標準輸出設備。

返回值 putchar()會返回輸出成功的字符,即參數c。若返回EOF則代表輸出失敗。

附加說明 putchar()非真正函數,而是putc(c,stdout)宏定義。

範例 參考getchar()。

 




rewind(重設檔案流的讀寫位置為檔案開頭)
相關函數 fseek,ftell,fgetpos,fsetpos

表頭檔案 #include

定義函數 void rewind(FILE * stream);

函數說明 rewind()用來把檔案流的讀寫位置移至檔案開頭。參數stream為已打開的檔案指標。此函數相當於調用fseek(stream,0,SEEK_SET)。

返回值

範例 參考fseek()

 




setbuf(設定檔案流的緩沖區)
相關函數 setbuffer,setlinebuf,setvbuf

表頭檔案 #include

定義函數 void setbuf(FILE * stream,char * buf);

函數說明 在打開檔案流後,讀取內容之前,調用setbuf()可以用來設定檔案流的緩沖區。參數stream為指定的檔案流,參數buf指向自定的緩沖區起始位址。如果參數buf為NULL指標,則為無緩沖IO。Setbuf()相當於調用:setvbuf(stream,buf,buf?_IOFBF:_IONBF,BUFSIZ)

返回值

 




setbuffer(設定檔案流的緩沖區)
相關函數 setlinebuf,setbuf,setvbuf

表頭檔案 #include

定義函數 void setbuffer(FILE * stream,char * buf,size_t size);

函數說明 在打開檔案流後,讀取內容之前,調用setbuffer()可用來設定檔案流的緩沖區。參數stream為指定的檔案流,參數buf指向自定的緩沖區起始位址,參數size為緩沖區大小。

返回值

 




setlinebuf(設定檔案流為線性緩沖區)
相關函數 setbuffer,setbuf,setvbuf

表頭檔案 #include

定義函數 void setlinebuf(FILE * stream);

函數說明 setlinebuf()用來設定檔案流以換行為依據的無緩沖IO。相當於調用:setvbuf(stream,(char * )NULL,_IOLBF,0);請參考setvbuf()。

返回值

 




setvbuf(設定檔案流的緩沖區)
相關函數 setbuffer,setlinebuf,setbuf

表頭檔案 #include

定義函數 int setvbuf(FILE * stream,char * buf,int mode,size_t size);

函數說明 在打開檔案流後,讀取內容之前,調用setvbuf()可以用來設定檔案流的緩沖區。參數stream為指定的檔案流,參數buf指向自定的緩沖區起始位址,參數size為緩沖區大小,參數mode有下列幾種
_IONBF 無緩沖IO
_IOLBF 以換行為依據的無緩沖IO
_IOFBF 完全無緩沖IO。如果參數buf為NULL指標,則為無緩沖IO。

返回值

 




ungetc(將指定字符寫回檔案流中)
相關函數 fputc,getchar,getc

表頭檔案 #include

定義函數 int ungetc(int c,FILE * stream);

函數說明 ungetc()將參數c字符寫回參數stream所指定的檔案流。這個寫回的字符會由下一個讀取檔案流的函數取得。

返回值 成功則返回c 字符,若有錯誤則返回EOF。

 

2007年6月25日 星期一

[轉]促進高效數據傳輸的TCP/IP選項

[本文轉載來源為:http://www.tongyi.net/article/20020424/200204243275.shtml]

在前一篇文章裡,我們討論了以下問題︰如何採用sendfile()系統函數降低從磁片到網路的數據傳輸負載。接下來我們繼續討論涉及網路連接控制的另一問題,同時希望透過對這一問題的討論能有助於在實際環境下把sendfile()的功能最大化,這就是如何設定TCP/IP選項來控制套接字的行為。

TCP/IP數據傳輸
TCP/IP網路的數據傳輸通常建立在數據塊的基礎之上。從程式員的觀點來看,發送數據意味著發出(或者提交)一系列“發送數據塊”的請求。在系統級,發送單個數據塊可以透過調用系統函數write() 或者sendfile() 來完成。在網路級可以看到更多的數據塊,通常把它們叫做幀,幀再被包裝上一定位元組長度的報頭然後透過線路在網路上傳輸。幀及其報頭內部的訊息是由若干協議層定義的,從OSI參考模型的物理層到應用層都可能會牽扯到。

因為在網路連接中是由程式員來選擇最適當的應用協議,所以網路包的長度和順序都在程式員的控制之下。同樣的,程式員還必須選擇這個協議在軟體中得以實現的模式。TCP/IP協議自身已經有了多種可互操作的實現,所以在雙方通信時,每一方都有它自身的低級行為,這也是程式員所應該知道的情況。

通常情況下,程式員不必關心作業系統和網路協議棧發送和接收網路數據的方法。系統內置算法定義了低級的數據組織和傳輸模式;然而,影響這些算法的行為以及對網路連接施加更大強度控制能力的方法也是有的。例如,如果某個應用協議使用了超時和重發機製,程式員就可以採取一定措施設定或者獲取超時參數。他或她還可能需要增加發送和接收緩沖區的大小來保證網路上的訊息流動不會中斷。改變TCP/IP協議棧行為的一般的方法是採用所謂的TCP/IP選項。下面就讓我們來看一看你該如何使用這些選項來優化數據傳輸。

TCP/IP選項
有好幾種選項都能改變TCP/IP協議棧的行為。使用這些選擇能對在同一計算機上營運的其他應用程式產生不利的影響,因此普通用戶通常是不能使用這些選項的(除了root用戶以外)。我們在這裡主要討論能改變單個連接操作(用TCP/IP的術語來說就是套接字)的選項。

ioctl風格的getsockopt()和setsockopt()系統函數都提供了控制套接字行為的模式。比方說,為了在Linux上設定TCP_NODELAY選項,你可以如下編寫代碼︰

intfd, on = 1;

/* 此處是創建套接字等操作,出於篇幅的考慮省略*/

setsockopt (fd, SOL_TCP, TCP_NODELAY, &on, sizeof (on));
儘管有許多TCP選項可供程式員操作,而我們卻最關注如何處置其中的兩個選項,它們是TCP_NODELAY 和 TCP_CORK,這兩個選項都對網路連接的行為具有重要的作用。許多UNIX系統都實現了TCP_NODELAY選項,但是,TCP_CORK則是Linux系統所獨有的而且相對較新;它首先在內核版本2.4上得以實現。此外,其他UNIX系統版本也有功能類似的選項,值得注意的是,在某種由BSD派生的系統上的TCP_NOPUSH選項其實就是TCP_CORK的一部分具體實現。

TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,Nagle化在這裡的含義是採用Nagle算法把較小的包組裝為更大的幀。John Nagle是Nagle算法的發明人,後者就是用他的名字來命名的,他在1984年首次用這種方法來嘗試解決福特汽車公司的網路擁塞問題(欲了解詳情請參看IETF RFC 896)。他解決的問題就是所謂的silly window syndrome ,中文稱“愚蠢視窗症候群”,具體含義是,因為普遍終端應用程式每產生一次擊鍵操作就會發送一個包,而典型情況下一個包會擁有一個位元組的數據載荷以及40個位元組長的包頭,於是產生4000%的過載,很輕易地就能令網路發生擁塞,。 Nagle化後來成了一種標準並且立即在網際網路上得以實現。它現下已經成為缺省配置了,但在我們看來,有些場合下把這一選項關掉也是合乎需要的。

現下讓我們假設某個應用程式發出了一個請求,希望發送小塊數據。我們可以選擇立即發送數據或者等待產生更多的數據然後再一次發送兩種策略。如果我們馬上發送數據,那麼交互性的以及客戶/伺服器型的應用程式將極大地受益。例如,當我們正在發送一個較短的請求並且等候較大的附應時,相關過載與傳輸的數據總量相比就會比較低,而且,如果請求立即發出那麼附應時間也會快一些。以上操作可以透過設定套接字的TCP_NODELAY選項來完成,這樣就禁用了Nagle算法。

另外一種情況則需要我們等到數據量達到最大時才透過網路一次發送全部數據,這種數據傳輸模式有益於大量數據的通信性能,典型的應用就是檔案伺服器。應用Nagle算法在這種情況下就會產生問題。但是,如果你正在發送大量數據,你可以設定TCP_CORK選項禁用Nagle化,其模式正好同TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。下面就讓我們仔細分析下其工作原理。

假設應用程式使用sendfile()函數來轉移大量數據。應用協議通常要求發送某些訊息來預先解釋數據,這些訊息其實就是報頭內容。典型情況下報頭很小,而且套接字上設定了TCP_NODELAY。有報頭的包將被立即傳輸,在某些情況下(取決於內部的包計數器),因為這個包成功地被對方收到後需要請求對方確認。這樣,大量數據的傳輸就會被延遲而且產生了不必要的網路流量交換。

但是,如果我們在套接字上設定了TCP_CORK(可以比喻為在管道上插入“塞子”)選項,具有報頭的包就會填補大量的數據,所有的數據都根據大小自動地透過包傳輸出去。當數據傳輸完成時,最好取消TCP_CORK 選項設定給連接“拔去塞子”以便任一部分的幀都能發送出去。這同“塞住”網路連接同等重要。

總而言之,如果你肯定能一起發送多個數據集合(例如HTTP附應的頭和正文),那麼我們建議你設定TCP_CORK選項,這樣在這些數據之間不存在延遲。能極大地有益於WWW、FTP以及檔案伺服器的性能,同時也簡化了你的工作。示例代碼如下︰

intfd, on = 1;

/* 此處是創建套接字等操作,出於篇幅的考慮省略*/

setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* cork */
write (fd, …);
fprintf (fd, …);
sendfile (fd, …);
write (fd, …);
sendfile (fd, …);

on = 0;
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* 拔去塞子 */


不幸的是,許多常用的程式並沒有考慮到以上問題。例如,Eric Allman編寫的sendmail就沒有對其套接字設定任何選項。

Apache HTTPD是網際網路上最流行的Web伺服器,它的所有套接字就都設定了TCP_NODELAY選項,而且其性能也深受大多數用戶的滿意。這是為什麼呢?答案就在於實現的差別之上。由BSD衍生的TCP/IP協議棧(值得注意的是FreeBSD)在這種狀況下的操作就不同。當在TCP_NODELAY 模式下提交大量小數據塊傳輸時,大量訊息將按照一次write()函數調用發送一塊數據的模式發送出去。然而,因為負責請求交付確認的記數器是面向位元組而非面向包(在Linux上)的,所以引入延遲的機率就降低了很多。結果僅僅和全部數據的大小有關係。而 Linux 在第一包到達之後就要求確認,FreeBSD則在進行如此操作之前會等待好幾百個包。

在Linux系統上,TCP_NODELAY的效果同習慣於BSD TCP/IP協議棧的開發者所期望的效果有很大不同,而且在Linux上的Apache性能表現也會更差些。其他在Linux上頻繁採用TCP_NODELAY的應用程式也有同樣的問題。
相得益彰
你的數據傳輸並不需要總是準確地遵守某一選項或者其它選擇。在那種情況下,你可能想要採取更為靈活的措施來控制網路連接︰在發送一系列當作單一消息的數據之前設定TCP_CORK,而且在發送應立即發出的短消息之前設定TCP_NODELAY。

把零拷貝和sendfile() 系統函數結合起來(前文有述)可以顯著地提升系統整體效率並且降低CPU負載。我們採用這一技術為Swsoft’s Virtuozzo公司開發了基於名稱的主機托管子系統,實踐經驗表明,該技術可以在裝備350-MHz Pentium II CPU的PC上實現每秒9000個HTTP請求,這一成績在以前幾乎是不可能實現的。性能上的提升顯而易見。

2007年6月11日 星期一

INT轉BCD碼

int Int2BCD(int Value, int ValueDigit, char* BCD)
{
  char tmp[10]={"\0"};
  int i,j,Result=0;
  for(i=0;ValueDigit>0;Value /= 100,i++,ValueDigit-=2)
  {
   Result = Value % 100;
   tmp[i] = (char)((( Result/10 )<<4) | (Result%10));
  }
  for(j=0,i=i-1;i>=0;i--,j++)
   BCD[j] = tmp[i];
  BCD[j] = '\0';
  return j;
}

BCD碼原理參照:
http://www.cublog.cn/opera/showart.php?blogid=15478&id=83640
http://xxcom.blogchina.com/


2007年4月16日 星期一

AutoRun Way in Linux (3)

Linux Autorn Way
啟動和關閉(或reboot):

1.程式的啟動由Script控制(ex:script filename為testserverctl)
則Script內容則由下法來設置啟動
case $1 in
 start)
   #將要啟動的程式寫至於此,如下:
   cd /tmp/testserver
   ./tesetserver &
   exit 0
   ;;
 stop)
   #將要結束的程式寫至於此,如下:
   cd /tmp/testserver
   test –e testserver_pid && cat testserver_pid xargs kill -15 echo “no testserver_pid file”
   exit 0
   ;;

執行script方式為:/script所在路徑/testserverctl start 及 /script所在路徑/testserverctl stop
將此script建立在/etc/rc.d/init.d/下
並分別在所需執行的runlevel下(如:/etc/rc.d/rc3.d/)分別建立兩種連結:S99testserver、K05testserver
使這兩連結分別連結到../init.d/testserver
即可在開機後自動啟動此程式,以及關機(或reboot)時關閉此程式
註:此法可不需修改原程式來判別參數

2.程式的啟動由程式本身執行檔控制(ex:script filename為testserverctl)
則Script內容則由下法來設置啟動
case $1 in
 start)
   #將要啟動的程式寫至於此,如下:
   cd /tmp/testserver
   ./tesetserver start
   exit 0
   ;;
 stop)
   #將要結束的程式寫至於此,如下:
   cd /tmp/testserver
   ./tesetserver stop
   exit 0
   ;;

執行script方式為:/script所在路徑/testserverctl start 及 /script所在路徑/testserverctl stop
將此script建立在/etc/rc.d/init.d/下
並分別在所需執行的runlevel下(如:/etc/rc.d/rc3.d/)分別建立兩種連結:S99testserver、K05testserver
使這兩連結分別連結到../init.d/testserver
即可在開機後自動啟動此程式,以及關機(或reboot)時關閉此程式
註:此法需修改原程式來判別參數start、stop
※以上兩種開機後自動啟動也可以將要執行的script寫入/etc/rc.d/rc.local裡
※結束需時較久的程式不建議在關機(或reboot)時才結束,最好事先結束完在關機(或reboot)

2007年4月12日 星期四

[轉]管理內核資源

一次大型PostgreSQL安裝會很容易耗盡各種作業系統的
資源上限.(在有些系統上,出廠設置低得你都不用一次"大型"安裝.) 如果你碰到這類問題,請繼續閱讀。

共享內存和信號燈的正確叫法是"System VIPC"( 還有消息隊列,不過對于PostgreSQL而言沒什麼 關係.)儘管所有現代作業系統都提供這個特性,但並不是
所有系統缺省都打開它或者有足夠的資源,尤其是有 BSD 親源 的系統. (對于QNXBeOS移植? PostgreSQL自己提供這套
機製的替換實現.)


完全缺少這些機製的表現通常是在 postmaster 啟動的時候的 Illegal system
call
錯誤.這時除了重新配置內核以外 沒什麼可做的 -- PostgreSQL
沒它們干不了活.


如果 PostgreSQL 超出了這些IPC 資源的硬限制之一的時候 就會拒絕啟動並且留下一條相當有啟發性的錯誤訊息告訴你它碰到了
什麼問題以及需要為它做些什麼. (又見 Section
3.3.1
.) 相關的內核參數在不同系統之間有著相對固定的術語; Table
3-3
是一個概況.不過,設置它們的方法卻 多種多樣.不過要注意的是,你可能最好重新啟動你的機器,或者還要 重新編譯內核來修改這些設置.



Table 3-3. System VIPC參數

































名字描述合理取值
SHMMAX最大共享內存段尺寸(位元組)250kB + 8.2 kB * shared_buffers + 14.2 kB * max_connections 或者無窮大
SHMMIN最小共享內存段尺寸(位元組)1
SHMALL可用共享內存的總數量(位元組或者頁面)如果是位元組,就和 SHMMAX
一樣;如果是頁面,ceil(SHMMAX/PAGE_SIZE)
SHMSEG每進程最大共享內存段數量只需要 1 個段,不過缺省比這高得多.
SHMMNI系統範圍最大共享內存段數量類似 SHMSEG
加上用于其他應用的空間
SEMMNI信號燈標識符的最小數量(也就是說,套)>= ceil(max_connections %
16)
SEMMNS系統範圍的最大信號燈數量ceil(max_connections / 16) *
17
+ 用于其他應用的空間
SEMMSL每套信號燈最小信號燈數量>= 17
SEMMAP信號燈映射裡的記錄數量參閱文本
SEMVMX信號燈的最大值>= 255
(缺省通常是32767,除非被要求,否則不要修改)

最重要的共享內存參數是 SHMMAX
以位元組記的共享內存段可擁有的最大尺寸. 如果你收到來自shmget的類似Invalid argument 這樣的錯誤訊息,那么很有可能是你超過限制了.
要求的共享內存段隨著請求的緩沖區數量(-B選項)和 允許的連接數量(-N選項)的變化而變化,儘管前者是 最重要的原素. (因此,作為一種臨時的解決方法,你可以降低這些設置來繞過失敗.)
如果粗略地估計,你可以估計所需要的段尺寸是緩沖區數量乘以塊 尺寸(缺省 8 kB)加上足夠的盈余(至少半兆位元組). 任何錯誤訊息都會包含分發失敗的尺寸.


不太可能出問題的是共享內存段的最小尺寸(SHMMIN), 對 PostgreSQL來說大約是 256 kB左右(通常只是 1),而 系統範圍(SHMMNI)或每進程(SHMSEG
最大共享內存段數量不應該會產生問題,除非你的系統把它們設成零. 有些系統還對系統裡的共享內存總量有限制;參閱下面平台相關的指導.


PostgreSQL 每個允許的聯接使用一個信號燈 (-N選項), 以 16 個為一套.每套信號燈還包含第十七個信號燈, 它裡面存儲一個"magic number(標誌數字)", 以檢測和其他應用使用的信號燈集衝突. 系統裡的最大信號燈數目是由SEMMNS設置的, 因此這個值應該至少和聯接設置一樣大,並且每十六個聯接還要另外加一個. (參閱Table
3-3
裡面的公式.) 參數SEMMNI決定 系統裡一次可以存在的信號燈集的數目.因此這個參數至少應該為
ceil(max_connections % 16).降低允許的聯接數目是一個臨時的
繞開失敗的方法,這個啟動失敗通常被來自函數semget() 的錯誤附應 "No space left on
device
"
搞得很讓人迷惑.


有時候還可能有必要增大SEMMAP,使之至少按照 SEMMNS配置.這個參數定義信號燈資源映射的尺寸, 可用的每個連續的信號燈塊在這個映射中存放一條記錄.
每當一套信號燈被釋放,那么它要么會加入到該映射中一條相連的 已釋放的塊的入口中,要么註冊成一條新的入口.如果映射填滿了碎片,
那么被釋放的信號燈就丟失了(除非重起).因此時間長信號燈空間的碎片 了會導致可用的信號燈比應該有的信號燈少.


SEMMSL 參數,決定一套信號燈裡可以有多少信號燈,對于 PostgreSQL而言應該至少是 17.


許多設置與 "semaphore undo(信號燈恢復)"有關,比如 SEMMNUSEMUME,這些與 PostgreSQL無關.







BSD/OS


共享內存. 缺省時是只支持 4 MB 的共享內存.請記住共享內存是不能分頁的;它是 鎖在 RAM 裡面的.要增加 postmaster
支持的共享緩沖區數目,向 你的內核配置文件裡增加下面的行.一個值為 1024 的 SHMALL 代表 4 M
共享內存.下面的東西把共享內存區域增加到 32 MB:

options "SHMALL=8192"
options "SHMMAX=\(SHMALL*PAGE_SIZE\)"


對于營運 4.1 或更新的版本的人,只需要做上面的修改, 然後編譯內核並重起.對于營運 更早的版本的,請用 bpatch 找出當前內核的 sysptsize值.它是啟動的時候自動計算的.

$ bpatch -r sysptsize
0x9 = 9

然後,把 SYSPTSIZE修改為在內核配置文件裡的一個硬代碼值. 用 bpatch 算出來的值,並且為你需要的每個額外的 4 MB 共享內存再加 1.

options "SYSPTSIZE=16"

sysptsize不能用 sysctl 修改.



信號燈. 你可能需要增加信號燈的數量.缺省時,PostgreSQL 分發
34 個信號燈.這個數量只是剛剛超過缺省的系統總數 60 的一半.


在內核配置文件裡設置你需要的值,比如︰

options "SEMMNI=40"
options "SEMMNS=240"
options "SEMUME=40"
options "SEMMNU=120"


FreeBSD
NetBSD
OpenBSD

編譯內核時 需要把選項SYSVSHMSYSVSEM打開. (缺省是打開的.) 共享內存的最大尺寸是由選項SHMMAXPGS(以頁計). 下面顯示了一個如何設置這些參數的例子︰

options         SYSVSHM
options SHMMAXPGS=4096
options SHMSEG=256

options SYSVSEM
options SEMMNI=256
options SEMMNS=512
options SEMMNU=256
options SEMMAP=256

(在 NetBSDOpenBSD裡,關鍵字實際上是單數的 option)


你可能原意使用 sysctl 設置將共享內存鎖在 RAM
中以避免它們被交換出去,也即,kern.ipc.shm_use_phys


HP-UX

缺省設置看來對普通安裝是足夠的了. 在 HP-UX 10,SEMMNS的出廠缺省是 128, 可能對大的數據庫節點來說太小了.


IPC可以在 System
Administration Manager(系統管理器)
(SAM)下面的 Kernel Configuration->Configurable Parameters 配置.你配置完了以後敲 Create A New Kernel選項.


Linux

在 2.2 內核裡缺省的共享內存限制( SHMMAXSHMALL)都是 32 MB,但是你可以在 proc
文件系統裡修改這些值(不用重起). 比如,要允許 128 MB︰

$echo 134217728 >/proc/sys/kernel/shmall
$echo 134217728 >/proc/sys/kernel/shmmax

你可以把這些命令放到一個引導時營運的腳本中.


另外,如果你的系統裡有的話,你可以使用 sysctl 來控制這些參數. 查找一個叫 /etc/sysctl.conf 的文件,然後再它裡面加下面 這樣的幾行︰

kernel.shmall = 134217728
kernel.shmmax = 134217728

通常在引導的時候會處理這個文件, 但你也可以稍后明確調用 sysctl


其他參數對任何應用來說都足夠了. 如果你想自己查看,你可以看看下面幾個文件︰ /usr/src/linux/include/asm-xxx/shmparam.h/usr/src/linux/include/linux/sem.h.


MacOS X

編輯文件 /System/Library/StartupItems/SystemTuning/SystemTuning
並且修改下列數值︰

sysctl -w kern.sysv.shmmax
sysctl -w kern.sysv.shmmin
sysctl -w kern.sysv.shmmni
sysctl -w kern.sysv.shmseg
sysctl -w kern.sysv.shmall

這些值在 MacOS X 上的含義和前面列出的作業系統的含義相同。


SCO OpenServer

缺省配置時,只允許每段 512KB 共享內存,大概只夠 -B 24 -N 12用的.要增大設置,首先進入
/etc/conf/cf.d目錄.要顯示當前的以位元組記的 SHMMAX,營運

./configure -y SHMMAX

設置 SHMMAX的新值︰

./configure SHMMAX=value

這裡 value 是你想設置的以位元組記的新值. 設置完了以後SHMMAX重新製作內核

./link_unix

然後重起.


Solaris

至少到版本 2.6 為止,共享內存段的缺省最大設置對 PostgreSQL
來說是太低了.相關的設置可以在/etc/system裡面修改, 例如︰

set shmsys:shminfo_shmmax=0x2000000
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=256
set shmsys:shminfo_shmseg=256

set semsys:seminfo_semmap=256
set semsys:seminfo_semmni=512
set semsys:seminfo_semmns=512
set semsys:seminfo_semmsl=32

你要重起系統令修改生效.


又見 http://www.sunworld.com/swol-09-1997/swol-09-insidesolaris.html
獲取關於 Solaris 裡面的共享內存的訊息.


UnixWare

UnixWare 7 上,缺省配置裡的最大共享內存段是 512 kB.
這個數只夠-B 24 -N 12用的.要顯示SHMMAX
當前值,營運

/etc/conf/bin/idtune -g SHMMAX

就會顯示以位元組記的當前的缺省的最小和最大值. 要給SHMMAX設置一個新值,營運︰

/etc/conf/bin/idtune SHMMAX value

這裡 value是你想設置的以位元組記的新值. 設置完SHMMAX后,重建內核

/etc/conf/bin/idbuild -B

然後重起.

Unix 類系統強製了許多資源限制,這些限制可能幹擾你的 PostgreSQL
伺服器的營運. 這裡尤其重要是對每個用戶的進程數目的限制,每個進程打開文件數目, 以及每個進程可用的內存. 這些限制中每個都有一個"硬"限制和一個"軟" 限制.
軟限制實際是管用的,但用戶可以自己修改成最大為硬限制的數目. 而硬限制是只能由 root 用戶修改的限制. 系統調用 setrlimit 負責設置這些參數. shell 的內建命令 ulimit(Bourne shells) 或limitcsh) 就是用于在命令行上控制資源限制的. 在 BSD 衍生的系統上,文件/etc/login.conf 控制在登錄時對各種資源設置什麼樣的限制數值. 參閱 login.conf 獲取細節.
相關的參數是 maxprocopenfiles,和 datasize. 比如︰
default:\
...
:datasize-cur=256M:\
:maxproc-cur=256:\
:openfiles-cur=256:\
...

-cur 是軟限制,后面附加 -max 就可以設置硬限制.)


內核通常也有一些系統範圍的資源限制.





  • Linux 上, /proc/sys/fs/file-max 決定內核可以支持的
    最大文件數.你可以透過往該文件寫入一個不同的數值修改此值, 或者在 /etc/sysctl.conf
    裡增加一個賦值. 每個進程的最大打開文件限制是在編譯內核的時候固定的; 參閱 /usr/src/linux/Documentation/proc.txt 獲取更多訊息.



PostgreSQL 伺服器每個聯接都使用一個進程,
所以你應該至少允許和聯接數相同的進程數,再加上你的系統其它部分 所需要的數目.通常這個並不是什麼問題,但如果你在一台機器上
營運多個伺服器,那你就要把事情理清楚.


打開文件數目的出廠缺省設置通常設置為 "社會友好"數值,就是說允許許多用戶共存于一台機器,
而不會導致只使用系統資源的不當比例.如果你在一台機器上營運許多 伺服器,這也許就是你想要的,但是在特殊的伺服器上,你可能需要提升 這個限制.


問題的另外一邊,一些系統允許獨立的進程打開非常多的文件; 如果有那么幾個進程這么干,那系統範圍的上限就很容易達到.
如果你發現這樣的現象,並且不想修改系統範圍的限止, 你就可以把 PostgreSQLmax_files_per_process 配置參數設置為你允許的 最大單進程打開文件數.

2007年4月11日 星期三

[轉]安裝Oracle於Debian

[本文轉載來源為:http://www.cuhkacs.org/~sky/wordpress/?p=143]

1. 於oracle上下載Oracle Database 10g Release 2,個File名叫做10201_database_linux32.zip


2. 要欺騙Oracle Installer我係RHEL 3
echo “Red Hat Enterprise Linux AS release 3 (Taroon)” > /etc/redhat-release

3. 建立Oracle專用戶口和群組
su - root
groupadd dba #group of users to be granted with SYSDBA privilege
groupadd oinstall # group owner of Oracle files
useradd -c "Oracle software owner" -g oinstall -G dba oracle
passwd -d /home/oracle -s /bin/bash oracle

4. 建立安裝地址,當然要確認夠位
su - root
mkdir /opt/oracle
mkdir /opt/oracle/product
mkdir /opt/oracle/product/10g
chown -R oracle.oinstall /opt/oracle

5. 更改核心用的/etc/sysctl.conf,在/etc/sysctl.conf上加上以下的
kernel.shmall=2097152
kernel.shmmax=2147483648
kernel.shmmni=4096
kernel.sem=250 32000 100 128
fs.file-max=65536
net.ipv4.ip_local_port_range=1024 65000

6. 用以下指令叫Linux重新讀取/etc/sysctl.conf
sysctl -e -p /etc/sysctl.conf

7. 用以下指令來確認是否正確重設系統參數
/sbin/sysctl -a grep sem
/sbin/sysctl -a grep shm
/sbin/sysctl -a grep file-max
/sbin/sysctl -a grep ip_local_port_range

8. 於 /etc/security/limits.conf 加上以下
* soft nproc 2047
* hard nproc 16384
* soft nofile 1024
* hard nofile 65536

9. 確認/etc/pam.d/login中有沒有以下這句,沒有的請自行加上去
session required pam_limits.so

10. 於/home/oracle/.bash_profile上加上以下的幾句
export ORACLE_BASE=/opt/oracle
export ORACLE_HOME=/opt/oracle/product/10g
export ORACLE_SID=SKY # Make sure you create a new Db with the same ORACLE_SID
export ORACLE_TERM=xterm
export DISPLAY=:0.0
# export TNS_ADMIN= Set if sqlnet.ora, tnsnames.ora, etc. are not in $ORACLE_HOME/network/admin
export NLS_LANG=AMERICAN;
export ORA_NLS33=$ORACLE_HOME/ocommon/nls/admin/data
LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
export LD_LIBRARY_PATH
# Set shell search paths
export PATH=$PATH:$ORACLE_HOME/bin

11. 安裝以下package
apt-get install rpm gcc-2.95 libaio

11. 加上以下Symlink
su - root
cd /usr/lib
ln -s libstdc++-libc6.2-2.so.3 libstdc++-libc6.1-1.so.2
ln -s /usr/bin/basename /bin/basename
rm /usr/bin/gcc
ln -s /usr/bin/gcc-2.95 /usr/bin/gcc
ln -s /usr/bin/rpm /bin/rpm
ln -s /usr/bin/awk /bin/awk

12. 可以開始用佢個Installer
./runInstaller

13. 最重要的一部,有可能你要在個installer行到30%時,即刻修改/opt/oracle/product/10g/bin/gennttab,把3句包令有(注意那好像好怪的換行):

SED 's/ /
/g'
改成

SED 's/ /n/g'

14. 如果部伺服器無行X-Windows,你想個Installer係你用緊部機打開,可以在runInstaller時行以下command
係你自己用的部機打:xhost +
係部伺服器打(假設你部機的IP係192.168.1.1):DISPLAY=192.168.1.1:0 ./runInstaller

參考:Successfully installed Oracle 10g on Knoppix 3.4