2007年7月19日 星期四
Cross-Compile sqlite 3.4.0
$export PATH=/tools/arm-linux-gnueabi/bin/$:PATH
$cd /home/working
$mkdir sqlite-arm-linux
$tar zxvf sqlite-3.4.0.tar.gz
$cd sqlite-arm-linux
$../sqlite-3.4.0/configure --disable-tcl --prefix=/home/working/sqlite-arm-linux/
--host=arm-linux CC=/tools/arm-linux-gnueabi/bin/arm-linux-gcc
$make
$ls .lib
copy libsqlite* 到target board上
如果需要用到執行檔sqlite3
可用
$/tools/arm-linux-gnueabi/bin/arm-linux-strip sqlite3
來decrease file size
2007年7月18日 星期三
[轉]SQLite 用法( 五)--如何用sqlite 執行標準 sql 語法
(2) SQL語句操作
本節介紹如何用sqlite 執行標準 sql 語法。
i.1 執行sql語句
int sqlite3_exec(sqlite3*, const char *sql, sqlite3_callback, void *, char **errmsg );
這就是執行一條 sql 語句的函數。
第1個參數不再說了,是前面open函數得到的指標。說了是關鍵數據架構。
第2個參數const char *sql 是一條 sql 語句,以\0結尾。
第3個參數sqlite3_callback 是回調,當這條語句執行之後,sqlite3會去調用你提供的這個函數。(什麼是回調函數,自己找別的資料學習)
第4個參數void * 是你所提供的指標,你可以傳遞任何一個指標參數到這裡,這個參數最終會傳到回調函數裡面,如果不需要傳遞指標給回調函數,可以填NULL。等下我們再看回調函數的寫法,以及這個參數的使用。
第5個參數char ** errmsg 是錯誤訊息。注意是指標的指標。sqlite3裡面有很多固定的錯誤訊息。執行 sqlite3_exec 之後,執行失敗時可以查閱這個指標(直接 printf(“%s\n”,errmsg))得到一串字元串訊息,這串訊息告訴你錯在什麼地方。sqlite3_exec函數透過修改你傳入的指標的指標,把你提供的指標指向錯誤提示訊息,這樣sqlite3_exec函數外面就可以透過這個 char*得到具體錯誤提示。
說明︰通常,sqlite3_callback 和它後面的 void * 這兩個位置都可以填 NULL。填NULL表示你不需要回調。比如你做 insert 操作,做 delete 操作,就沒有必要使用回調。而當你做 select 時,就要使用回調,因為 sqlite3 把數據查出來,得透過回調告訴你查出了什麼數據。
i.2 exec 的回調
typedef int (*sqlite3_callback)(void*,int,char**, char**);
你的回調函數必須定義成上面這個函數的類型。下面給個簡單的例子︰
//sqlite3的回調函數
// sqlite 每查到一條記錄,就調用一次這個回調
int LoadMyInfo( void * para, int n_column, char ** column_value, char ** column_name )
{
//para是你在 sqlite3_exec 裡傳入的 void * 參數
//透過para參數,你可以傳入一些特殊的指標(比如類指標、架構指標),然後在這裡面強製轉換成對應的類型(這裡面是void*類型,必須強製轉換成你的類型才可用)。然後操作這些數據
//n_column是這一條記錄有多少個字段 (即這條記錄有多少列)
// char ** column_value 是個關鍵值,查出來的數據都儲存在這裡,它實際上是個1維數組(不要以為是2維數組),每一個元素都是一個 char * 值,是一個字段內容(用字元串來表示,以\0結尾)
//char ** column_name 跟 column_value是對應的,表示這個字段的字段名稱
//這裡,我不使用 para 參數。忽略它的存在.
int i;
printf( “記錄包含 %d 個字段\n”, n_column );
for( i = 0 ; i <> 字段值:%s\n”, column_name[i], column_value[i] );
}
printf( “------------------\n“ );
return 0;
}
int main( int , char ** )
{
sqlite3 * db;
int result;
char * errmsg = NULL;
result = sqlite3_open( “c:\\Dcg_database.db”, db );
if( result != SQLITE_OK )
{
//數據庫打開失敗
return -1;
}
//數據庫操作代碼
//創建一個測試表,表名叫 MyTable_1,有2個字段︰ ID 和 name。其中ID是一個自動增加的類型,以後insert時可以不去指定這個字段,它會自己從0開始增加
result = sqlite3_exec( db, “create table MyTable_1( ID integer primary key autoincrement, name nvarchar(32) )”, NULL, NULL, errmsg );
if(result != SQLITE_OK )
{
printf( “創建表失敗,錯誤碼:%d,錯誤原因:%s\n”, result, errmsg );
}
//插入一些記錄
result = sqlite3_exec( db, “insert into MyTable_1( name ) values ( ‘走路’ )”, 0, 0, errmsg );
if(result != SQLITE_OK )
{
printf( “插入記錄失敗,錯誤碼:%d,錯誤原因:%s\n”, result, errmsg );
}
result = sqlite3_exec( db, “insert into MyTable_1( name ) values ( ‘騎單車’ )”, 0, 0, errmsg );
if(result != SQLITE_OK )
{
printf( “插入記錄失敗,錯誤碼:%d,錯誤原因:%s\n”, result, errmsg );
}
result = sqlite3_exec( db, “insert into MyTable_1( name ) values ( ‘坐汽車’ )”, 0, 0, errmsg );
if(result != SQLITE_OK )
{
printf( “插入記錄失敗,錯誤碼:%d,錯誤原因:%s\n”, result, errmsg );
}
//開始查詢數據庫
result = sqlite3_exec( db, “select * from MyTable_1”, LoadMyInfo, NULL, errmsg );
//關閉數據庫
sqlite3_close( db );
return 0;
}
透過上面的例子,應該可以知道如何打開一個數據庫,如何做數據庫基本操作。
有這些知識,基本上可以應付很多數據庫操作了。
i.3 不使用回調查詢數據庫
上面介紹的 sqlite3_exec 是使用回調來執行 select 操作。還有一個方法可以直接查詢而不需要回調。但是,我個人感覺還是回調好,因為代碼可以更加整齊,只不過用回調很麻煩,你得聲明一個函數,如果這個函數是類成員函數,你還不得不把它聲明成 static 的(要問為什麼?這又是C++基礎了。C++成員函數實際上隱藏了一個參數︰this,C++調用類的成員函數的時候,隱含把類指標當成函數的第一個參數傳遞進去。結果,這造成跟前面說的 sqlite 回調函數的參數不相符。只有當把成員函數聲明成 static 時,它才沒有多餘的隱含的this參數)。
雖然回調顯得代碼整齊,但有時候你還是想要非回調的 select 查詢。這可以透過 sqlite3_get_table 函數做到。
int sqlite3_get_table(sqlite3*, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg );
第1個參數不再多說,看前面的例子。
第2個參數是 sql 語句,跟 sqlite3_exec 裡的 sql 是一樣的。是一個很普通的以\0結尾的char *字元串。
第3個參數是查詢結果,它依然一維數組(不要以為是二維數組,更不要以為是三維數組)。它內存佈局是︰第一行是字段名稱,後面是緊接著是每個字段的值。下面用例子來說事。
第4個參數是查詢出多少條記錄(即查出多少行)。
第5個參數是多少個字段(多少列)。
第6個參數是錯誤訊息,跟前面一樣,這裡不多說了。
下面給個簡單例子:
int main( int , char ** )
{
sqlite3 * db;
int result;
char * errmsg = NULL;
char **dbResult; //是 char ** 類型,兩個*號
int nRow, nColumn;
int i , j;
int index;
result = sqlite3_open( “c:\\Dcg_database.db”, db );
if( result != SQLITE_OK )
{
//數據庫打開失敗
return -1;
}
//數據庫操作代碼
//假設前面已經創建了 MyTable_1 表
//開始查詢,傳入的 dbResult 已經是 char **,這裡又加了一個 取位址符,傳遞進去的就成了 char ***
result = sqlite3_get_table( db, “select * from MyTable_1”, dbResult, nRow, nColumn, errmsg );
if( SQLITE_OK == result )
{
//查詢成功
index = nColumn; //前面說過 dbResult 前面第一行數據是字段名稱,從 nColumn 索引開始才是真正的數據
printf( “查到%d條記錄\n”, nRow );
for( i = 0; i < j =" 0"> 字段值:%s\n”, dbResult[j], dbResult [index] );
++index; // dbResult 的字段值是連續的,從第0索引到第 nColumn - 1索引都是字段名稱,從第 nColumn 索引開始,後面都是字段值,它把一個二維的表(道統的行清單示法)用一個扁平的形式來表示
}
printf( “-------\n” );
}
}
//到這裡,不論數據庫查詢是否成功,都釋放 char** 查詢結果,使用 sqlite 提供的功能來釋放
sqlite3_free_table( dbResult );
//關閉數據庫
sqlite3_close( db );
return 0;
}
到這個例子為止,sqlite3 的常用用法都介紹完了。
用以上的方法,再配上 sql 語句,完全可以應付絕大多數數據庫需求。
但有一種情況,用上面方法是無法實現的︰需要insert、select 二進製。當需要處理二進製數據時,上面的方法就沒辦法做到。下面這一節說明如何插入二進製數據
(2) 操作二進製
sqlite 操作二進製數據需要用一個輔助的數據類型︰sqlite3_stmt * 。
這個數據類型記錄了一個“sql語句”。為什麼我把 “sql語句” 用雙引號引起來?因為你可以把 sqlite3_stmt * 所表示的內容看成是 sql語句,但是實際上它不是我們所熟知的sql語句。它是一個已經把sql語句解析了的、用sqlite自己標記記錄的內部數據架構。
正因為這個架構已經被解析了,所以你可以往這個語句裡插入二進製數據。當然,把二進製數據插到 sqlite3_stmt 架構裡可不能直接 memcpy ,也不能像 std::string 那樣用 + 號。必須用 sqlite 提供的函數來插入。
i.1 寫入二進製
下面說寫二進製的步驟。
要插入二進製,前提是這個表的字段的類型是 blob 類型。我假設有這么一張表︰
create table Tbl_2( ID integer, file_content blob )
首先聲明
sqlite3_stmt * stat;
然後,把一個 sql 語句解析到 stat 架構裡去︰
sqlite3_prepare( db, “insert into Tbl_2( ID, file_content) values( 10, ? )”, -1, stat, 0 );
上面的函數完成 sql 語句的解析。第一個參數跟前面一樣,是個 sqlite3 * 類型變量,第二個參數是一個 sql 語句。
這個 sql 語句特別之處在於 values 裡面有個 ? 號。在sqlite3_prepare函數裡,?號表示一個未定的值,它的值等下才插入。
第三個參數我寫的是-1,這個參數含義是前面 sql 語句的長度。如果小於0,sqlite會自動計算它的長度(把sql語句當成以\0結尾的字元串)。
第四個參數是 sqlite3_stmt 的指標的指標。解析以後的sql語句就放在這個架構裡。
第五個參數我也不知道是干什麼的。為0就可以了。
如果這個函數執行成功(返回值是 SQLITE_OK 且 stat 不為NULL ),那麼下面就可以開始插入二進製數據。
sqlite3_bind_blob( stat, 1, pdata, (int)(length_of_data_in_bytes), NULL ); // pdata為數據緩沖區,length_of_data_in_bytes為數據大小,以位元組為單位
這個函數一共有5個參數。
第1個參數︰是前面prepare得到的 sqlite3_stmt * 類型變量。
第2個參數︰?號的索引。前面prepare的sql語句裡有一個?號,假如有多個?號怎么插入?方法就是改變 bind_blob 函數第2個參數。這個參數我寫1,表示這裡插入的值要替換 stat 的第一個?號(這裡的索引從1開始計數,而非從0開始)。如果你有多個?號,就寫多個 bind_blob 語句,並改變它們的第2個參數就替換到不同的?號。如果有?號沒有替換,sqlite為它取值null。
第3個參數︰二進製數據起始指標。
第4個參數︰二進製數據的長度,以位元組為單位。
第5個參數︰是個析夠回調函數,告訴sqlite當把數據處理完後調用此函數來析夠你的數據。這個參數我還沒有使用過,因此理解也不深刻。但是一般都填NULL,需要釋放的內存自己用代碼來釋放。
bind完了之後,二進製數據就進入了你的“sql語句”裡了。你現下可以把它儲存到數據庫裡︰
int result = sqlite3_step( stat );
透過這個語句,stat 表示的sql語句就被寫到了數據庫裡。
最後,要把 sqlite3_stmt 架構給釋放︰
sqlite3_finalize( stat ); //把剛才分發的內容析構掉
i.2 讀出二進製
下面說讀二進製的步驟。
跟前面一樣,先聲明 sqlite3_stmt * 類型變量︰
sqlite3_stmt * stat;
然後,把一個 sql 語句解析到 stat 架構裡去︰
sqlite3_prepare( db, “select * from Tbl_2”, -1, stat, 0 );
當 prepare 成功之後(返回值是 SQLITE_OK ),開始查詢數據。
int result = sqlite3_step( stat );
這一句的返回值是 SQLITE_ROW 時表示成功(不是 SQLITE_OK )。
你可以循環執行 sqlite3_step 函數,一次 step 查詢出一條記錄。直到返回值不為 SQLITE_ROW 時表示查詢結束。
然後開始獲取第一個字段︰ID 的值。ID是個整數,用下面這個語句獲取它的值︰
int id = sqlite3_column_int( stat, 0 ); //第2個參數表示獲取第幾個字段內容,從0開始計算,因為我的表的ID字段是第一個字段,因此這裡我填0
下面開始獲取 file_content 的值,因為 file_content 是二進製,因此我需要得到它的指標,還有它的長度︰
const void * pFileContent = sqlite3_column_blob( stat, 1 );
int len = sqlite3_column_bytes( stat, 1 );
這樣就得到了二進製的值。
把 pFileContent 的內容儲存出來之後,不要忘了釋放 sqlite3_stmt 架構︰
sqlite3_finalize( stat ); //把剛才分發的內容析構掉
i.3 重複使用 sqlite3_stmt 架構
如果你需要重複使用 sqlite3_prepare 解析好的 sqlite3_stmt 架構,需要用函數︰ sqlite3_reset。
result = sqlite3_reset(stat);
這樣, stat 架構又成為 sqlite3_prepare 完成時的狀態,你可以重新為它 bind 內容
2007年7月17日 星期二
[轉]SQLite 用法(四)--sqlite3編譯安裝與交叉編譯
sqlite-3.3.6編譯安裝與交叉編譯
作者: zieckey@yahoo.com.cn
下文介紹的內容都是基於 Linux RedHat 9.0 平台的。
一、PC機編譯安裝
請閱讀在安裝包裡的 INSTALL 文件。或者使用PEAR installer with "pear install sqlite"。SQLite已經內置了,你不需要安裝任何附加的軟件(additional software)。
Windows users可以下載SQLite擴展DLL(php_sqlite.dl)。
這裡簡單介紹一下:
假設你得到的是源代碼sqlite-3.3.6.tar.gz,這裡將告訴你怎麼編譯它。
解壓sqlite-3.3.6.tar.gz 到 /home目錄下
For example:
tar zxvf sqlite-3.3.6.tar.gz -C /home
cd /home
mkdir sqlite-ix86-linux
cd /home/sqlite-ix86-linux/
../sqlite-3.3.6/configure --prefix=/home/sqlite-ix86-linux/
編譯並安裝,然後生成幫助文檔
make && make install && make doc
如果出現下列錯誤
../sqlite-3.3.6/src/tclsqlite.c: In function `DbUpdateHandler':
../sqlite-3.3.6/src/tclsqlite.c:333: warning: passing arg 3 of `Tcl_ListObjAppendElement' makes pointer from integer without a cast
../sqlite-3.3.6/src/tclsqlite.c: In function `tclSqlFunc':
../sqlite-3.3.6/src/tclsqlite.c:419: warning: passing arg 1 of `Tcl_NewByteArrayObj' discards qualifiers from pointer target type
這個都是tcl相關的錯誤,可以先安裝ActiveTcl以解決.假如你不需要tcl支持,那麼這個錯誤可以這樣避免:
cd /home/sqlite-ix86-linux/
../sqlite-3.3.6/configure --disable-tcl --prefix=/home/sqlite-ix86-linux/
編譯並安裝,然後生成幫助文檔
make && make install && make doc
不出意外,將不會出現錯誤,那麼
庫文件已經生成在 /home/sqlite-ix86-linux/lib 目錄下
可執行文件sqlite3已經生成在 /home/sqlite-ix86-linux/bin 目錄下
下面創建一個新的數據庫文件名叫"zieckey.db" (當然你可以使用不同的名字) 來測試數據庫.
直接輸入: /home/sqlite-ix86-linux/bin/sqlite3 test.db
如果出現下面字樣表明編譯安裝已經成功了.
SQLite version 3.3.6
Enter ".help" for instructions
sqlite>
二、交叉編譯sqlite.3.3.6.tar.gz庫文件
tar zxvf sqlite-3.3.6.tar.gz -C /home (這一步前面已經有了,為了完整性,這裡還是寫出來)
mkdir /home/sqlite-arm-linux
設置交叉編譯環境
export PATH=/usr/local/arm-linux/bin:$PATH
cd /home/sqlite-arm-linux/
../sqlite-3.3.6/configure --disable-tcl --prefix=/home/sqlite-arm-linux/ --host=arm-linux
這步出現錯誤而沒有生成Makefile
configure: error: unable to find a compiler for building build tools
前面檢查arm-linux-gcc都通過了,怎麼還說沒有找到編譯器呢?花了點時間看configure的腳本,太複雜了,又結合configure.ac看了一下。原來是要設置config_TARGET_CC和config_BUILD_CC兩個環境變量。config_TARGET_CC是交叉編譯器,config_BUILD_CC是主機編譯器。重來:
export config_BUILD_CC=gcc
export config_TARGET_CC=arm-linux-gcc
../sqlite-3.3.6/configure --disable-tcl --prefix=/home/sqlite-arm-linux/ --host=arm-linux
又出現如下錯誤
checking for /usr/include/readline.h... configure: error: cannot check for file existence when cross compiling
說readline.h的錯,找到readline.h在/usr/include/readline/readline.h目錄,我想這樣解決
ln -s /usr/include/readline/readline.h /usr/include/readline.h
但還是不行
../sqlite-3.3.6/configure --disable-tcl --prefix=/home/sqlite-arm-linux/ --host=arm-linux
還是出現如下同樣的錯誤
checking for /usr/include/readline.h... configure: error: cannot check for file existence when cross compiling
上面說是要檢查交叉編譯環境,我可以肯定我的交叉編譯環境是正確的,
所以我決定欺騙configure,我是這樣做的
cd /home/sqlite-3.3.6
將該目錄下的 configure 文件的部分內容修改下(這裡是根據 test "$cross_compiling" = yes && 找到的 ),
這樣可以讓configure不去檢查你的交叉編譯環境。
20420行 { (exit 1); exit 1; }; }改為 { (echo 1); echo 1; }; }
20446行 { (exit 1); exit 1; }; }改為 { (echo 1); echo 1; }; }
在回去重新配置下:
cd /home/sqlite-arm-linux/
../sqlite-3.3.6/configure --disable-tcl --prefix=/home/sqlite-arm-linux/ --host=arm-linux
中間打印信息出現如下錯誤信息,
checking for /usr/include/readline.h... configure: error: cannot check for file existence when cross compiling
但是還是生成了Makefile文件一個libtool腳本,這些將在make時用到.
注意:
如果Makefile文件中有如下語句
BCC = arm-linux-gcc -g -O2
請將其改掉,改成:
BCC = gcc -g -O2
編譯並安裝
make && make install
這裡如果不出意外,將不會出現錯誤,那麼
庫文件已經生成在 /home/sqlite-ix86-linux/lib 目錄下
好了,就到這裡。
[轉]SQLite 用法(三)--用C操作sqlite3
如何在Linux下用C/C++語言操作數據庫sqlite3 作者:zieckey(zieckey@yahoo.com.cn)
All Rights Reserved!
0. 引言
我們這篇文章主要講述了如何在C/C++語言中調用 sqlite 的函數接口來實現對數據庫的管理,
包括創建數據庫、創建表格、插入數據、查詢數據、刪除數據等。
1. 說明
這裡我們假設你已經編譯好了sqlite的庫文件 :
libsqlite3.a libsqlite3.la libsqlite3.so libsqlite3.so.0 libsqlite3.so.0.8.6 pkgconfig
和可執行文件 : sqlite3
我們再假設你的sqlite3的安裝目錄在 /usr/local/sqlite3 目錄下。
如果不是,我們可以這樣做,將你的安裝文件複製到 /usr/local/sqlite3 這個目錄,
這樣我們好在下面的操作中更加統一,從而減少出錯的概率
例如:[root@localhost home]# cp -rf sqlite-3.3.8-ix86/ /usr/local/sqlite3
這裡假設 /home/sqlite-3.3.8-ix86/ 是你的安裝目錄,也就是說你的sqlite原來就是安裝在這裡
這樣之後,我們的sqlite3的庫文件目錄是:/usr/local/sqlite3/lib
可執行文件 sqlite3 的目錄是: /usr/local/sqlite3/bin
頭文件 sqlite3.h 的目錄是: /usr/local/sqlite3/include
可以用ls命令查看下:
[root@localhost sqlite]# ls /usr/local/sqlite3/lib
libsqlite3.a libsqlite3.la libsqlite3.so libsqlite3.so.0 libsqlite3.so.0.8.6 pkgconfig
好拉,現在開始我們的Linux下sqlite3編程之旅。
2. 開始
這裡我們現在進行一個測試。
現在我們來寫個C/C++程序,調用 sqlite 的 API 接口函數。
下面是一個C程序的例子,顯示怎麼使用 sqlite 的 C/C++ 接口. 數據庫的名字由第一個參數取得且第二個參數或更多的參數是 SQL 執行語句. 這個函數調用sqlite3_open() 在 16 行打開數據庫,並且sqlite3_close() 在 25 行關閉數據庫連接。
[root@localhost temp]# vi opendbsqlite.c
按下 i 鍵切換到輸入模式,輸入下列代碼:
// name: opendbsqlite.c
// This prog is used to test C/C++ API for sqlite3.It is very simple,ha!
// Author : zieckey All rights reserved.
// date : 2006/11/13
#include
#include
int main( void )
{
sqlite3 *db=NULL;
char *zErrMsg = 0;
int rc;
//打開指定的數據庫文件,如果不存在將創建一個同名的數據庫文件
rc = sqlite3_open("zieckey.db", &db);
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
else printf("You have opened a sqlite3 database named zieckey.db successfully!\nCongratulations! Have fun ! ^-^ \n");
sqlite3_close(db); //關閉數據庫
return 0;
}
退出,保存。(代碼輸入完成後,按下 Esc 鍵,然後輸入: :wq ,回車就好拉)
好拉,現在編譯:[root@localhost temp]# gcc opendbsqlite.c -o db.out
或者遇到這樣的問題:
[root@localhost temp]# gcc opendbsqlite.c -o db.out
opendbsqlite.c:11:21: sqlite3.h: 沒有那個文件或目錄
opendbsqlite.c: In function `main':
opendbsqlite.c:19: `sqlite3' undeclared (first use in this function)
opendbsqlite.c:19: (Each undeclared identifier is reported only once
opendbsqlite.c:19: for each function it appears in.)
opendbsqlite.c:19: `db' undeclared (first use in this function)
這是由於沒有找到頭文件的原因。
也許會碰到類似這樣的問題:
[root@localhost temp]# gcc opendbsqlite.c -o db.out
/tmp/ccTkItnN.o(.text+0x2b): In function `main':
: undefined reference to `sqlite3_open'
/tmp/ccTkItnN.o(.text+0x45): In function `main':
: undefined reference to `sqlite3_errmsg'
/tmp/ccTkItnN.o(.text+0x67): In function `main':
: undefined reference to `sqlite3_close'
/tmp/ccTkItnN.o(.text+0x8f): In function `main':
: undefined reference to `sqlite3_close'
collect2: ld returned 1 exit status
這是個沒有找到庫文件的問題。
下面我們著手解決這些問題。
由於用到了用戶自己的庫文件,所用應該指明所用到的庫,我們可以這樣編譯:
[root@localhost temp]# gcc opendbsqlite.c -o db.out -lsqlite3
我用用 -lsqlite3 選項就可以了(前面我們生成的庫文件是 libsqlite3.so.0.8.6 等,
去掉前面的lib和後面的版本標誌,就剩下 sqlite3 了所以是 -lsqlite3 )。
如果我們在編譯安裝的時候,選擇了安裝路徑,例如這樣的話:
.......
# ../sqlite/configure --prefix=/usr/local/sqlite3
# make
.......
這樣編譯安裝時,sqlite的庫文件將會生成在 /usr/local/sqlite3/lib 目錄下
sqlite的頭文件將會生成在 /usr/local/sqlite3/include 目錄下
這時編譯還要指定庫文件路徑,因為系統默認的路徑沒有包含 /usr/local/sqlite3/lib
[root@localhost temp]# gcc opendbsqlite.c -o db.out -lsqlite3 -L/usr/local/sqlite3/lib
如果還不行的話,可能還需要指定頭文件 sqlite3.h 的路徑,如下:
[root@localhost temp]# gcc opendbsqlite.c -o db.out -lsqlite3 -L/usr/local/sqlite3/lib -I/usr/local/sqlite3/include
這樣編譯應該就可以了 ,運行:
[root@localhost temp]# ./db.out
./db.out: error while loading shared libraries: libsqlite3.so.0: cannot open shared object file: No such file or directory
運行是也許會出現類似上面的錯誤。
這個問題因為剛剛編譯的時候沒有選擇靜態編譯,那麼按照默認的編譯就動態編譯的。
動態編譯後,由於可執行文件在運行時要調用系統庫文件,
那麼沿著系統默認的庫文件搜索路徑搜索,就可能找不到我們現在所需的庫文件。
致使出現 "error while loading shared libraries" 等錯誤。
我們可以這樣解決:
方法一:靜態編譯
在編譯時加上 -static 參數,例如
[root@localhost temp]# gcc opendbsqlite.c -o db.out -lsqlite3 -L/usr/local/sqlite3/lib -I/usr/local/sqlite3/include -static
[root@localhost temp]# ll
總用量 1584
-rwxr-xr-x 1 root root 1596988 11月 13 10:50 db.out
-rw-r--r-- 1 root root 614 11月 13 10:31 opendbsqlite.c
可以看到輸出文件 db.out ,其大小為: 1596988k
運行,好了,沒有出現錯誤
[root@localhost temp]# ./db.out
You have opened a sqlite3 database named zieckey.db successfully!
Congratulations! Have fun ! ^-^
方法二:重新配置系統環境變量 LD_LIBRARY_PATH
這時需要指定 libsqlite3.so.0 庫文件的路徑,也就是配置系統環境變量 LD_LIBRARY_PATH ,
使系統能夠找到 libsqlite3.so.0 。
去掉 -static ,再編譯:
[root@localhost temp]# gcc opendbsqlite.c -o db.out -lsqlite3 -L/usr/local/sqlite3/lib -I/usr/local/sqlite3/include
[root@localhost temp]# ll
總用量 36
-rwxr-xr-x 1 root root 12716 11月 13 10:56 db.out
-rw-r--r-- 1 root root 614 11月 13 10:31 opendbsqlite.c
[root@localhost temp]#
可以看到輸出文件 db.out ,其大小為: 12716k,比剛才的靜態編譯要小得多。
所以我們推薦使用動態編譯的方法。
好了,現在我們來指定系統環境變量 LD_LIBRARY_PATH 的值
在shell下輸入:
[root@localhost temp]# export LD_LIBRARY_PATH=/usr/local/sqlite3/lib:$LD_LIBRARY_PATH
看看現在系統環境設置:
[root@localhost temp]#env
SSH_AGENT_PID=3511
HOSTNAME=localhost.localdomain
DESKTOP_STARTUP_ID=
SHELL=/bin/bash
TERM=xterm
HISTSIZE=1000
GTK_RC_FILES=/etc/gtk/gtkrc:/root/.gtkrc-1.2-gnome2
WINDOWID=29388238
OLDPWD=/mnt/usb/wuruan/sqlite
QTDIR=/usr/lib/qt-3.1
USER=root
LD_LIBRARY_PATH=/usr/local/sqlite3/lib:
LS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;32:*.btm=00;32:*.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;35:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:
SSH_AUTH_SOCK=/tmp/ssh-XXt14q3a/agent.3456
SESSION_MANAGER=local/localhost.localdomain:/tmp/.ICE-unix/3456
USERNAME=root
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
INPUTRC=/etc/inputrc
PWD=/mnt/usb/wuruan
XMODIFIERS=@im=fcitx
LANG=zh_CN.GB18030
GDMSESSION=Default
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
HOME=/root
SHLVL=2
LANGUAGE=zh_CN.GB18030:zh_CN.GB2312:zh_CN
GNOME_DESKTOP_SESSION_ID=Default
BASH_ENV=/root/.bashrc
LOGNAME=root
LESSOPEN=/usr/bin/lesspipe.sh %s
DISPLAY=:0.0
G_BROKEN_FILENAMES=1
COLORTERM=gnome-terminal
XAUTHORITY=/root/.Xauthority
_=/bin/env
看到了吧,LD_LIBRARY_PATH這一行開始就是sqlite3的庫文件路徑:
LD_LIBRARY_PATH=/usr/local/sqlite3/lib: ............
好拉,現在再運行
[root@localhost temp]# ./db.out
You have opened a sqlite3 database named zieckey.db successfully!
Congratulations! Have fun ! ^-^
是不是很有成就感阿 ,呵呵,這個上手還是很快的。
3. 插入:insert
剛剛我們知道了怎麼調用 sqlite3 的C/C++的API函數接口,下面我們看看怎麼在C語言中向數據庫插入數據。
好的,我們現編輯一段c代碼,取名為 insert.c
// name: insert.c
// This prog is used to test C/C++ API for sqlite3 .It is very simple,ha !
// Author : zieckey All rights reserved.
// date : 2006/11/18
#include
#include
#include "sqlite3.h"
#define _DEBUG_
int main( void )
{
sqlite3 *db=NULL;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("zieckey.db", &db); //打開指定的數據庫文件,如果不存在將創建一個同名的數據庫文件
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
else printf("You have opened a sqlite3 database named zieckey.db successfully!\nCongratulations! Have fun ! ^-^ \n");
//創建一個表,如果該表存在,則不創建,並給出提示信息,存儲在 zErrMsg 中
char *sql = " CREATE TABLE SensorData(
ID INTEGER PRIMARY KEY,
SensorID INTEGER,
SiteNum INTEGER,
Time VARCHAR(12),
SensorParameter REAL
);" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
#ifdef _DEBUG_
printf("%s\n",zErrMsg);
#endif
//插入數據
sql = "INSERT INTO \"SensorData\" VALUES( NULL , 1 , 1 , '200605011206', 18.9 );" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
sql = "INSERT INTO \"SensorData\" VALUES( NULL , 1 , 1 , '200605011306', 16.4 );" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
sqlite3_close(db); //關閉數據庫
return 0;
}
好的,將上述代碼寫入一個文件,並將其命名為 insert.c 。
解釋:
sqlite3_exec的函數原型說明如下:
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be executed */
sqlite_callback, /* Callback function */
void *, /* 1st argument to callback function */
char **errmsg /* Error msg written here */
);
編譯:
[root@localhost temp]# gcc insert.c -lsqlite3 -L/usr/local/sqlite3/lib -I/usr/local/sqlite3/include
insert.c:28:21: warning: multi-line string literals are deprecated
[root@localhost temp]#
執行
[root@localhost temp]# ./a.out
./a.out: error while loading shared libraries: libsqlite3.so.0: cannot open shared object file: No such file or directory
[root@localhost temp]#
同樣的情況,如上文處理方法:
[root@localhost temp]# export LD_LIBRARY_PATH=/usr/local/sqlite3/lib:$LD_LIBRARY_PATH
[root@localhost temp]# ./a.out
You have opened a sqlite3 database named zieckey.db successfully!
Congratulations! Have fun ! ^-^
(null)
(null)
(null)
[root@localhost temp]#
運行成功了,好了,現在我們來看看是否插入了數據
[root@localhost temp]# /usr/local/sqlite3/bin/sqlite3 zieckey.db
SQLite version 3.3.8
Enter ".help" for instructions
sqlite> select * from SensorData;
11120060501120618.9
21120060501130616.4
sqlite>
哈哈,已經插入進去了,不是嗎?
很簡單是不?
4. 查詢: SELETE
好了,我們知道了怎麼調用 sqlite3 的C/C++的API函數接口去創建數據庫、創建表格、並插入數據,
下面我們看看怎麼在C語言中查詢數據庫中的數據。
好的,我們現編輯一段c代碼,取名為 query.c
// name: query.c
// This prog is used to test C/C++ API for sqlite3 .It is very simple,ha !
// Author : zieckey All rights reserved.
// date : 2006/11/18
#include
#include
#include "sqlite3.h"
#define _DEBUG_
int main( void )
{
sqlite3 *db=NULL;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("zieckey.db", &db); //打開指定的數據庫文件,如果不存在將創建一個同名的數據庫文件
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
else printf("You have opened a sqlite3 database named zieckey.db successfully!\nCongratulations! Have fun ! ^-^ \n");
//創建一個表,如果該表存在,則不創建,並給出提示信息,存儲在 zErrMsg 中
char *sql = " CREATE TABLE SensorData(
ID INTEGER PRIMARY KEY,
SensorID INTEGER,
SiteNum INTEGER,
Time VARCHAR(12),
SensorParameter REAL
);" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
#ifdef _DEBUG_
printf("zErrMsg = %s \n", zErrMsg);
#endif
//插入數據
sql = "INSERT INTO \"SensorData\" VALUES(NULL , 1 , 1 , '200605011206', 18.9 );" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
sql = "INSERT INTO \"SensorData\" VALUES(NULL , 1 , 1 , '200605011306', 16.4 );" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
int nrow = 0, ncolumn = 0;
char **azResult; //二維數組存放結果
//查詢數據
/*
int sqlite3_get_table(sqlite3*, const char *sql,char***result , int *nrow , int *ncolumn ,char **errmsg );
result中是以數組的形式存放你所查詢的數據,首先是表名,再是數據。
nrow ,ncolumn分別為查詢語句返回的結果集的行數,列數,沒有查到結果時返回0
*/
sql = "SELECT * FROM SensorData ";
sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
int i = 0 ;
printf( "row:%d column=%d \n" , nrow , ncolumn );
printf( "\nThe result of querying is : \n" );
for( i=0 ; i<( nrow + 1 ) * ncolumn ; i++ )
printf( "azResult[%d] = %s\n", i , azResult[i] );
//釋放掉 azResult 的內存空間
sqlite3_free_table( azResult );
#ifdef _DEBUG_
printf("zErrMsg = %s \n", zErrMsg);
#endif
sqlite3_close(db); //關閉數據庫
return 0;
}
我們這裡用到了一個查詢的語句是 "SELECT * FROM SensorData " ,
在C語言中對應的函數接口是 sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
這個函數接口的解釋在程序中已經註釋。
下面我們編譯運行下看看,
[root@localhost temp]# export LD_LIBRARY_PATH=/usr/local/sqlite3/lib:$LD_LIBRARY_PATH
[root@localhost temp]# gcc query.c -lsqlite3 -L/usr/local/sqlite3/lib -I/usr/local/sqlite3/include
query.c:29:21: warning: multi-line string literals are deprecated
[root@localhost temp]# ./a.out
You have opened a sqlite3 database named zieckey.db successfully!
Congratulations! Have fun ! ^-^
zErrMsg = (null)
row:2 column=5
The result of querying is :
azResult[0] = ID
azResult[1] = SensorID
azResult[2] = SiteNum
azResult[3] = Time
azResult[4] = SensorParameter
azResult[5] = 1
azResult[6] = 1
azResult[7] = 1
azResult[8] = 200605011206
azResult[9] = 18.9
azResult[10] = 2
azResult[11] = 1
azResult[12] = 1
azResult[13] = 200605011306
azResult[14] = 16.4
zErrMsg = (null)
這裡我們可以看到,azResult 的前面 5 個數據正好是我們的表 SensorData 的列屬性,
之後才是我們要查詢的數據。所以我們的程序中才有 i<( nrow + 1 ) * ncolumn 的判斷條件:
for( i=0 ; i<( nrow + 1 ) * ncolumn ; i++ )
printf( "azResult[%d] = %s\n", i , azResult[i] );
輸出中有 zErrMsg = (null) 這樣的字句,這是 zErrMsg 保留的錯誤信息,
正如你所看到的,zErrMsg 為空,表明在執行過程中沒有錯誤信息。
5. 刪除:delete
下面我們看看怎麼在C語言中刪除數據庫中的特定的數據。
好的,我們現編輯一段c代碼,取名為 delete.c
// name: delete.c
// This prog is used to test C/C++ API for sqlite3 .It is very simple,ha !
// Author : zieckey All rights reserved.
// date : 2006/11/18
#include
#include
#include "sqlite3.h"
#define _DEBUG_
int main( void )
{
sqlite3 *db=NULL;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("zieckey.db", &db); //打開指定的數據庫文件,如果不存在將創建一個同名的數據庫文件
if( rc )
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
else printf("You have opened a sqlite3 database named zieckey.db successfully!\nCongratulations! Have fun ! ^-^ \n");
//創建一個表,如果該表存在,則不創建,並給出提示信息,存儲在 zErrMsg 中
char *sql = " CREATE TABLE SensorData(
ID INTEGER PRIMARY KEY,
SensorID INTEGER,
SiteNum INTEGER,
Time VARCHAR(12),
SensorParameter REAL
);" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
#ifdef _DEBUG_
printf("zErrMsg = %s \n", zErrMsg);
#endif
//插入數據
sql = "INSERT INTO \"SensorData\" VALUES(NULL , 1 , 1 , '200605011206', 18.9 );" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
sql = "INSERT INTO \"SensorData\" VALUES(NULL , 23 , 45 , '200605011306', 16.4 );" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
sql = "INSERT INTO \"SensorData\" VALUES(NULL , 34 , 45 , '200605011306', 15.4 );" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
int nrow = 0, ncolumn = 0;
char **azResult; //二維數組存放結果
//查詢數據
sql = "SELECT * FROM SensorData ";
sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
int i = 0 ;
printf( "row:%d column=%d \n" , nrow , ncolumn );
printf( "\nThe result of querying is : \n" );
for( i=0 ; i<( nrow + 1 ) * ncolumn ; i++ )
printf( "azResult[%d] = %s\n", i , azResult[i] );
//刪除數據
sql = "DELETE FROM SensorData WHERE SensorID = 1 ;" ;
sqlite3_exec( db , sql , 0 , 0 , &zErrMsg );
#ifdef _DEBUG_
printf("zErrMsg = %s \n", zErrMsg);
#endif
sql = "SELECT * FROM SensorData ";
sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
printf( "\n\n\n\nrow:%d column=%d " , nrow , ncolumn );
printf( "\nAfter deleting , the result of querying is : \n" );
for( i=0 ; i<( nrow + 1 ) * ncolumn ; i++ )
printf( "azResult[%d] = %s\n", i , azResult[i] );
//釋放掉 azResult 的內存空間
sqlite3_free_table( azResult );
#ifdef _DEBUG_
printf("zErrMsg = %s \n", zErrMsg);
#endif
sqlite3_close(db); //關閉數據庫
return 0;
}
下面我們編譯運行看看,效果如何
[root@localhost temp]# export LD_LIBRARY_PATH=/usr/local/sqlite3/lib:$LD_LIBRARY_PATH
[root@localhost temp]# gcc delete.c -lsqlite3 -L/usr/local/sqlite3/lib -I/usr/local/sqlite3/include
delete.c:29:21: warning: multi-line string literals are deprecated
[root@localhost temp]# ./a.out
You have opened a sqlite3 database named zieckey.db successfully!
Congratulations! Have fun ! ^-^
zErrMsg = (null)
row:3 column=5
The result of querying is :
azResult[0] = ID
azResult[1] = SensorID
azResult[2] = SiteNum
azResult[3] = Time
azResult[4] = SensorParameter
azResult[5] = 1
azResult[6] = 1
azResult[7] = 1
azResult[8] = 200605011206
azResult[9] = 18.9
azResult[10] = 2
azResult[11] = 23
azResult[12] = 45
azResult[13] = 200605011306
azResult[14] = 16.4
azResult[15] = 3
azResult[16] = 34
azResult[17] = 45
azResult[18] = 200605011306
azResult[19] = 15.4
zErrMsg = (null)
row:2 column=5
After deleting , the result of querying is :
azResult[0] = ID
azResult[1] = SensorID
azResult[2] = SiteNum
azResult[3] = Time
azResult[4] = SensorParameter
azResult[5] = 2
azResult[6] = 23
azResult[7] = 45
azResult[8] = 200605011306
azResult[9] = 16.4
azResult[10] = 3
azResult[11] = 34
azResult[12] = 45
azResult[13] = 200605011306
azResult[14] = 15.4
zErrMsg = (null)
從程序輸出結果就可以看出,在刪除數據前,我們有三條記錄,
刪除數據後我們發現,數據庫內記錄少了。從而實現了我們的刪除數據目的。
總結:
在這篇文章裡,我們主要講述了如何在C/C++語言中調用 sqlite 的函數接口來實現對數據庫的管理,
包括創建數據庫、創建表格、插入數據、查詢數據、刪除數據等。而這些操作似乎都很簡單不是嗎?
[轉]SQLite 用法(二)--簡單SQL語法使用
SQLite使用教學
OpenVanilla輸入法與文字處理架構
OS X自從10.4後把SQLite這套相當出名的資料庫軟體,放進了作業系統工具集裡。OS X包裝的是第三版的SQLite,又稱SQLite3。這套軟體有幾個特色:
軟體屬於公共財(public domain),SQLite可說是某種「美德軟體」(virtueware),作者本人放棄著作權,而給使用SQLite的人以下的「祝福」(blessing):
May you do good and not evil. 願你行善莫行惡
May you find forgiveness for yourself and forgive others. 願你原諒自己寬恕他人
May you share freely, never taking more than you give. 願你寬心與人分享,所取不多於你所施予
支援大多數的SQL指令(下面會簡單介紹)。
一個檔案就是一個資料庫。不需要安裝資料庫伺服器軟體。
完整的Unicode支援(因此沒有跨語系的問題)。
速度很快。
目前在OS X 10.4裡,SQLite是以/usr/bin/sqlite3的形式包裝,也就說這是一個命令列工具,必須先從終端機(Terminal.app或其他程式)進入shell之後才能使用。網路上有一些協助使用SQLite的視覺化工具,但似乎都沒有像CocoaMySQL(配合MySQL資料庫使用)那般好用。或許隨時有驚喜也未可知,以下僅介紹命令列的操作方式。
SQLite顧名思議是以SQL為基礎的資料庫軟體,SQL是一套強大的資料庫語言,主要概念是由「資料庫」、「資料表」(table)、「查詢指令」(queries)等單元組成的「關聯性資料庫」(進一步的概念可參考網路上各種關於SQL及關聯性資料庫的文件)。因為SQL的查詢功能強大,語法一致而入門容易,因此成為現今主流資料庫的標準語言(微軟、Oracle等大廠的資料庫軟體都提供SQL語法的查詢及操作)。
以下我們就建立資料庫、建立資料表及索引、新增資料、查詢資料、更改資料、移除資料、sqlite3命令列選項等幾個項目做簡單的介紹。
[編輯]建立資料庫檔案
用sqlite3建立資料庫的方法很簡單,只要在shell下鍵入(以下$符號為shell提示號,請勿鍵入):
$ sqlite3 foo.db3
如果目錄下沒有foo.db3,sqlite3就會建立這個資料庫。sqlite3並沒有強制資料庫檔名要怎麼取,因此如果你喜歡,也可以取個例如foo.icannameitwhateverilike的檔名。
[編輯]在sqlite3提示列下操作
進入了sqlite3之後,會看到以下文字:
SQLite version 3.1.3
Enter ".help" for instructions
sqlite>
這時如果使用.help可 以取得求助,.quit則是離開(請注意:不是quit)
[編輯]SQL的指令格式
所有的SQL指令都是以分號(;)結尾的。如果遇到兩個減號(--)則代表註解,sqlite3會略過去。
[編輯]建立資料表
假設我們要建一個名叫film的資料表,只要鍵入以下指令就可以了:
create table film(title, length, year, starring);
這樣我們就建立了一個名叫film的資料表,裡面有name、length、year、starring四個欄位。
這個create table指令的語法為:
create table table_name(field1, field2, field3, ...);
table_name是資料表的名稱,fieldx則是欄位的名字。sqlite3與許多SQL資料庫軟體不同的是,它不在乎欄位屬於哪一種資料型態:sqlite3的欄位可以儲存任何東西:文字、數字、大量文字(blob),它會在適時自動轉換。
[編輯]建立索引
如果資料表有相當多的資料,我們便會建立索引來加快速度。好比說:
create index film_title_index on film(title);
意思是針對film資料表的title欄位,建立一個名叫film_title_index的索引。這個指令的語法為
create index index_name on table_name(field_to_be_indexed);
一旦建立了索引,sqlite3會在針對該欄位作查詢時,自動使用該索引。這一切的操作都是在幕後自動發生的,無須使用者特別指令。
[編輯]加入一筆資料
接下來我們要加入資料了,加入的方法為使用insert into指令,語法為:
insert into table_name values(data1, data2, data3, ...);
例如我們可以加入
insert into film values ('Silence of the Lambs, The', 118, 1991, 'Jodie Foster');
insert into film values ('Contact', 153, 1997, 'Jodie Foster');
insert into film values ('Crouching Tiger, Hidden Dragon', 120, 2000, 'Yun-Fat Chow');
insert into film values ('Hours, The', 114, 2002, 'Nicole Kidman');
如果該欄位沒有資料,我們可以填NULL。
[編輯]查詢資料
講到這裡,我們終於要開始介紹SQL最強大的select指令了。我們首先簡單介紹select的基本句型:
select columns from table_name where expression;
最常見的用法,當然是倒出所有資料庫的內容:
select * from film;
如果資料太多了,我們或許會想限制筆數:
select * from film limit 10;
或是年份比較早的電影先列出來(預設為 asc):
select * from film order by year limit 10;
或是年份比較晚的電影先列出來:
select * from film order by year desc limit 10;
或是我們只想看電影名稱跟年份:
select title, year from film order by year desc limit 10;
查所有茱蒂佛斯特演過的電影:
select * from film where starring='Jodie Foster';
查所有演員名字開頭叫茱蒂的電影('%'、'_' 符號便是 SQL 的萬用字元,前者代表任意長度字元,後者代表任意一個字元):
select * from film where starring like 'Jodie%';
查所有演員名字以茱蒂開頭、年份晚於1985年、年份晚的優先列出、最多十筆,只列出電影名稱和年份:
select title, year from film where starring like 'Jodie%' and year >= 1985 order by year desc limit 10;
有時候我們只想知道資料庫一共有多少筆資料:
select count(*) from film;
有時候我們只想知道1985年以後的電影有幾部:
select count(*) from film where year >= 1985;
(進一步的各種組合,要去看SQL專書,不過你大概已經知道SQL為什麼這麼流行了:這種語言允許你將各種查詢條件組合在一起──而我們還沒提到「跨資料庫的聯合查詢」呢!)
[編輯]如何更改或刪除資料
瞭解select的用法非常重要,因為要在sqlite更改或刪除一筆資料,也是靠同樣的語法。
例如有一筆資料的名字打錯了:
update film set starring='Jodie Foster' where starring='Jodee Foster';
就會把主角欄位裡,被打成'Jodee Foster'的那筆(或多筆)資料,改回成Jodie Foster。
delete from film where year <> output.sql
利用輸出的資料,建立一個一模一樣的資料庫(加上以上指令,就是標準的SQL資料庫備份了):
sqlite3 film.db < title="SQLite%E4%BD%BF%E7%94%A8%E6%95%99%E5%AD%B8">
[轉]SQLite 用法(一)--安裝編譯使用
作者: zieckey ( zieckey@yahoo.com.cn )
第一章 從這裡開始
1.0 引言
最近發現網上很多人在文 sqlite 數據庫的問題,可是解答的人並不多。於是萌生了寫點這方面的文章的念頭。雖然我也是剛接觸 sqlite 不到半年,對其瞭解得也不是很深很透徹,但是寫點東西讓初學者能夠很快上手,也算是我的心願之一。也許很多高手會覺得我這是班門弄斧,但是我還是想寫點關於 sqlite 的基本知識,這樣也許能給予某些人一些幫助,希望如此。
下文介紹的內容都是基於 Linux RedHat 9.0 平台的。
1.1 sqlite簡介
sqlite是嵌入式SQL數據庫引擎SQLite(SQLite Embeddable SQL Database Engine)的一個擴展。SQLite是一個實現嵌入式SQL數據庫引擎小型C語言庫(C library),實現了獨立的,可嵌入的,零配置的SQL數據庫引擎。特性包括:事務操作是原子,一致,孤立,並且持久的,即使在系統崩潰和電源故障之後。 零配置——不需要安裝和管理。 實現了絕大多數SQL92標準。整個數據庫存儲在一個單一的文件中。數據庫文件可以在不同字節序的機器之間自由地共享。支持最大可達2T的數據庫。字符串和BLOB類型的大小只受限於可用內存。完整配置的少於250KB,忽略一些可選特性的少於150KB。在大多數常見操作上比流行的客戶/服務器數據庫引擎更快。 簡單易於使用的API。 內建TCL綁定。 另外提供可用於許多其他語言的綁定。具有良好註釋的源代碼,代碼95%有較好的註釋。 獨立:沒有外部依賴。 源代碼位於公共域,可用於任何用途。用SQLite連接的程序可以使用SQL數據庫,但不需要運行一個單獨的關係型數據庫管理系統進程(separate RDBMS process)。SQLite不是一個用於連接到大型數據庫服務器(big database server)的客戶端庫(client library),而是非常適合桌面程序和小型網站的數據庫服務器。SQLite直接讀寫(reads and writes directly)在硬盤上的數據庫文件。
這一章告訴你怎麼開始體驗 SQLite ,這裡沒有長長的說明和冗煩的配置。
1.2 下載代碼
取得一份二進制拷貝, 或者是源代碼並自己編譯它。關於這個可以到 http://www.sqlite.org/的下載頁面獲得更多信。
這裡我就不多說了。
1.3 安裝
請閱讀在安裝包裡的 INSTALL 文件。或者使用PEAR installer with "pear install sqlite"。SQLite已經內置了,你不需要安裝任何附加的軟件(additional software)。
Windows users可以下載SQLite擴展DLL(php_sqlite.dl)。
這裡簡單介紹一下:
假設你得到的是源代碼,這裡將告訴你怎麼編譯它。
解壓sqlite-3.0.8.tar.gz 到 /home目錄下
For example:
# tar xzf sqlite.tar.gz –C /home/ ;# Unpack the source tree into "sqlite"
# mkdir sqlite-pc ;# Build will occur in a sibling directory
# cd sqlite-pc ;# Change to the build directory
# ../sqlite/configure ;# Run the configure script
# make ;# Run the makefile.
# make install
生成幫助文檔
# make doc
如果一切順利的話,sqlite的庫文件將會生成在 /usr/local/lib 目錄下。去看看有沒有這些庫文件。
libsqlite3.a libsqlite3.la libsqlite3.so libsqlite3.so.0 libsqlite3.so.0.8.6 pkgconfig
好了我們可以進入下一節了。激動人心的時刻到了,我們可以輕而易舉的創建和操縱數據庫了。
1.4 創建一個新數據庫
在 shell 命令行下, 輸入: "sqlite3 zieckey.db"。 將創建一個新的數據庫文件名叫"zieckey.db". (當然你可以使用不同的名字)
輸入 SQL 命令在提示符下創建和寫入新的數據。
例如,新建一個名為zieckey.db的數據庫,並創建一個名為 SensorData 的表,其包含兩個列元素,並插入具體列元素,具體如下
# sqlite3 zieckey.db
SQLite version 3.0.8
Enter ".help" for instructions
sqlite> create table SensorData(one varchar(10), two smallint);
sqlite> insert into SensorData values('hello!',10);
sqlite> insert into SensorData values('goodbye', 20);
sqlite> select * from SensorData;
hello!10
goodbye20
sqlite>
安全退出 sqlite 的方法是
sqlite> .quit
這樣剛才的操作就保存下來了,如果強行退出有可能丟失剛才對數據庫的操作。
是不是相當容易,呵呵,當然拉!如果你會 SQL 語言,你會發現,sqlite的語法幾乎和它一個樣。
這裡先到這裡,後面的章節中我們會繼續學習sqlite的語法規則的。
1.5 寫個C語言程序調用SQLite
現在我們來寫個C/C++程序,調用 sqlite 的 API 接口函數。
下面是一個C程序的例子,顯示怎麼使用 sqlite 的 C/C++ 接口. 這個函數調用sqlite3_open() 在 22 行打開數據庫, sqlite3_exec() 在 27 行執行 SQL 命令, 並且sqlite3_close() 在 31 行關閉數據庫連接。
代碼:
// name: opendbsqlite.c
// This file is used to test C/C++ API for sqlite
// Author : zieckey
// 2006/04/11
#include
#include
int main( void )
{
sqlite3 *db=NULL;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("zieckey.db", &db); //打開指定的數據庫文件,如果不存在將創建一個同名的數據庫文件
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
else printf("open zieckey.db successfully!\n");
sqlite3_close(db); //關閉數據庫
return 0;
}
編譯:# gcc opendbsqlite.c -o db.out
也許會碰到類似這樣的問題:
/tmp/ccTkItnN.o(.text+0x2b): In function `main':
: undefined reference to `sqlite3_open'
/tmp/ccTkItnN.o(.text+0x45): In function `main':
: undefined reference to `sqlite3_errmsg'
/tmp/ccTkItnN.o(.text+0x67): In function `main':
: undefined reference to `sqlite3_close'
/tmp/ccTkItnN.o(.text+0x8f): In function `main':
: undefined reference to `sqlite3_close'
collect2: ld returned 1 exit status
這是個沒有找到庫文件的問題。
由於用到了用戶自己的庫文件,所用應該指明所用到的庫,我們可以這樣編譯:
# gcc opendbsqlite.c -o db.out -lsqlite3
我用用 -lsqlite3 選項就可以了(前面我們生成的庫文件是 libsqlite3.so.0.8.6 等,
去掉前面的lib和後面的版本標誌,就剩下 sqlite3 了所以是 -lsqlite3 )。
如果我們在編譯安裝的時候,選擇了安裝路徑,例如這樣的話:
.......
# ../sqlite/configure --prefix=/usr/local/arm-linux/sqlite-ix86-linux
.......
這樣編譯安裝時,sqlite的庫文件將會生成在 /usr/local/arm-linux/sqlite-ix86-linux/lib 目錄下
這時編譯還要指定庫文件路徑,因為系統默認的路徑沒有包含 /usr/local/arm-linux/sqlite-ix86-linux/lib
# gcc opendbsqlite.c -lsqlite3 -L/usr/local/arm-linux/sqlite-ix86-linux/lib
如果還不行的話,可能還需要指定頭文件 sqlite3.h 的路徑,如下:
# gcc opendbsqlite.c -lsqlite3 -L/usr/local/arm-linux/sqlite-ix86-linux/lib -I/usr/local/arm-linux/sqlite-ix86-linux/include
這樣編譯應該就可以了 ,運行:
# ./db.out
./db.out: error while loading shared libraries: libsqlite3.so.0: cannot open shared object file: No such file or directory
運行是也許會出現類似上面的錯誤。
這個問題是由於運行沿著系統默認路徑沒有找到庫文件引起的。這時需要指定 libsqlite3.so.0 庫文件的路徑。
在shell下輸入:
export LD_LIBRARY_PATH=/usr/local/arm-linux/sqlite-ix86-linux/lib:$LD_LIBRARY_PATH
再運行
# ./db.out
open zieckey.db successfully!
是不是很有成就感阿 ,呵呵,這個上手還是很快的。
總結:這一章裡,我們瞭解了sqlite的基本概貌,並且知道了怎麼創建數據庫,
怎麼向數據庫中插入表格,向表格中插入數據,更令人興奮的是我們知道了
怎麼在C/C++中調用 sqlite 的 API 函數。
寫後感:這是我第一次這樣用心的比較正規的寫關於一個專題的文章。
我也是望著篇文章能帶給你幫助。如果是這樣的話,那我的努力就沒有白費,這讓我覺得我還做了點好事的。
本文一定存在很多不妥或者錯誤之處,還勞煩各位不吝賜教指正。在此跟貼,或者發 email :zieckey@yahoo.com.cn ,本人將不勝感激。
作者鄭重申明:本文可以自由轉載,但請不要去掉版權信息,也不要用於商業用途。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=707507
2007年7月9日 星期一
[轉]Linux環境下使用XFS文件系統
XfS檔案系統是SGI開發的進階日誌檔案系統,XFS極具伸縮性,非常健壯。所幸的是SGI將其移植到了Linux系統中。在linux環境下。目前版本可用的最新XFS檔案系統的為1.2版本,可以很好地工作在2.4核心下。
一、XFS檔案系統簡介
主要特性包括以下幾點︰
數據完全性
採用XFS檔案系統,當意想不到的宕機發生後,首先,由於檔案系統開啟了日誌功能,所以你磁片上的檔案不再會意外宕機而遭到破壞了。不論目前檔案系統上存儲的檔案與數據有多少,檔案系統都可以根據所記錄的日誌在很短的時間內迅速恢復磁片檔案內容。
傳輸特性
XFS檔案系統採用優化算法,日誌記錄對整體檔案操作影響非常小。XFS查詢與分發存儲空間非常快。xfs檔案系統能連續提供快速的回應時間。筆者曾經對XFS、JFS、Ext3、ReiserFS檔案系統進行過測試,XFS檔案檔案系統的性能表現相當出眾。
可擴展性
XFS 是一個全64-bit的檔案系統,它可以支援上百萬T位元組的存儲空間。對特大檔案及小尺寸檔案的支援都表現出眾,支援特大數量的目錄。最大可支援的檔案大小為263 = 9 x 1018 = 9 exabytes,最大檔案系統尺寸為18 exabytes。
XFS使用高的表架構(B+樹),保證了檔案系統可以快速搜索與快速空間分發。XFS能夠持續提供高速操作,檔案系統的性能不受目錄中目錄及檔案數量的限制。
傳輸帶寬
XFS 能以接近裸設備I/O的性能存儲數據。在單個檔案系統的測試中,其吞吐量最高可達7GB每秒,對單個檔案的讀寫操作,其吞吐量可達4GB每秒。
二、XFS檔案系統的使用
1.下載與編譯內核
下載相應版本的內核補丁,解壓補丁套裝軟件,對系統核心打補丁
下載位址︰
對核心打補丁,下載解壓後,得到一個檔案︰xfs-1.1-2.4.18-all.patch檔案。
對核心進行修補如下︰
# cd /usr/src/linux
# patch -p1 < /path/to/xfs-1.1-2.4.18-all.patch 修補工作完成後,下一步要進行的工作是編譯核心,將XFS編譯進Linux核心可中。 首先營運以下命令,選擇核心支援XFS檔案系統︰ #make menuconfig 在“檔案系統“選單中選擇︰ <*> SGI XFS filesystem support ##說明︰將XFS檔案系統的支援編譯進核心
或
SGI XFS filesystem support ##說明︰以動態加載模塊的模式支援XFS檔案系統
另外還有兩個選擇︰
Enable XFS DMAPI ##說明︰對磁片管理的API,存儲管理應用程式使用
Enable XFS Quota ##說明︰支援配合Quota對用戶使用磁片空間大小管理
完成以上工作後,退出並儲存核心選擇配置.
之後,然後編譯內核,安裝核心:
#make bzImage
#make module
#make module_install
#make install
如果你對以上複雜繁瑣的工作沒有耐心或沒有把握,那麼可以直接從SGI的站點上下載已經打好補丁的核心,其版本為2.4.18。它是一個rpm套裝軟件,你只要簡單地安裝即可。SGI提交的核心有兩種,分別供smp及單處理器的機器使用。
2.創建XFS檔案系統
完成對核心的編譯後,還應下載與之配套的XFSprogs工具套裝軟件,也即mkfs.xfs工具。不然我們無法完成對分區的格式化:即無法將一個分區格式化成XFS檔案系統的格式。要下載的套裝軟件名稱︰xfsprogs-2.0.3。
將所下載的XFSProgs工具解壓,安裝,mkfs.xfs自動安裝在/sbin目錄下。
#tar xvf xfsprogs-2.0.3.src.tar.gz
#cd xfsprogs-2.0.3src
#./configure
#make
#make install
使用mkfs.xfs格式化磁片為xfs檔案系統,方法如下︰
# /sbin/mkfs.xfs /dev/sda6 #說明︰將分區格式化為xfs檔案系統,以下為顯示內容︰
meta-data=/dev/sda6 isize=256 agcount=8, agsize=128017 blks
data = bsize=4096 blocks=1024135, imaxpct=25
= sunit=0 swidth=0 blks, unwritten=0
naming =version 2 bsize=4096
log =internal log bsize=4096 blocks=1200
realtime =none extsz=65536 blocks=0, rtextents=0
格式化磁片時,如果mkfs.xfs提示你分區原本已被格式化為其它檔案系統,可以使用參數 -f 強行格式化︰
#/sbin/mkfs.xfs -f /dev/sda6
3.加載XFS檔案系統
#mount -t xfs /dev/sda6 /xfs ##其中/xfs是主分區/下的一個目錄。
最後,為了讓系統啟動後就自動加載,應該更改/etc/fstab,這樣系統啟動後就會自動加載xfs分區而不必每次都手工加載。
要說明的一點是目前的xfs由於受linux內存頁限制,在x86版本中,只能實現檔案系統的塊尺寸為4K。另外,XFS檔案系統可以不同的模式mount,即允許檔案系統以讀模式加載,也允許以讀寫模式加載。這是因為xfs檔案系統用作根檔案系統時,為了安全要以只讀模式加載。
三、檔案系統的遷移
要使得系統中的其它分區使用XFS檔案系統,還有一步是遷移檔案系統。建議在遷移檔案系統時,首先將磁片上的數據、檔案先備份,以免發生不可挽回的損失,在進行檔案系統轉換之間,最好能將整個系統進行完全備份。這一步有很多種方法,本文僅就筆者的遷移方法加以描述。各位可以按照自己習慣的模式去完成
如果你想得到一個純的xfs系統(系統的所有檔案系統均採用XFS檔案系統)話,還得將根檔案系統也格式化為xfs檔案系統。這實際上是比較繁雜的一步。因為根檔案系統不能被umount,所以,必須首先創建一個分區,其檔案系統為ext2檔案系統,然後將目前的根分區上的所有檔案與目錄,原原本本地複製到這一個分區,然後更改/etc/fstab檔案,替換原來的根分區。
方法如下︰
$ mkfs -t ext2 /dev/hda4
$ mkdir /mnt/temp
$ mount -t ext2 /dev/hda4 /mnt/temp
$ cd /
$ tar lcvf - .|(cd /mnt/temp; tar xpvf - )
以上操作是將根分區上的所有檔案打包,複製到新建立的分區。當然,你也可以直接使用以下命令複製檔案。
# cp dpR / /mnt/temp
接著,將下次啟動的根分區更改到/dev/hda4分區,更改/etc/fstab檔案及/etc/lilo.conf ,然後,營運 lilo.
重新啟動後,新的根分區就已經為/dev/hda4。
接下來,創建一個xfs檔案系統的分區︰
$ mkfs -t xfs /dev/hda2
加載此分區,採用兩樣的方法,將根分區的內容複製到此分區
$ mount -t xfs /dev/hda2 /mnt/temp
在根分區下,營運
$ cd /
$ tar lcvf - .|(cd /mnt/temp; tar xpvf - )
再次更改/etc/fstab、/etc/lilo.conf,用新建的xfs分區替換原來的ext2主分區。如下所示︰
/dev/hda2 / xfs defaults 1 1
將新建的xfs分區用作根分區,儲存以上設定。再次檢查配置檔案內容,確認無誤後再重新啟動系統。如果你的設定全部正確,那麼系統成功啟動後,你就擁有一個純XFS檔案系統的系統了。
2007年7月2日 星期一
[轉]監看網路封包程式設計概說
監看網路封包程式設計概說.
作者: 邵文琳
前一個禮拜花了不少時間在看網路封包方面的程式設計 ..覺得十分有興趣.
感謝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
相關函數 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;i
}
執行 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選項
在前一篇文章裡,我們討論了以下問題︰如何採用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碼
{
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)
啟動和關閉(或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日 星期四
[轉]管理內核資源
資源上限.(在有些系統上,出廠設置低得你都不用一次"大型"安裝.) 如果你碰到這類問題,請繼續閱讀。
共享內存和信號燈的正確叫法是"System VIPC"( 還有消息隊列,不過對于PostgreSQL而言沒什麼 關係.)儘管所有現代作業系統都提供這個特性,但並不是
所有系統缺省都打開它或者有足夠的資源,尤其是有 BSD 親源 的系統. (對于QNX和 BeOS移植? 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(信號燈恢復)"有關,比如 SEMMNU 和 SEMUME,這些與 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 編譯內核時 需要把選項SYSVSHM 和 SYSVSEM打開. (缺省是打開的.) 共享內存的最大尺寸是由選項SHMMAXPGS(以頁計). 下面顯示了一個如何設置這些參數的例子︰
options SYSVSHM
options SHMMAXPGS=4096
options SHMSEG=256
options SYSVSEM
options SEMMNI=256
options SEMMNS=512
options SEMMNU=256
options SEMMAP=256(在 NetBSD 和 OpenBSD裡,關鍵字實際上是單數的 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 內核裡缺省的共享內存限制( SHMMAX 和 SHMALL)都是 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
然後重起.
伺服器的營運. 這裡尤其重要是對每個用戶的進程數目的限制,每個進程打開文件數目, 以及每個進程可用的內存. 這些限制中每個都有一個"硬"限制和一個"軟" 限制.
軟限制實際是管用的,但用戶可以自己修改成最大為硬限制的數目. 而硬限制是只能由 root 用戶修改的限制. 系統調用 setrlimit 負責設置這些參數. shell 的內建命令 ulimit(Bourne shells) 或limit (csh) 就是用于在命令行上控制資源限制的. 在 BSD 衍生的系統上,文件/etc/login.conf 控制在登錄時對各種資源設置什麼樣的限制數值. 參閱 login.conf 獲取細節.
相關的參數是 maxproc, openfiles,和 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 伺服器每個聯接都使用一個進程,
所以你應該至少允許和聯接數相同的進程數,再加上你的系統其它部分 所需要的數目.通常這個並不是什麼問題,但如果你在一台機器上
營運多個伺服器,那你就要把事情理清楚.
打開文件數目的出廠缺省設置通常設置為 "社會友好"數值,就是說允許許多用戶共存于一台機器,
而不會導致只使用系統資源的不當比例.如果你在一台機器上營運許多 伺服器,這也許就是你想要的,但是在特殊的伺服器上,你可能需要提升 這個限制.
問題的另外一邊,一些系統允許獨立的進程打開非常多的文件; 如果有那么幾個進程這么干,那系統範圍的上限就很容易達到.
如果你發現這樣的現象,並且不想修改系統範圍的限止, 你就可以把 PostgreSQL 的 max_files_per_process 配置參數設置為你允許的 最大單進程打開文件數.
2007年4月11日 星期三
[轉]安裝Oracle於Debian
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
[轉]ulimit命令的用法
有時候在程序裡面需要打開多個文件,進行分析,系統一般默認數量是1024,(用ulimit-a可以看到)對於正常使用是夠了,但是對於程序來講,就太少了。
修改辦法:
vi /etc/security/limits.conf
加上:
* soft nofile 8192
* hard nofile 20480
重啟就OK
ulimit命令
用途
設置或報告用戶資源極限。
語法
ulimit [ -H ] [ -S ] [ -a ] [ -c ] [ -d ] [ -f ] [ -m ] [ -n ] [ -s ] [ -t ] [ Limit ]
描述
ulimit命令設置或報告用戶進程資源極限,如/etc/security/limits文件所定義。 文件包含以下缺省值極限:
fsize = 2097151
core = 2097151
cpu = -1
data = 262144
rss = 65536
stack = 65536
nofiles = 2000
當新用戶添加到系統中時,這些值被作為缺省值使用。當向系統中添加用戶時,以上值通過mkuser命令設置,或通過chuser命令更改。
極限分為軟性和硬性。 通過ulimit命令,用戶可將軟極限更改到硬極限的最大設置值。 要更改資源硬極限,必須擁有root用戶權限。
很多系統不包括以上一種或數種極限。特定資源的極限在指定Limit參數時設定。Limit參數的值可以是每個資源中指定單元中的數字,或者為值unlimited。 要將特定的ulimit設置為unlimited,可使用詞unlimited。
注:在/etc/security/limits文件中設置缺省極限就是設置了系統寬度極限,而不僅僅是創建用戶時用戶所需的極限。
省略Limit參數時,將會打印出當前資源極限。除非用戶指定-H標誌,否則打印出軟極限。當用戶指定一個以上資源時,極限名稱和單元在值之前打印。如果未給予選項,則假定帶有了-f標誌。
由於ulimit命令影響當前shell環境,所以它將作為shell常規內置命令提供。如果在獨立的命令執行環境中調用該命令,則不影響調用者環境的文件大小極限。以下示例中正是這種情況:
nohup ulimit -f 10000nohupulimit-f10000
env ulimit 10000envulimit10000
一旦通過進程減少了硬極限,若無root特權則無法增加,即使返回到原值也不可能。
關於用戶和系統資源極限的更多信息,請參見AIX5LVersion5.2TechnicalReference:BaseOperatingSystemandExtensionsVolume1中的getrlimit、setrlimit或vlimit子例程。
标志標誌
-a 列出所有當前資源極限。
-c 以512字節塊為單位,指定核心轉儲的大小。
-d 以K字節為單位指定數據區域的大小。
-f 使用Limit參數時設定文件大小極限(以塊計),或者在未指定參數時報告文件大小極限。缺省值為-f標誌。
-H 指定設置某個給定資源的硬極限。 如果用戶擁有root用戶權限,可以增大硬極限。任何用戶均可減少硬極限。
-m 以K字節為單位指定物理存儲器的大小。
-n 指定一個進程可以擁有的文件描述符的數量的極限。
-s 以K字節為單位指定堆棧的大小。
-S 指定為給定的資源設置軟極限。軟極限可增大到硬極限的值。如果-H和-S標誌均未指定,極限適用於以上二者。
-t 指定每個進程所使用的秒數。
退出狀態
返回以下退出值:
0成功完成。
>0 拒絕對更高的極限的請求,或發生錯誤。
示例
要將文件大小極限設置為51,200字節,輸入:
ulimit -f 100ulimit-f100
文件
usr/bin/ksh包含ulimit內置命令。
相關信息
ksh命令。
AIX5LVersion5.2TechnicalReference:BaseOperatingSystemandExtensionsVolume1中的ulimit子例程、getrlimit、setrlimit或vlimit子例程。
2007年3月20日 星期二
2007年2月20日 星期二
autoconf手冊(七)
陳舊的宏名
在Autoconf的第2版,大部分宏被重新命名以使用更加統一和具有描述性的命名方案。下面是被重新命名了的宏的原來名字,隨後給出了這些宏現下的名字。雖然為了保持向後兼容,舊名字仍然能夠被autoconf程式所接受,舊名字都被看作過時的。關於新的命名方案,參見宏名。
AC_ALLOCA
AC_FUNC_ALLOCA
AC_ARG_ARRAY
因為用途有限而被刪除了。
AC_CHAR_UNSIGNED
AC_C_CHAR_UNSIGNED
AC_CONST
AC_C_CONST
AC_CROSS_CHECK
AC_C_CROSS
AC_ERROR
AC_MSG_ERROR
AC_FIND_X
AC_PATH_X
AC_FIND_XTRA
AC_PATH_XTRA
AC_FUNC_CHECK
AC_CHECK_FUNC
AC_GCC_TRADITIONAL
AC_PROG_GCC_TRADITIONAL
AC_GETGROUPS_T
AC_TYPE_GETGROUPS
AC_GETLOADAVG
AC_FUNC_GETLOADAVG
AC_HAVE_FUNCS
AC_CHECK_FUNCS
AC_HAVE_HEADERS
AC_CHECK_HEADERS
AC_HAVE_POUNDBANG
AC_SYS_INTERPRETER (不同的調用慣例)
AC_HEADER_CHECK
AC_CHECK_HEADER
AC_HEADER_EGREP
AC_EGREP_HEADER
AC_INLINE
AC_C_INLINE
AC_LN_S
AC_PROG_LN_S
AC_LONG_DOUBLE
AC_C_LONG_DOUBLE
AC_LONG_FILE_NAMES
AC_SYS_LONG_FILE_NAMES
AC_MAJOR_HEADER
AC_HEADER_MAJOR
AC_MINUS_C_MINUS_O
AC_PROG_CC_C_O
AC_MMAP
AC_FUNC_MMAP
AC_MODE_T
AC_TYPE_MODE_T
AC_OFF_T
AC_TYPE_OFF_T
AC_PID_T
AC_TYPE_PID_T
AC_PREFIX
AC_PREFIX_PROGRAM
AC_PROGRAMS_CHECK
AC_CHECK_PROGS
AC_PROGRAMS_PATH
AC_PATH_PROGS
AC_PROGRAM_CHECK
AC_CHECK_PROG
AC_PROGRAM_EGREP
AC_EGREP_CPP
AC_PROGRAM_PATH
AC_PATH_PROG
AC_REMOTE_TAPE
因為用途有限而被刪除了。
AC_RESTARTABLE_SYSCALLS
AC_SYS_RESTARTABLE_SYSCALLS
AC_RETSIGTYPE
AC_TYPE_SIGNAL
AC_RSH
因為用途有限而被刪除了。
AC_SETVBUF_REVERSED
AC_FUNC_SETVBUF_REVERSED
AC_SET_MAKE
AC_PROG_MAKE_SET
AC_SIZEOF_TYPE
AC_CHECK_SIZEOF
AC_SIZE_T
AC_TYPE_SIZE_T
AC_STAT_MACROS_BROKEN
AC_HEADER_STAT
AC_STDC_HEADERS
AC_HEADER_STDC
AC_STRCOLL
AC_FUNC_STRCOLL
AC_ST_BLKSIZE
AC_STRUCT_ST_BLKSIZE
AC_ST_BLOCKS
AC_STRUCT_ST_BLOCKS
AC_ST_RDEV
AC_STRUCT_ST_RDEV
AC_SYS_SIGLIST_DECLARED
AC_DECL_SYS_SIGLIST
AC_TEST_CPP
AC_TRY_CPP
AC_TEST_PROGRAM
AC_TRY_RUN
AC_TIMEZONE
AC_STRUCT_TIMEZONE
AC_TIME_WITH_SYS_TIME
AC_HEADER_TIME
AC_UID_T
AC_TYPE_UID_T
AC_UTIME_NULL
AC_FUNC_UTIME_NULL
AC_VFORK
AC_FUNC_VFORK
AC_VPRINTF
AC_FUNC_VPRINTF
AC_WAIT3
AC_FUNC_WAIT3
AC_WARN
AC_MSG_WARN
AC_WORDS_BIGENDIAN
AC_C_BIGENDIAN
AC_YYTEXT_POINTER
AC_DECL_YYTEXT
環境變量索引
這是一個按照字母順序排序的,由Autoconf檢查的環境變量的清單。
Jump to: a - c - s
a
AC_MACRODIR, AC_MACRODIR, AC_MACRODIR, AC_MACRODIR, AC_MACRODIR, AC_MACRODIR
c
CONFIG_FILES
CONFIG_HEADERS
CONFIG_SHELL
CONFIG_SITE
CONFIG_STATUS
s
SIMPLE_BACKUP_SUFFIX
輸出變量索引
這是一個按照字母順序排序的,Autoconf將在它所創建的檔案(通常是一個或更多``Makefile'')中進行替換的變量的清單。關於這些是如何實現的,請參見設定輸出變量。
Jump to: a - b - c - d - e - f - h - i - k - l - m - n - o - p - r - s - t - x - y
a
ALLOCA
AWK
b
bindir
build
build_alias
build_cpu
build_os
build_vendor
c
CC, CC, CC
CFLAGS, CFLAGS
configure_input
CPP
CPPFLAGS
CXX
CXXCPP
CXXFLAGS, CXXFLAGS
d
datadir
DEFS
e
exec_prefix
EXEEXT
f
F77
FFLAGS, FFLAGS
FLIBS
h
host
host_alias
host_cpu
host_os
host_vendor
i
includedir
infodir
INSTALL
INSTALL_DATA
INSTALL_PROGRAM
INSTALL_SCRIPT
k
KMEM_GROUP
l
LDFLAGS
LEX
LEX_OUTPUT_ROOT
LEXLIB
libdir
libexecdir
LIBOBJS, LIBOBJS, LIBOBJS, LIBOBJS, LIBOBJS
LIBS, LIBS, LIBS
LN_S
localstatedir
m
mandir
n
NEED_SETGID
o
OBJEXT
oldincludedir
p
prefix
program_transform_name
r
RANLIB
s
sbindir
SET_MAKE
sharedstatedir
srcdir
subdirs
sysconfdir
t
target
target_alias
target_cpu
target_os
target_vendor
top_srcdir
x
X_CFLAGS
X_EXTRA_LIBS
X_LIBS
X_PRE_LIBS
y
YACC
預處理器符號索引
這是一個按照字母順序排序的,由Autoconf宏定義的C預處理符號的清單。為了與Autoconf協同工作,C源代碼應該在#if指令中使用這些名字。
Jump to: _ - c - d - f - g - h - i - l - m - n - o - p - r - s - t - u - v - w - y
_
__CHAR_UNSIGNED__
_ALL_SOURCE
_MINIX
_POSIX_1_SOURCE
_POSIX_SOURCE, _POSIX_SOURCE
_POSIX_VERSION
c
C_ALLOCA
CLOSEDIR_VOID
const
d
DGUX
DIRENT
f
F77_NO_MINUS_C_MINUS_O
g
GETGROUPS_T
GETLODAVG_PRIVILEGED
GETPGRP_VOID
gid_t
h
HAVE_ALLOCA_H
HAVE_CONFIG_H
HAVE_DIRENT_H
HAVE_DOPRNT
HAVE_function
HAVE_GETMNTENT
HAVE_header
HAVE_LONG_DOUBLE
HAVE_LONG_FILE_NAMES
HAVE_MMAP
HAVE_NDIR_H
HAVE_RESTARTABLE_SYSCALLS
HAVE_ST_BLKSIZE
HAVE_ST_BLOCKS
HAVE_ST_RDEV
HAVE_STRCOLL
HAVE_STRFTIME
HAVE_STRINGIZE
HAVE_SYS_DIR_H
HAVE_SYS_NDIR_H
HAVE_SYS_WAIT_H
HAVE_TM_ZONE
HAVE_TZNAME
HAVE_UNISTD_H
HAVE_UTIME_NULL
HAVE_VFORK_H
HAVE_VPRINTF
HAVE_WAIT3
i
inline
INT_16_BITS
l
LONG_64_BITS
m
MAJOR_IN_MKDEV
MAJOR_IN_SYSMACROS
mode_t
n
NDIR
NEED_MEMORY_H
NEED_SETGID
NLIST_NAME_UNION
NLIST_STRUCT
NO_MINUS_C_MINUS_O
o
off_t
p
pid_t
r
RETSIGTYPE
s
SELECT_TYPE_ARG1
SELECT_TYPE_ARG234
SELECT_TYPE_ARG5
SETPGRP_VOID
SETVBUF_REVERSED
size_t
STDC_HEADERS
SVR4
SYS_SIGLIST_DECLARED
SYSDIR
SYSNDIR
t
TIME_WITH_SYS_TIME
TM_IN_SYS_TIME
u
uid_t
UMAX
UMAX4_3
USG
v
vfork
VOID_CLOSEDIR
w
WORDS_BIGENDIAN
y
YYTEXT_POINTER
宏索引
這是按字母排序的Autoconf宏清單。為了使清單易於使用,宏以沒有前綴``AC_''的形式列出。
Jump to: a - b - c - d - e - f - g - h - i - l - m - o - p - r - s - t - u - v - w - x - y
a
AIX
ALLOCA
ARG_ARRAY
ARG_ENABLE
ARG_PROGRAM
ARG_WITH
b
BEFORE
c
C_BIGENDIAN
C_CHAR_UNSIGNED
C_CONST
C_CROSS
C_INLINE
C_LONG_DOUBLE
C_STRINGIZE
CACHE_CHECK
CACHE_LOAD
CACHE_SAVE
CACHE_VAL
CANONICAL_HOST
CANONICAL_SYSTEM
CHAR_UNSIGNED
CHECK_FILE
CHECK_FILES
CHECK_FUNC
CHECK_FUNCS
CHECK_HEADER
CHECK_HEADERS
CHECK_LIB
CHECK_PROG
CHECK_PROGS
CHECK_SIZEOF
CHECK_TOOL
CHECK_TYPE
CHECKING
COMPILE_CHECK
CONFIG_AUX_DIR
CONFIG_HEADER
CONFIG_SUBDIRS
CONST
CROSS_CHECK
CYGWIN
d
DECL_SYS_SIGLIST
DECL_YYTEXT
DEFINE
DEFINE_UNQUOTED
DEFUN
DIR_HEADER
DYNIX_SEQ
e
EGREP_CPP
EGREP_HEADER
ENABLE
ERROR
EXEEXT
f
F77_LIBRARY_LDFLAGS
FIND_X
FIND_XTRA
FUNC_ALLOCA
FUNC_CHECK
FUNC_CLOSEDIR_VOID
FUNC_FNMATCH
FUNC_GETLOADAVG
FUNC_GETMNTENT
FUNC_GETPGRP
FUNC_MEMCMP
FUNC_MMAP
FUNC_SELECT_ARGTYPES
FUNC_SETPGRP
FUNC_SETVBUF_REVERSED
FUNC_STRCOLL
FUNC_STRFTIME
FUNC_UTIME_NULL
FUNC_VFORK
FUNC_VPRINTF
FUNC_WAIT3
g
GCC_TRADITIONAL
GETGROUPS_T
GETLOADAVG
h
HAVE_FUNCS
HAVE_HEADERS
HAVE_LIBRARY
HAVE_POUNDBANG
HEADER_CHECK
HEADER_DIRENT
HEADER_EGREP
HEADER_MAJOR
HEADER_STAT
HEADER_STDC
HEADER_SYS_WAIT
HEADER_TIME
i
INIT
INLINE
INT_16_BITS
IRIX_SUN
ISC_POSIX
l
LANG_C
LANG_CPLUSPLUS
LANG_FORTRAN77
LANG_RESTORE
LANG_SAVE
LINK_FILES
LN_S
LONG_64_BITS
LONG_DOUBLE
LONG_FILE_NAMES
m
MAJOR_HEADER
MEMORY_H
MINGW32
MINIX
MINUS_C_MINUS_O
MMAP
MODE_T
MSG_CHECKING
MSG_ERROR
MSG_RESULT
MSG_WARN
o
OBJEXT
OBSOLETE
OFF_T
OUTPUT
p
PATH_PROG
PATH_PROGS
PATH_X
PATH_XTRA
PID_T
PREFIX
PREFIX_PROGRAM
PREREQ
PROG_AWK
PROG_CC
PROG_CC_C_O
PROG_CPP
PROG_CXX
PROG_CXXCPP
PROG_F77_C_O
PROG_FORTRAN
PROG_GCC_TRADITIONAL
PROG_INSTALL
PROG_LEX
PROG_LN_S
PROG_MAKE_SET
PROG_RANLIB
PROG_YACC
PROGRAM_CHECK
PROGRAM_EGREP
PROGRAM_PATH
PROGRAMS_CHECK
PROGRAMS_PATH
PROVIDE
r
REMOTE_TAPE
REPLACE_FUNCS
REQUIRE
REQUIRE_CPP
RESTARTABLE_SYSCALLS
RETSIGTYPE
REVISION
RSH
s
SCO_INTL
SEARCH_LIBS, SEARCH_LIBS
SET_MAKE
SETVBUF_REVERSED
SIZE_T
SIZEOF_TYPE
ST_BLKSIZE
ST_BLOCKS
ST_RDEV
STAT_MACROS_BROKEN, STAT_MACROS_BROKEN
STDC_HEADERS
STRCOLL
STRUCT_ST_BLKSIZE
STRUCT_ST_BLOCKS
STRUCT_ST_RDEV
STRUCT_TIMEZONE
STRUCT_TM
SUBST
SUBST_FILE
SYS_INTERPRETER
SYS_LONG_FILE_NAMES
SYS_RESTARTABLE_SYSCALLS
SYS_SIGLIST_DECLARED
t
TEST_CPP
TEST_PROGRAM
TIME_WITH_SYS_TIME
TIMEZONE
TRY_COMPILE
TRY_CPP
TRY_LINK
TRY_LINK_FUNC, TRY_LINK_FUNC
TRY_RUN
TYPE_GETGROUPS
TYPE_MODE_T
TYPE_OFF_T
TYPE_PID_T
TYPE_SIGNAL
TYPE_SIZE_T
TYPE_UID_T
u
UID_T
UNISTD_H
USG
UTIME_NULL
v
VALIDATE_CACHED_SYSTEM_TUPLE
VERBOSE
VFORK
VPRINTF
w
WAIT3
WARN
WITH
WORDS_BIGENDIAN
x
XENIX_DIR
y
YYTEXT_POINTER