技术资料的存档

嵌入式数据库系统Berkeley DB

2008-06-06 8:34 pm
 

  施聪
高级程序员、网络设计师
2005 年 4 月

   BerkeleyDB是历史悠久的嵌入式数据库系统,主要应用在UNIX/LINUX操作系统上,其设计思想是简单、小巧、可靠、高性能。本文是对DB开 发的一个入门级指南,重点讨论了DB的核心数据结构和数据访问算法,并通过实际的代码演示如何使用DB。最后有一个对DB的简单总结,并提出作者对工具选 择的一些感想。
前言
UNIX/LINUX平台下的数据库种类非常多,参考资料1中 列举了其中的大部分。通常,我们在设计UNIX/LINUX平台下的应用软件时,如果数据种类繁多,数据与数据之间关系比较复杂,就会选用一些大型的企业 级数据库系统,如DB2,ORACLE、SYBASE等,如果软件规模不大,就倾向选用如MYSQL、POSTGRESQL等中小型数据库。例如使用 PHP/PERL +MYSQL/POSTGRESQL设计网站基本上是一个很常规的做法。但是,当应用软件管理的数据类型较少(特别注意:这并不是说需要管理的数据量 小),数据管理本身不复杂,且对数据操作要求高效率,则由大名鼎鼎的Berkeley(美国加州大学伯克利分校)开发的 BerkeleyDB可能是一个很明智的选择。

DB综述
DB最初开发的目的是以新的HASH访问算法来代替旧的hsearch函数和大量的dbm实现(如AT&T的dbm,Berkeley的 ndbm,GNU项目的gdbm),DB的第一个发行版在1991年出现,当时还包含了B+树数据访问算法。在1992年,BSDUNIX第4.4发行版 中包含了DB1.85版。基本上认为这是DB的第一个正式版。在1996年中期,Sleepycat软件公司成立,提供对DB的商业支持。在这以后,DB得到了广泛的应用,当前最新版本是4.3.27。

DB支持几乎所有的现代操作系统,如LINUX、UNIX、WINDOWS等,也提供了丰富的应用程序接口,支持C、C++、JAVA、PERL、TCL、PYTHON、PHP等。DB的应用十分广泛,在很多知名的软件中都能看到其身影。例如参考资料2中作者谈到利用DB在LINUX下实现内核级文件系统;参考资料3中通过实际测试数据说明DB提高了OPENLDAP的效率。LINUX下的软件包管理器RPM也使用DB管理软件包相关数据,可以使用命令file查看RPM数据目录/var/lib/rpm下的文件,则有形式如下的输出:

Dirnames: Berkeley DB (Btree, version 9, native byte-order)
Filemd5s: Berkeley DB (Hash, version 8, native byte-order)

值得注意的是DB是嵌入式数据库系统,而不是常见的关系/对象型数据库,对SQL语言不支持,也不提供数据库常见的高级功能,如存储过程,触发器等。
DB的设计思想
DB 的设计思想是简单、小巧、可靠、高性能。如果说一些主流数据库系统是大而全的话,那么DB就可称为小而精。DB提供了一系列应用程序接口(API),调用 本身很简单,应用程序和DB所提供的库在一起编译成为可执行程序。这种方式从两方面极大提高了DB的效率。第一:DB库和应用程序运行在同一个地址空间, 没有客户端程序和数据库服务器之间昂贵的网络通讯开销,也没有本地主机进程之间的通讯;第二:不需要对SQL代码解码,对数据的访问直截了当。

DB对需要管理的数据看法很简单,DB数据库包含若干条记录,每一个记录由关键字和数据(KEY/VALUE)构成。数据可以是简单的数据类型,也可以是 复杂的数据类型,例如C语言中结构。DB对数据类型不做任何解释,完全由程序员自行处理,典型的C语言指针的"自由"风格。如果把记录看成一个有n个字段 的表,那么第1个字段为表的主键,第2--n个字段对应了其它数据。DB应用程序通常使用多个DB数据库,从某种意义上看,也就是关系数据库中的多个表。 DB库非常紧凑,不超过500K,但可以管理大至256T的数据量。

DB的设计充分体现了UNIX的基于工具的哲学,即若干简单工具的组合可以实现强大的功能。DB的每一个基础功能模块都被设计为独立的,也即意味着其使用 领域并不局限于DB本身。例如加锁子系统可以用于非DB应用程序的通用操作,内存共享缓冲池子系统可以用于在内存中基于页面的文件缓冲。

DB核心数据结构
数据库句柄结构DB:包含了若干描述数据库属性的参数,如数据库访问方法类型、逻辑页面大小、数据库名称等;同时,DB结构中包含了大量的数据库处理函数指针,大多数形式为 (*dosomething)(DB *, arg1, arg2,…)。其中最重要的有open,close,put,get等函数。

数据库记录结构DBT:DB中的记录由关键字和数据构成,关键字和数据都用结构DBT表示。实际上完全可以把关键字看成特殊的数据。结构中最重要的两个字段是 void * data和u_int32_t size,分别对应数据本身和数据的长度。

数据库游标结构DBC:游标(cursor)是数据库应用中常见概念,其本质上就是一个关于特定记录的遍历器。注意到DB支持多重记录(duplicate records),即多条记录有相同关键字,在对多重记录的处理中,使用游标是最容易的方式。

数据库环境句柄结构DB_ENV:环境在DB中属于高级特性,本质上看,环境是多个数据库的包装器。当一个或多个数据库在环境中打开后,环境可以为这些数据库提供多种子系统服务,例如多线/进程处理支持、事务处理支持、高性能支持、日志恢复支持等。

DB中核心数据结构在使用前都要初始化,随后可以调用结构中的函数(指针)完成各种操作,最后必须关闭数据结构。从设计思想的层面上看,这种设计方法是利用面向过程语言实现面对对象编程的一个典范。


DB数据访问算法
在数据库领域中,数据访问算法对应了数据在硬盘上的存储格式和操作方法。在编写应用程序时,选择合适的算法可能会在运算速度上提高1个甚至多个数量级。大 多数数据库都选用B+树算法,DB也不例外,同时还支持HASH算法、Recno算法和Queue算法。接下来,我们将讨论这些算法的特点以及如何根据需 要存储数据的特点进行选择。

B+树算法:B+树是一个平衡树,关键字有序存储,并且其结构能随数据的插入和删除进行动态调整。为了代码的简单,DB没有实现对关键字的前缀码压缩。B+树支持对数据查询、插入、删除的常数级速度。关键字可以为任意的数据结构。

HASH算法:DB中实际使用的是扩展线性HASH算法(extended linear hashing),可以根据HASH表的增长进行适当的调整。关键字可以为任意的数据结构。

Recno算法: 要求每一个记录都有一个逻辑纪录号,逻辑纪录号由算法本身生成。实际上,这和关系型数据库中逻辑主键通常定义为int AUTO型是同一个概念。Recho建立在B+树算法之上,提供了一个存储有序数据的接口。记录的长度可以为定长或不定长。

Queue算法:和Recno方式接近, 只不过记录的长度为定长。数据以定长记录方式存储在队列中,插入操作把记录插入到队列的尾部,相比之下插入速度是最快的。

对算法的选择首先要看关键字的类型,如果为复杂类型,则只能选择B+树或HASH算法,如果关键字为逻辑记录号,则应该选择Recno或Queue算法。 当工作集关键字有序时,B+树算法比较合适;如果工作集比较大且基本上关键字为随机分布时,选择HASH算法。Queue算法只能存储定长的记录,在高的 并发处理情况下,Queue算法效率较高;如果是其它情况,则选择Recno算法,Recno算法把数据存储为平面文件格式。


DB常用函数使用范例



#include <db.h>

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

/* DB的函数执行完成后,返回0代表成功,否则失败 */

void print_error(int ret)

{

 if(ret != 0)

                printf("ERROR: %s\n",db_strerror(ret));

}

/* 数据结构DBT在使用前,应首先初始化,否则编译可通过但运行时报参数错误  */

void init_DBT(DBT * key, DBT * data)

{

 memset(key, 0, sizeof(DBT));

        memset(data, 0, sizeof(DBT));

}

void main(void)

{

      DB *dbp;

 DBT key, data;

      u_int32_t flags;

  int ret;

        char *fruit = "apple";

      int number = 15;

    typedef struct customer

     {

           int  c_id;

          char name[10];

              char address[20];

           int  age;

   } CUSTOMER;

 CUSTOMER cust;

     int key_cust_c_id = 1;

  cust.c_id = 1;

      strncpy(cust.name, "javer", 9);

     strncpy(cust.address, "chengdu", 19);

       cust.age = 32;

  /* 首先创建数据库句柄 */

    ret = db_create(&dbp, NULL, 0);

 print_error(ret);

    /* 创建数据库标志 */

        flags = DB_CREATE;

  /* 创建一个名为single.db的数据库,使用B+树访问算法,本段代码演示对简单数据类型的处理 */

     ret = dbp->open(dbp, NULL, "single.db", NULL, DB_BTREE, flags, 0);

      print_error(ret);

       init_DBT(&key, &data);

    /* 分别对关键字和数据赋值和规定长度 */

      key.data = fruit;

   key.size = strlen(fruit) + 1;

       data.data = &number;

    data.size = sizeof(int);

        /* 把记录写入数据库中,不允许覆盖关键字相同的记录 */

        ret = dbp->put(dbp, NULL, &key, &data,DB_NOOVERWRITE);

  print_error(ret);

/* 手动把缓存中的数据刷新到硬盘文件中,实际上在关闭数据库时,数据会被自动刷新 */

dbp->sync();

init_DBT(&key, &data);

      key.data = fruit;

   key.size = strlen(fruit) + 1;

    /* 从数据库中查询关键字为apple的记录 */

     ret = dbp->get(dbp, NULL, &key, &data, 0);

       print_error(ret);

       /* 特别要注意数据结构DBT的字段data为void *型,所以在对data赋值和取值时,要做必要的类型转换。 */

     printf("The number = %d\n", *(int*)(data.data));

    if(dbp != NULL)

             dbp->close(dbp, 0);

 ret = db_create(&dbp, NULL, 0);

 print_error(ret);

       flags = DB_CREATE;

  /* 创建一个名为complex.db的数据库,使用HASH访问算法,本段代码演示对复杂数据结构的处理 */

    ret = dbp->open(dbp, NULL, "complex.db", NULL, DB_HASH, flags, 0);

      print_error(ret);

       init_DBT(&key, &data);

  key.size = sizeof(int);

     key.data = &(cust.c_id);

    data.size = sizeof(CUSTOMER);

       data.data = &cust;

  ret = dbp->put(dbp, NULL, &key, &data,DB_NOOVERWRITE);

   print_error(ret);

    memset(&cust, 0, sizeof(CUSTOMER));

    key.size = sizeof(int);

     key.data = &key_cust_c_id;

  data.data = &cust;

      data.ulen = sizeof(CUSTOMER);

      data.flags = DB_DBT_USERMEM;

    dbp->get(dbp, NULL, &key, &data, 0);

     print_error(ret);

    printf("c_id = %d name = %s address = %s age = %d\n",

              cust.c_id, cust.name, cust.address, cust.age);

    if(dbp != NULL)

             dbp->close(dbp, 0);

}

DB游标使用范例
游标是依赖于数据库句柄的,应用程序代码框架如下:



     /* 定义一个游标变量 */

      DBC * cur;

  /* 首先打开数据库,再打开游标 */

    dbp->open(dbp, ……);

    dbp->cursor(dbp, NULL, &cur, 0);

    /* do something with cursor */

  /* 首先关闭,在关闭数据库 */

        cur->c_close(cur);

       dbp->close(dbp, 0);

      

在游标打开后,可以以多种方式遍历特定记录。



             Memset(&key, 0, sizeof(DBT));

   Memset(&data, 0, sizeof(DBT));

      /* 因为KEY和DATA为空,则游标遍历整个数据库记录 */

   While((ret = cur->c_get(cur, &key, &data, DB_NEXT)) == 0)

        {

           /* do something with key and data */

        }

   

当想查询特定关键字对应的记录,则应对关键字赋值,并把cur->c_get()函数中标志位设置为DB_SET。例如:



             key.data = "xxxxx";

 key.size =  XXX;

    While((ret = cur->c_get(cur, &key, &data, DB_SET)) == 0)

 {

           /* do something with key and data */

        }

   

游标的作用还有很多,如查询多重记录,插入/修改/删除记录等。

DB环境使用范例
本文前面已说明环境是DB数据库的包装器,提供多种高级功能。应用程序代码框架如下:



             /* 定义一个环境变量,并创建 */

      DB_ENV *dbenv;

      db_env_create(&dbenv, 0);

/* 在环境打开之前,可调用形式为dbenv->set_XXX()的若干函数设置环境 */

 /* 通知DB使用Rijndael加密算法(参考资料4)对数据进行处理 */

dbenv->set_encrypt(dbenv, "encrypt_string", DB_ENCRYPT_AES);

       /* 设置DB的缓存为5M */

dbenv->set_cachesize(dbenv, 0, 5 * 1024 * 1024, 0);

/* 设置DB查找数据库文件的目录 */

        dbenv->set_data_dir(dbenv, "/usr/javer/work_db");

    /* 打开数据库环境,注意后四个标志分别指示DB启动日志、加锁、缓存、事务处理子系统 */

  dbenv->open(dbenv,home,DB_CREATE|DB_INIT_LOG|DB_INIT_LOCK| DB_INIT_MPOOL|DB_INIT_TXN, 0);

    /* 在环境打开后,则可以打开若干个数据库,所有数据库的处理都在环境的控制和保护中。注意db_create函数的第二个参数是环境变量 */

db_create(&dbp1, dbenv, 0);

dbp1->open(dbp1, ……);

db_create(&dbp2, dbenv, 0);

dbp1->open(dbp2, ……);

 /* do something with the database */

    /* 最后首先关闭打开的数据库,再关闭环境 */

  dbp2->close(dbp2, 0);

    dbp1->close(dbp1, 0);

    dbenv->close(dbenv, 0);

  

DB软件的安装和编译
从DB的官方站点http://www.sleepycat.com/下载最新的软件包db-4.3.27.tar.gz,解压到工作目录,进入该目录,依次执行下列三条命令即可。



../dist/configure

make

make install

执行make uninstall,则可卸载已安装的DB软件。

DB缺省把库和头文件安装在目录/usr/local/BerkeleyDB.4.3/下,使用gcc test.c -ggdb-I/usr/local/BerkeleyDB.4.3/include/ -L/usr/local/BerkeleyDB.4.3/lib/-ldb -lpthread就可正确编译程序。如果读者的测试主机操作系统为REDHAT9,则安装的DB版本可能是4.0。特别要注意到这两个版本的库是不兼容 的。例如打开数据库函数DB->open(),在4.0版本中入参为6个,而在4.3版中则为7个(可自行比较两个库的头文件db.h中 DB->open函数的定义)。因为在DB相关的应用程序中,open函数基本上都是要执行的,所以如果函数和版本不匹配,编译肯定会出错。当然, 编译完成后,可以使用命令ldd查看库的依赖关系。

总结
DB是一个具有工业强度的嵌入式数据库系统,数据处理的效率很高。DB功能的稳定性历经时间的考验,在大量应用程序中使用便是明证。可以想见,在同等代码 质量的条件下,软件的BUG数和代码的长度是成正比的,相对几十兆、几百兆大型数据库软件,DB的只有不到500K的大小!

从实现功能上看,DB是轻量级数据库系统,或可称为"极"轻量级数据库系统。但是,我认为不能因此而心存轻视之意,所谓"尺有所短,寸有所长",以绝对角 度比较工具之间的好坏是没有什么意义的,关键在于对工具的选择和运用(似乎可以参考一下极限编程的思想)。也许,正确的"表达范式"应该是:在当前应用背 景下,选择这种工具是最合适的。

来源:http://www-128.ibm.com/developerworks/cn/linux/l-embdb/ 

推荐(0)
收藏

sqlite 入门初步

2008-06-06 8:29 pm

译者: zhuyubing@gmail.com

这里告诉你怎么开始实验 SQLite ,这里没有长长的说明和配置。

下载代码
http://www.sqlite.org/download.html
目前的最新版本: http://www.sqlite.org/sqlite-3.4.0.tar.gz

取得一份二进制拷贝, 或者是源代码并自己编译它. 关于下载页(download) 的更多信息.

    ./configure      ;#  Run the configure script
make                     ;#  Run the makefile.
make install

创建一个新数据库

在命令行下, 输入: "sqlite3 test.db". 将创建一个新的数据库文件名叫"test.db". (你可以使用不同的名字)

输入 SQL 命令在提示符下创建和写入新的数据.

$ sqlite3 ex1
SQLite version 3.3.10
Enter ".help" for instructions
sqlite> create table tbl1(one varchar(10), two smallint);
sqlite> insert into tbl1 values('hello!',10);
sqlite> insert into tbl1 values('goodbye', 20);
sqlite> select * from tbl1;
hello!|10
goodbye|20
sqlite>.q

下面是一个C程序的例子,显示怎么使用 sqlite 的 C/C++ 接口. 数据库的名字由第一个参数取得且第二个参数或更多的参数是 SQL 执行语句. 这个函数调用sqlite3_open() 在 22 行打开数据库, sqlite3_exec() 在 27 行执行 SQL 命令, 并且sqlite3_close() 在 31 行关闭数据库连接.

代码:

#include <stdio.h>
#include <sqlite3.h>

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}

int main(int argc, char **argv){
sqlite3 *db;
char *zErrMsg = 0;
int rc;

if( argc!=3 ){
fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
exit(1);
}
rc = sqlite3_open(argv[1], &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
}
sqlite3_close(db);
return 0;
}

将上面的文件保存为test.c
编译:

# gcc -o test test.c -lsqlite3

执行:

#./test ex1 "select * from tbl1"

 不使用回调查询数据库

上面介绍的 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 是一样的。是一个很普通的以结尾的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 < nRow ; i++ )
{
printf( “第 %d 条记录\n”, i+1 );
for( j = 0 ; j < nColumn; j++ )
{
printf( “字段名:%s  ?> 字段值:%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;
}

例子二:

#include <stdio.h>
#include <sqlite3.h>
int main(int argc, char **argv){
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char ** azResult;
int nrow,ncolumn;
int k;
if( argc!=3 ){
fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
exit(1);
}
rc = sqlite3_open(argv[1], &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
//rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
rc = sqlite3_get_table(db, argv[2], &azResult, &nrow, &ncolumn, &zErrMsg);

if( rc!=SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
} else {
for ( k = 1; k <= nrow; k++ )
{
printf(azResult[k*ncolumn]);
printf(azResult[k*ncolumn+1]);
}
}
sqlite3_close(db);
return 0;
}

推荐(0)
收藏

The MySQL C API

2008-06-06 8:19 pm

简介

C APIs包含在mysqlclient库文件当中与MySQL的源代码一块发行,用于连接到数据库和执行数据库查询。有一些例子在MySQL原代码的clients目录里。

MySQL C 变量类型

以下变量类型在MySQL的库当中定义。我们需要这些变量是为了使用MySQL的函数。这些变量有详细的解释,但是这些解释对于写代码来说并不重要。

MYSQL

要连接MYSQL,必须建立MYSQL实例,通过mysql_init初始化方能开始进行连接。

typedef struct st_mysql {
NET           net;            /* Communication parameters */
gptr          connector_fd;   /* ConnectorFd for SSL */
char          *host,*user,*passwd,*unix_socket,
*server_version,*host_info,*info,*db;
unsigned int  port,client_flag,server_capabilities;
unsigned int  protocol_version;
unsigned int  field_count;
unsigned int  server_status;
unsigned long thread_id;      /* Id for connection in server */
my_ulonglong affected_rows;
my_ulonglong insert_id;       /* id if insert on table with NEXTNR */
my_ulonglong extra_info;              /* Used by mysqlshow */
unsigned long packet_length;
enum mysql_status status;
MYSQL_FIELD   *fields;
MEM_ROOT      field_alloc;
my_bool       free_me;        /* If free in mysql_close */
my_bool       reconnect;      /* set to 1 if automatic reconnect */
struct st_mysql_options options;
char          scramble_buff[9];
struct charset_info_st *charset;
unsigned int  server_language;
} MYSQL;

MYSQL_RES

这个结构代表返回行的一个查询的(SELECT, SHOW, DESCRIBE, EXPLAIN)的结果。返回的数据称为“数据集”,在C的API里对应的就是MYSQL_RES,从数据库读取数据,最后就是从MYSQL_RES中读取数据。

typedef struct st_mysql_res {
my_ulonglong row_count;
unsigned int  field_count, current_field;
MYSQL_FIELD   *fields;
MYSQL_DATA    *data;
MYSQL_ROWS    *data_cursor;
MEM_ROOT      field_alloc;
MYSQL_ROW     row;            /* If unbuffered read */
MYSQL_ROW     current_row;    /* buffer to current row */
unsigned long *lengths;       /* column lengths of current row */
MYSQL         *handle;        /* for unbuffered reads */
my_bool       eof;            /* Used my mysql_fetch_row */
} MYSQL_RES;

MYSQL_ROW

这是一个行数据的类型安全(type-safe)的表示。当前它实现为一个计数字节的字符串数组。(如果字段值可能包含二进制数据,你不能将这些视为空终止串,因为这样的值可以在内部包含空字节) 行通过调用mysql_fetch_row()获得。

typedef char **MYSQL_ROW;

MYSQL_FIELD

这个结构包含字段信息,例如字段名、类型和大小。其成员在下面更详细地描述。你可以通过重复调用mysql_fetch_field()对每一列获得MYSQL_FIELD结构。字段值不是这个结构的部分;它们被包含在一个MYSQL_ROW结构中。

typedef struct st_mysql_field {
char *name;                   /* Name of column */
char *table;                  /* Table of column if column was a field */
char *def;                    /* Default value (set by mysql_list_fields) */
enum enum_field_types type;   /* Type of field. Se mysql_com.h for types */
unsigned int length;          /* Width of column */
unsigned int max_length;      /* Max width of selected set */
unsigned int flags;           /* Div flags */
unsigned int decimals;        /* Number of decimals in field */
} MYSQL_FIELD;

my_ulonglong
typedef unsigned long my_ulonglong;

该类型用于行编号和mysql_affected_rows()、mysql_num_rows()和mysql_insert_id()。这种类型提供 0到1.84e19的一个范围。在一些系统上,试图打印类型my_ulonglong的值将不工作。为了打印出这样的值,将它变换到unsigned long并且使用一个%lu打印格式。例如:

printf ("Number of rows: %lu\n", (unsigned long) mysql_num_rows(result));

连接MySQL,查询数据

MySQL的库文件在mysqlclient,因此在编译MySQL程序的时候有必要加上-lmysqlclient编译选项。
MySQL 的头文件在/usr/include/mysql目录下(根据Linux的发行版本的不同,这个目录也有所不同),因此你的程序头部看起来有点这个样子:

#include <mysql.h>

MySQL的变量类型和函数都包含在这个头文件当中

然后,我们需要创建连接数据库的变量,可以简单地这么做:

MYSQL mysql;

在连接数据库之前,先调用以下函数初始化这个变量:

mysql_init(&mysql);

然后,调用mysql_real_connect函数:

MYSQL *         STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned int clientflag);

该函数被调用连接到数据库。host是MySQL服务器的主机名,user是登录的用户名,passwd是登录密码,db是要连接的数据库,port是 MySQL服务器的TCP/IP端口,unix_socket是连接类型,clientflag是MySQL运行成ODBC数据库的标记。连接寻建立后,这个函数返回0。

现在可以连接数据库,进行查询了:

char *query;

使用这个字符串我们可以创立任何SQL查询语句进行查询。执行这个查询的函数是:

int STDCALL mysql_real_query(MYSQL *mysql, const char *q, unsigned int length);

mysql是我们前面用过的变量,q是SQL查询语句,length是这个查询语句的长度。如果查询成功,函数返回0。

查询之后,我们要到一个MYSQL_RES变量来使用查询的结果。以下这行创立这个变量:

MYSQL_RES *res;

然后

 res = mysql_store_result(&mysql);

对客户端而言,有两种方法处理结果集合。一种方法是通过调用mysql_store_result()立刻检索全部结果。该函数从服务器获得查询返回的所有行,并将他们存储在客户端。第二种方法是对客户通过调用mysql_use_result()初始化一个一行一行地结果集合的检索。该函数初始化检索,但是实际上不从服务器获得任何行。

在两种情况中,你通过mysql_fetch_row()存取行。用mysql_store_result()、mysql_fetch_row()储存取已经从服务器被取出的行。用mysql_use_result()、mysql_fetch_row()实际上从服务器检索行。调用 mysql_fetch_lengths()可获得关于每行中数据值尺寸的信息。

在你用完一个结果集合以后,调用mysql_free_result()释放由它使用的内存。

两种检索机制是互补的。客户程序应该选择最适合他们的要求的途径。在实践中,客户通常更愿意使用mysql_store_result()。

该函数读出查询结果。

尽管可以很容易地查询了,要用这个查询的结果还要用到其它的函数。第一个是:

 MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);

该函数把结果转换成“数组”。你可能注意到了,该函数返回的是MYSQL_ROW变量类型。以下语句创立那样的变量:

 MYSQL_ROW row = mysql_fetch_row(res)

如前所解释的,变量row是一个字符串数组。也就是说,row[0]是数组的第一个值,row[1]是数组的第二个值...当我们用mysql_fetch_row的时候,接着变量row会取得结果的下一组的数据。当到了结果的尾部,该函数返回一负值。

使用数据集结束后,记得释放数据集,否则会发生内存泄漏,释放数据集函数如下:

void mysql_free_result(MYSQL_RES *result)

释放由mysql_store_result()、mysql_use_result()、mysql_list_dbs()等为一个结果集合分配的内存。当你用完了一个结果集合时,你必须调用mysql_free_result()来释放它使用的内存。

最后我们要关闭这个连接:

mysql_close(&mysql);

例子程序

执行一个select操作,从数据库中取数据,并执行一个insert操作,往数据库中插入数据,根据这两个操作你可以自由的扩展为任意数据库操作,
准备条件

1、已经安装mysql,上有数据库test,如果没有执行Create Databse test

建立数据库

2、test数据库上有表t1,如果没有,执行CREATE TABLE `t1` (
`id` int(11) default NULL,
`name` varchar(100) default NULL
)

建立表t1
testsql.c:

  #include <mysql.h>
#include <stdio.h>
int main(){
MYSQL mysql;     // need a instance to init
MYSQL_RES *res;
MYSQL_ROW row;
char *query;
int t,r;
// connect the database
mysql_init(&mysql);
if (!mysql_real_connect(&mysql,"localhost", "mmim", "mmim", "test",0,NULL,0))
{
printf( "Error connecting to database: %s\n",mysql_error(&mysql));
}
else printf("Connected...\n");

// get the result from the executing select query
query = "select * from t1";

t = mysql_real_query(&mysql,query,(unsigned int) strlen(query));
if (t)
{
printf("Error making query: %s\n",
mysql_error(&mysql));
}
else printf("[%s] made...\n", query);
res = mysql_store_result(&mysql);
while(row = mysql_fetch_row(res))
{
for(t=0;t<mysql_num_fields(res);t++)
{
printf("%s ",row[t]);
}
printf("\n");
}

printf("mysql_free_result...\n");
mysql_free_result(res);     //free result after you get the result

sleep(1);

// execute the insert query
query = "insert into t1(id, name) values(3, 'kunp')";
t = mysql_real_query(&mysql,query,(unsigned int) strlen(query));
if (t)
{
printf("Error making query: %s\n",
mysql_error(&mysql));
}
else printf("[%s] made...\n", query);

mysql_close(&mysql);

return 0;
}

编译

假定mysql的头文件在/usr/include/mysql,库文件在/usr/lib/mysql,执行下列命令进行编译:
gcc testsql.c -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient

推荐(0)
收藏

分布式计算与DHT网络

2008-06-06 7:43 pm

引用:http://www.wikilib.com/wiki?title=DHT%E7%BD%91%E7%BB%9C

分布式散列表(英语:Distributed Hash Table,简称DHT)是分布式计算系统中的一类,用来将一个关键值(key)的集合分散到所有在分布式系统中的节点,并且可以有效地将讯息转送到唯一一个拥有查询者提供的关键值的节点(Peers)。这里的节点类似散列表中的储存位置。分布式散列表通常是为了拥有极大节点数量的系统,而且在系统的节点常常会加入或离开(例如网络断线)而设计的。在一个结构性的延展网络(overlay network)中,参加的节点需要与系统中一小部份的节点沟通,这也需要使用分布式散列表。分布式散列表可以用以建立更复杂的服务,例如分布式档案系统、点对点技术档案分享系统、合作的网页快取、多播、任播(anycast)、网域名称系统以及即时通讯等。
目录

* 1 发展背景
* 2 性质
* 3 结构
o 3.1 关键值空间分割
o 3.2 延展网络
* 4 范例
o 4.1 分布式散列表实作与协定
o 4.2 分布式散列表的应用
* 5 参见
* 6 参考资料
* 7 外部链接

 发展背景

研究分布式散列表的主要动机是为了开发点对点系统,像是Napster、Gnutella及Freenet。这些系统得益于使用分散在因特网上的各项资源以提供实用的应用,特别在带宽及硬盘储存空间上,他们所提供的档案分享功能因此得到最大的好处。

这些系统使用不同的方法来解决如何找到拥有某资料的节点的问题。Napster 使用中央的索引服务器:每个节点加入网络的同时,会将他们所拥有的档案列表传送给服务器,这使得服务器可以进行搜寻并将结果回传给进行查询的节点。但中央索引服务器让整个系统易受攻击,且可能造成法律问题。于是,Gnutella 和相似的网络改用大量查询模式(flooding query model):每次搜寻都会把查询讯息广播给网络上的所有节点。虽然这个方式能够防止单点故障(single point of failure),但比起 Napster 来说却极没效率。

最后,Freenet 使用了完全分布式的系统,但它建置了一套使用经验法则的基于关键值的转送方法(key based routing)。在这个方法中,每个档案与一个关键值相结合,而拥有相似关键值的档案会倾向被相似的节点构成的集合所保管。于是查询讯息就可以根据它所提供的关键值被转送到该集合,而不需要经过所有的节点。然而,Freenet 并不保证存在网络上的资料在查询时一定会被找到。

分布式散列表为了达到 Gnutella 与 Freenet 的分散性(decentralization)以及 Napster 的效率与正确结果,使用了较为结构化的基于关键值的转送方法。不过分布式散列表也有个 Freenet 有的缺点,就是只能作精确搜寻,而不能只提供部份的关键字;但这个功能可以在分布式散列表的上层实做。

最初的四项分布式散列表技术——内容可寻址网络(Content addressable network,CAN)、Chord(Chord project)[1]、Pastry(Pastry (DHT)),以及 Tapestry (DHT)(Tapestry (DHT))皆同时于2001年发表。从那时开始,相关的研究便一直十分活跃。在学术领域以外,分布式散列表技术已经被应用在BitTorrent及CoralCDN(Coral Content Distribution Network)等。

 性质

分布式散列表本质上强调以下特性:

* 分散性:构成系统的节点并没有任何中央式的协调机制。
* 规模性:即使有成千上万个节点,系统仍然应该十分有效率。
* 容错:即使节点不断地加入、离开或是停止工作,系统仍然必须达到一定的可靠度。

要达到以上的目标,有一个关键的技术:任一个节点只需要与系统中的部份节点沟通。一般来说,若系统有n 个节点,那麽只有 Θ(logn) 个节点是必须的(见后述)。因此,当成员改变的时候,只有一部分的工作(例如资料或关键值的传送,散列表的改变等)必须要完成。

有些分布式散列表的设计寻求能对抗网络中恶意的节点的安全性,但仍然保留参加节点的匿名性。在其他的点对点系统(特别是档案分享)中较为少见。参见匿名点对点技术。

最后,分布式散列表必须处理传统分布式系统可能遇到的问题,例如负载平衡、资料完整性,以及效能问题(特别是确认转送讯息、资料储存及读取等动作能快速完成)。

 结构

分布式散列表的结构可以分成几个主要的元件[2][3]。其基础是一个抽象的关键值空间(keyspace),例如说所有160位元长的字符串集合。关键值空间分割(keyspace partitioning)将关键值空间分割成数个,并指定到在此系统的节点中。而延展网络则连接这些节点,并让他们能够借由在关键值空间内的任一值找到拥有该值的节点。

当这些元件都准备好后,一般使用分布式散列表来储存与读取的方式如下所述。假设关键值空间是一个160位元长的字符串集合。为了在分布式散列表中储存一个档案,名称为 filename 且内容为 data,我们计算出 filename 的 SHA1 散列值——一个160位元的关键值 k——并将讯息 put(k,data) 送给分布式散列表中的任意参与节点。此讯息在延展网络中被转送,直到抵达在关键值空间分割中被指定负责储存关键值 k 的节点。而 (k,data) 即储存在该节点。其他的节点只需要重新计算 filename 的散列值 k,然后送出讯息 get(k) 给分布式散列表中的任意参与节点,以此来找与 k 相关的资料。此讯息也会在延展网络中被转送到负责储存 k 的节点。而此节点则会负责传回储存的资料 data。

以下分别描述关键值空间分割及延展网络的基本概念。这些概念在大多数的分布式散列表实作中是相同的,但设计的细节部份则大多不同。

 关键值空间分割

大多数的分布式散列表使用某些稳定散列(consistent hashing)方法来将关键值对应到节点。此方法使用了一个函数 δ(k1,k2) 来定义一个抽象的概念:从关键值k1 到 k2 的距离。每个节点被指定了一个关键值,称为ID。ID 为 i 的节点拥有根据函数 δ 计算,最接近 i 的所有关键值。

例:Chord 分布式散列表实作将关键值视为一个圆上的点,而 δ(k1,k2) 则是沿著圆顺时钟地从 k1 走到 k2 的距离。结果,圆形的关键值空间就被切成连续的圆弧段,而每段的端点都是节点的ID。如果 i1 与 i2 是邻近的 ID,则 ID 为 i2 的节点拥有落在 i1 及 i2 之间的所有关键值。

稳定散列拥有一个基本的性质:增加或移除节点只改变邻近ID的节点所拥有的关键值集合,而其他节点的则不会被改变。对比于传统的散列表,若增加或移除一个位置,则整个关键值空间就必须重新对应。由于拥有资料的改变通常会导致资料从分布式散列表中的一个节点被搬到另一个节点,而这是非常浪费带宽的,因此若要有效率地支援大量密集的节点增加或离开的动作,这种重新配置的行为必须尽量减少。

 延展网络

每个节点保有一些到其他节点(它的邻居)的连结。将这些连结总合起来就形成延展网络。而这些连结是使用一个结构性的方式来挑选的,称为网络拓撲。

所有的分布式散列表实作拓撲有某些基本的性质:对于任一关键值 k,某个节点要不就拥有 k,要不就拥有一个连结能连结到距离较接近 k 的节点。因此使用以下的贪心算法即可容易地将讯息转送到拥有关键值 k 的节点:在每次执行时,将讯息转送到 ID 较接近 k 的邻近节点。若没有这样的节点,那我们一定抵达了最接近 k 的节点,也就是拥有 k 的节点。这样的转送方法有时被称为“基于关键值的转送方法”。

除了基本的转送正确性之外,拓撲中另有两个关键的限制:其一为保证任何的转送路径长度必须尽量短,因而请求能快速地被完成;其二为任一节点的邻近节点数目(又称最大节点度(Degree (graph theory)))必须尽量少,因此维护的花费不会过多。当然,转送长度越短,则最大节点度越大。以下列出常见的最大节点度及转送长度(n 为分布式散列表中的节点数)

* 最大节点度 O(1),转送长度 O(logn)
* 最大节点度 O(logn),转送长度 O(logn / loglogn)
* 最大节点度 O(logn),转送长度 O(logn)
* 最大节点度 O(n1 / 2),转送长度 O(1)

第三个选择最为常见。虽然他在最大节点度与转送长度的取舍中并不是最佳的选择,但这样的拓撲允许较为有弹性地选择邻近节点。许多分布式散列表实作利用这种弹性来选择延迟较低的邻近节点。

最大的转送长度与直径有关:最远的两节点之间的最短距离。无疑地,网络的最大转送长度至少要与它的直径一样长,因而拓撲也被最大节点度与直径的取舍限制住[4],而这在图论中是基本的性质。因为贪婪算法(Greed Method)可能找不到最短路径,因此转送长度可能比直径长[5]。

 范例

分布式散列表实作与协定

* Bamboo[6]
* Bunshin[7]
* 内容可寻址网络 (Content Addressable Network)
* Chord
* DKS系统[8]
* Kademlia
* Leopard
* MACE[9]
* Pastry
* P-Grid
* Tapestry

分布式散列表的应用

* BitTorrent:档案分享应用。BitTorrent 可以选用DHT作为分布式Tracker。
* Warez P2P:档案分享应用。
* The Circle:档案分享应用与聊天。
* CSpace:安全的沟通系统。
* Codeen:网页快取。
* CoralCDN
* Dijjer
* eMule:档案分享应用。
* I2P:匿名网络。
* JXTA:开放原始码的点对点平台。
* NEOnet:档案分享应用。
* Overnet:档案分享应用。

参见

* memcached:一个高效能、分布式的物件快取系统。

参考资料

  1.  (英文) Hari Balakrishnan, M. Frans Kaashoek, David Karger, Robert Morris 与 Ion Stoica, Looking up data in P2P systems. 于 Communications of the ACM, 2003年1月
  2. (英文) Moni Naor 与 Udi Wieder. Novel Architectures for P2P Applications: the Continuous-Discrete Approach. Proc. SPAA, 2003年
  3. (英文) Gurmeet Singh Manku. Dipsea: A Modular Distributed Hash Table. 博士论文 (史丹佛大学),2004年8月
  4. (英文) http://maite71.upc.es/grup_de_grafs/table_g.html
  5. (英文) Gurmeet Singh Manku, Moni Naor, and Udi Wieder. Know thy Neighbor's Neighbor: the Power of Lookahead in Randomized P2P Networks. Proc. STOC, 2004.
  6.  http://www.bamboo-dht.org/
  7.  http://planet.urv.es/bunshin/
  8.  http://dks.sics.se
  9.  http://mace.ucsd.edu

外部链接

推荐(0)
收藏

SED单行脚本快速参考

2008-06-04 3:09 pm
-------------------------------------------------------------------------
SED单行脚本快速参考(Unix 流编辑器)                       2005年12月29日

英文标题:USEFUL ONE-LINE SCRIPTS FOR SED (Unix stream editor)
原标题:HANDY ONE-LINERS FOR SED (Unix stream editor)

整理:Eric Pement  - 电邮:pemente[at]northpark[dot]edu         版本5.5
译者:Joe Hong     - 电邮:hq00e[at]126[dot]com

在以下地址可找到本文档的最新(英文)版本:
   http://sed.sourceforge.net/sed1line.txt
   http://www.pement.org/sed/sed1line.txt

其他语言版本:
  中文          - http://sed.sourceforge.net/sed1line_zh-CN.html
  捷克语        - http://sed.sourceforge.net/sed1line_cz.html
  荷语          - http://sed.sourceforge.net/sed1line_nl.html
  法语          - http://sed.sourceforge.net/sed1line_fr.html
  德语          - http://sed.sourceforge.net/sed1line_de.html

  葡语          - http://sed.sourceforge.net/sed1line_pt-BR.html


文本间隔:
--------

 # 在每一行后面增加一空行
 sed G

 # 将原来的所有空行删除并在每一行后面增加一空行。
 # 这样在输出的文本中每一行后面将有且只有一空行。
 sed '/^$/d;G'

 # 在每一行后面增加两行空行
 sed 'G;G'

 # 将第一个脚本所产生的所有空行删除(即删除所有偶数行)
 sed 'n;d'

 # 在匹配式样“regex”的行之前插入一空行
 sed '/regex/{x;p;x;}'

 # 在匹配式样“regex”的行之后插入一空行
 sed '/regex/G'

 # 在匹配式样“regex”的行之前和之后各插入一空行
 sed '/regex/{x;p;x;G;}'

编号:
--------

 # 为文件中的每一行进行编号(简单的左对齐方式)。这里使用了“制表符”
 # (tab,见本文末尾关于'\t'的用法的描述)而不是空格来对齐边缘。
 sed = filename | sed 'N;s/\n/\t/'

 # 对文件中的所有行编号(行号在左,文字右端对齐)。
 sed = filename | sed 'N; s/^/     /; s/ *\(.\{6,\}\)\n/\1  /'

 # 对文件中的所有行编号,但只显示非空白行的行号。
 sed '/./=' filename | sed '/./N; s/\n/ /'

 # 计算行数 (模拟 "wc -l")
 sed -n '$='

文本转换和替代:
--------

 # Unix环境:转换DOS的新行符(CR/LF)为Unix格式。
 sed 's/.$//'                     # 假设所有行以CR/LF结束
 sed 's/^M$//'                    # 在bash/tcsh中,将按Ctrl-M改为按Ctrl-V
 sed 's/\x0D$//'                  # ssed、gsed 3.02.80,及更高版本

 # Unix环境:转换Unix的新行符(LF)为DOS格式。
 sed "s/$/`echo -e \\\r`/"        # 在ksh下所使用的命令
 sed 's/$'"/`echo \\\r`/"         # 在bash下所使用的命令
 sed "s/$/`echo \\\r`/"           # 在zsh下所使用的命令
 sed 's/$/\r/'                    # gsed 3.02.80 及更高版本

 # DOS环境:转换Unix新行符(LF)为DOS格式。
 sed "s/$//"                      # 方法 1
 sed -n p                         # 方法 2

 # DOS环境:转换DOS新行符(CR/LF)为Unix格式。
 # 下面的脚本只对UnxUtils sed 4.0.7 及更高版本有效。要识别UnxUtils版本的
 #  sed可以通过其特有的“--text”选项。你可以使用帮助选项(“--help”)看
 # 其中有无一个“--text”项以此来判断所使用的是否是UnxUtils版本。其它DOS
 # 版本的的sed则无法进行这一转换。但可以用“tr”来实现这一转换。
 sed "s/\r//" infile >outfile     # UnxUtils sed v4.0.7 或更高版本
 tr -d \r <infile >outfile        # GNU tr 1.22 或更高版本

 # 将每一行前导的“空白字符”(空格,制表符)删除
 # 使之左对齐
 sed 's/^[ \t]*//'                # 见本文末尾关于'\t'用法的描述

 # 将每一行拖尾的“空白字符”(空格,制表符)删除
 sed 's/[ \t]*$//'                # 见本文末尾关于'\t'用法的描述

 # 将每一行中的前导和拖尾的空白字符删除
 sed 's/^[ \t]*//;s/[ \t]*$//'

 # 在每一行开头处插入5个空格(使全文向右移动5个字符的位置)
 sed 's/^/     /'

 # 以79个字符为宽度,将所有文本右对齐
 sed -e :a -e 's/^.\{1,78\}$/ &/;ta'  # 78个字符外加最后的一个空格

 # 以79个字符为宽度,使所有文本居中。在方法1中,为了让文本居中每一行的前
 # 头和后头都填充了空格。 在方法2中,在居中文本的过程中只在文本的前面填充
 # 空格,并且最终这些空格将有一半会被删除。此外每一行的后头并未填充空格。
 sed  -e :a -e 's/^.\{1,77\}$/ & /;ta'                     # 方法1
 sed  -e :a -e 's/^.\{1,77\}$/ &/;ta' -e 's/\( *\)\1/\1/'  # 方法2

 # 在每一行中查找字串“foo”,并将找到的“foo”替换为“bar”
 sed 's/foo/bar/'                 # 只替换每一行中的第一个“foo”字串
 sed 's/foo/bar/4'                # 只替换每一行中的第四个“foo”字串
 sed 's/foo/bar/g'                # 将每一行中的所有“foo”都换成“bar”
 sed 's/\(.*\)foo\(.*foo\)/\1bar\2/' # 替换倒数第二个“foo”
 sed 's/\(.*\)foo/\1bar/'            # 替换最后一个“foo”

 # 只在行中出现字串“baz”的情况下将“foo”替换成“bar”
 sed '/baz/s/foo/bar/g'

 # 将“foo”替换成“bar”,并且只在行中未出现字串“baz”的情况下替换
 sed '/baz/!s/foo/bar/g'

 # 不管是“scarlet”“ruby”还是“puce”,一律换成“red”
 sed 's/scarlet/red/g;s/ruby/red/g;s/puce/red/g'  #对多数的sed都有效
 gsed 's/scarlet\|ruby\|puce/red/g'               # 只对GNU sed有效

 # 倒置所有行,第一行成为最后一行,依次类推(模拟“tac”)。
 # 由于某些原因,使用下面命令时HHsed v1.5会将文件中的空行删除
 sed '1!G;h;$!d'               # 方法1
 sed -n '1!G;h;$p'             # 方法2

 # 将行中的字符逆序排列,第一个字成为最后一字,……(模拟“rev”)
 sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//'

 # 将每两行连接成一行(类似“paste”)
 sed '$!N;s/\n/ /'

 # 如果当前行以反斜杠“\”结束,则将下一行并到当前行末尾
 # 并去掉原来行尾的反斜杠
 sed -e :a -e '/\\$/N; s/\\\n//; ta'

 # 如果当前行以等号开头,将当前行并到上一行末尾
 # 并以单个空格代替原来行头的“=”
 sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D'

 # 为数字字串增加逗号分隔符号,将“1234567”改为“1,234,567”
 gsed ':a;s/\B[0-9]\{3\}\>/,&/;ta'                     # GNU sed
 sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta'  # 其他sed

 # 为带有小数点和负号的数值增加逗号分隔符(GNU sed)
 gsed -r ':a;s/(^|[^0-9.])([0-9]+)([0-9]{3})/\1\2,\3/g;ta'

 # 在每5行后增加一空白行 (在第5,10,15,20,等行后增加一空白行)
 gsed '0~5G'                      # 只对GNU sed有效
 sed 'n;n;n;n;G;'                 # 其他sed

选择性地显示特定行:
--------

 # 显示文件中的前10行 (模拟“head”的行为)
 sed 10q

 # 显示文件中的第一行 (模拟“head -1”命令)
 sed q

 # 显示文件中的最后10行 (模拟“tail”)
 sed -e :a -e '$q;N;11,$D;ba'

 # 显示文件中的最后2行(模拟“tail -2”命令)
 sed '$!N;$!D'

 # 显示文件中的最后一行(模拟“tail -1”)
 sed '$!d'                        # 方法1
 sed -n '$p'                      # 方法2

 # 显示文件中的倒数第二行
 sed -e '$!{h;d;}' -e x              # 当文件中只有一行时,输入空行
 sed -e '1{$q;}' -e '$!{h;d;}' -e x  # 当文件中只有一行时,显示该行
 sed -e '1{$d;}' -e '$!{h;d;}' -e x  # 当文件中只有一行时,不输出

 # 只显示匹配正则表达式的行(模拟“grep”)
 sed -n '/regexp/p'               # 方法1
 sed '/regexp/!d'                 # 方法2

 # 只显示“不”匹配正则表达式的行(模拟“grep -v”)
 sed -n '/regexp/!p'              # 方法1,与前面的命令相对应
 sed '/regexp/d'                  # 方法2,类似的语法

 # 查找“regexp”并将匹配行的上一行显示出来,但并不显示匹配行
 sed -n '/regexp/{g;1!p;};h'

 # 查找“regexp”并将匹配行的下一行显示出来,但并不显示匹配行
 sed -n '/regexp/{n;p;}'

 # 显示包含“regexp”的行及其前后行,并在第一行之前加上“regexp”所
 # 在行的行号 (类似“grep -A1 -B1”)
 sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h

 # 显示包含“AAA”、“BBB”或“CCC”的行(任意次序)
 sed '/AAA/!d; /BBB/!d; /CCC/!d'  # 字串的次序不影响结果

 # 显示包含“AAA”、“BBB”和“CCC”的行(固定次序)
 sed '/AAA.*BBB.*CCC/!d'

 # 显示包含“AAA”“BBB”或“CCC”的行 (模拟“egrep”)
 sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d    # 多数sed
 gsed '/AAA\|BBB\|CCC/!d'                        # 对GNU sed有效

 # 显示包含“AAA”的段落 (段落间以空行分隔)
 # HHsed v1.5 必须在“x;”后加入“G;”,接下来的3个脚本都是这样
 sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;'

 # 显示包含“AAA”“BBB”和“CCC”三个字串的段落 (任意次序)
 sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;/BBB/!d;/CCC/!d'

 # 显示包含“AAA”、“BBB”、“CCC”三者中任一字串的段落 (任意次序)
 sed -e '/./{H;$!d;}' -e 'x;/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
 gsed '/./{H;$!d;};x;/AAA\|BBB\|CCC/b;d'         # 只对GNU sed有效

 # 显示包含65个或以上字符的行
 sed -n '/^.\{65\}/p'

 # 显示包含65个以下字符的行
 sed -n '/^.\{65\}/!p'            # 方法1,与上面的脚本相对应
 sed '/^.\{65\}/d'                # 方法2,更简便一点的方法

 # 显示部分文本——从包含正则表达式的行开始到最后一行结束
 sed -n '/regexp/,$p'

 # 显示部分文本——指定行号范围(从第8至第12行,含8和12行)
 sed -n '8,12p'                   # 方法1
 sed '8,12!d'                     # 方法2

 # 显示第52行
 sed -n '52p'                     # 方法1
 sed '52!d'                       # 方法2
 sed '52q;d'                      # 方法3, 处理大文件时更有效率

 # 从第3行开始,每7行显示一次
 gsed -n '3~7p'                   # 只对GNU sed有效
 sed -n '3,${p;n;n;n;n;n;n;}'     # 其他sed

 # 显示两个正则表达式之间的文本(包含)
 sed -n '/Iowa/,/Montana/p'       # 区分大小写方式

选择性地删除特定行:
--------

 # 显示通篇文档,除了两个正则表达式之间的内容
 sed '/Iowa/,/Montana/d'

 # 删除文件中相邻的重复行(模拟“uniq”)
 # 只保留重复行中的第一行,其他行删除
 sed '$!N; /^\(.*\)\n\1$/!P; D'

 # 删除文件中的重复行,不管有无相邻。注意hold space所能支持的缓存
 # 大小,或者使用GNU sed。
 sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

 # 删除除重复行外的所有行(模拟“uniq -d”)
 sed '$!N; s/^\(.*\)\n\1$/\1/; t; D'

 # 删除文件中开头的10行
 sed '1,10d'

 # 删除文件中的最后一行
 sed '$d'

 # 删除文件中的最后两行
 sed 'N;$!P;$!D;$d'

 # 删除文件中的最后10行
 sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # 方法1
 sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # 方法2

 # 删除8的倍数行
 gsed '0~8d'                           # 只对GNU sed有效
 sed 'n;n;n;n;n;n;n;d;'                # 其他sed

 # 删除匹配式样的行
 sed '/pattern/d'                      # 删除含pattern的行。当然pattern
                                       # 可以换成任何有效的正则表达式

 # 删除文件中的所有空行(与“grep '.' ”效果相同)
 sed '/^$/d'                           # 方法1
 sed '/./!d'                           # 方法2

 # 只保留多个相邻空行的第一行。并且删除文件顶部和尾部的空行。
 # (模拟“cat -s”)
 sed '/./,/^$/!d'        #方法1,删除文件顶部的空行,允许尾部保留一空行
 sed '/^$/N;/\n$/D'      #方法2,允许顶部保留一空行,尾部不留空行

 # 只保留多个相邻空行的前两行。
 sed '/^$/N;/\n$/N;//D'

 # 删除文件顶部的所有空行
 sed '/./,$!d'

 # 删除文件尾部的所有空行
 sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'  # 对所有sed有效
 sed -e :a -e '/^\n*$/N;/\n$/ba'        # 同上,但只对 gsed 3.02.*有效

 # 删除每个段落的最后一行
 sed -n '/^$/{p;h;};/./{x;/./p;}'

特殊应用:
--------

 # 移除手册页(man page)中的nroff标记。在Unix System V或bash shell下使
 # 用'echo'命令时可能需要加上 -e 选项。
 sed "s/.`echo \\\b`//g"    # 外层的双括号是必须的(Unix环境)
 sed 's/.^H//g'             # 在bash或tcsh中, 按 Ctrl-V 再按 Ctrl-H
 sed 's/.\x08//g'           # sed 1.5,GNU sed,ssed所使用的十六进制的表示方法

 # 提取新闻组或 e-mail 的邮件头
 sed '/^$/q'                # 删除第一行空行后的所有内容

 # 提取新闻组或 e-mail 的正文部分
 sed '1,/^$/d'              # 删除第一行空行之前的所有内容

 # 从邮件头提取“Subject”(标题栏字段),并移除开头的“Subject:”字样
 sed '/^Subject: */!d; s///;q'

 # 从邮件头获得回复地址
 sed '/^Reply-To:/q; /^From:/h; /./d;g;q'

 # 获取邮件地址。在上一个脚本所产生的那一行邮件头的基础上进一步的将非电邮
 # 地址的部分剃除。(见上一脚本)
 sed 's/ *(.*)//; s/>.*//; s/.*[:<] *//'

 # 在每一行开头加上一个尖括号和空格(引用信息)
 sed 's/^/> /'

 # 将每一行开头处的尖括号和空格删除(解除引用)
 sed 's/^> //'

 # 移除大部分的HTML标签(包括跨行标签)
 sed -e :a -e 's/<[^>]*>//g;/</N;//ba'

 # 将分成多卷的uuencode文件解码。移除文件头信息,只保留uuencode编码部分。
 # 文件必须以特定顺序传给sed。下面第一种版本的脚本可以直接在命令行下输入;
 # 第二种版本则可以放入一个带执行权限的shell脚本中。(由Rahul Dhesi的一
 # 个脚本修改而来。)
 sed '/^end/,/^begin/d' file1 file2 ... fileX | uudecode   # vers. 1
 sed '/^end/,/^begin/d' "$@" | uudecode                    # vers. 2

 # 将文件中的段落以字母顺序排序。段落间以(一行或多行)空行分隔。GNU sed使用
 # 字元“\v”来表示垂直制表符,这里用它来作为换行符的占位符——当然你也可以
 # 用其他未在文件中使用的字符来代替它。
 sed '/./{H;d;};x;s/\n/={NL}=/g' file | sort | sed '1s/={NL}=//;s/={NL}=/\n/g'
 gsed '/./{H;d};x;y/\n/\v/' file | sort | sed '1s/\v//;y/\v/\n/'

 # 分别压缩每个.TXT文件,压缩后删除原来的文件并将压缩后的.ZIP文件
 # 命名为与原来相同的名字(只是扩展名不同)。(DOS环境:“dir /b”
 # 显示不带路径的文件名)。
 echo @echo off >zipup.bat
 dir /b *.txt | sed "s/^\(.*\)\.TXT/pkzip -mo \1 \1.TXT/" >>zipup.bat

使用SED:Sed接受一个或多个编辑命令,并且每读入一行后就依次应用这些命令。
当读入第一行输入后,sed对其应用所有的命令,然后将结果输出。接着再读入第二
行输入,对其应用所有的命令……并重复这个过程。上一个例子中sed由标准输入设
备(即命令解释器,通常是以管道输入的形式)获得输入。在命令行给出一个或多
个文件名作为参数时,这些文件取代标准输入设备成为sed的输入。sed的输出将被
送到标准输出(显示器)。因此:

 cat filename | sed '10q'         # 使用管道输入
 sed '10q' filename               # 同样效果,但不使用管道输入
 sed '10q' filename > newfile     # 将输出转移(重定向)到磁盘上

要了解sed命令的使用说明,包括如何通过脚本文件(而非从命令行)来使用这些命
令,请参阅《sed & awk》第二版,作者Dale Dougherty和Arnold Robbins
(O'Reilly,1997;http://www.ora.com),《UNIX Text Processing》,作者
Dale Dougherty和Tim O'Reilly(Hayden Books,1987)或者是Mike Arst写的教
程——压缩包的名称是“U-SEDIT2.ZIP”(在许多站点上都找得到)。要发掘sed
的潜力,则必须对“正则表达式”有足够的理解。正则表达式的资料可以看
《Mastering Regular Expressions》作者Jeffrey Friedl(O'reilly 1997)。
Unix系统所提供的手册页(“man”)也会有所帮助(试一下这些命令
“man sed”、“man regexp”,或者看“man ed”中关于正则表达式的部分),但
手册提供的信息比较“抽象”——这也是它一直为人所诟病的。不过,它本来就不
是用来教初学者如何使用sed或正则表达式的教材,而只是为那些熟悉这些工具的人
提供的一些文本参考。

括号语法:前面的例子对sed命令基本上都使用单引号('...')而非双引号
("...")这是因为sed通常是在Unix平台上使用。单引号下,Unix的shell(命令
解释器)不会对美元符($)和后引号(`...`)进行解释和执行。而在双引号下
美元符会被展开为变量或参数的值,后引号中的命令被执行并以输出的结果代替
后引号中的内容。而在“csh”及其衍生的shell中使用感叹号(!)时需要在其前
面加上转义用的反斜杠(就像这样:\!)以保证上面所使用的例子能正常运行
(包括使用单引号的情况下)。DOS版本的Sed则一律使用双引号("...")而不是
引号来圈起命令。

'\t'的用法:为了使本文保持行文简洁,我们在脚本中使用'\t'来表示一个制表
符。但是现在大部分版本的sed还不能识别'\t'的简写方式,因此当在命令行中为
脚本输入制表符时,你应该直接按TAB键来输入制表符而不是输入'\t'。下列的工
具软件都支持'\t'做为一个正则表达式的字元来表示制表符:awk、perl、HHsed、
sedmod以及GNU sed v3.02.80。

不同版本的SED:不同的版本间的sed会有些不同之处,可以想象它们之间在语法上
会有差异。具体而言,它们中大部分不支持在编辑命令中间使用标签(:name)或分
支命令(b,t),除非是放在那些的末尾。这篇文档中我们尽量选用了可移植性较高
的语法,以使大多数版本的sed的用户都能使用这些脚本。不过GNU版本的sed允许使
用更简洁的语法。想像一下当读者看到一个很长的命令时的心情:

   sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d

好消息是GNU sed能让命令更紧凑:

   sed '/AAA/b;/BBB/b;/CCC/b;d'      # 甚至可以写成
   sed '/AAA\|BBB\|CCC/b;d'

此外,请注意虽然许多版本的sed接受象“/one/ s/RE1/RE2/”这种在's'前带有空
格的命令,但这些版本中有些却不接受这样的命令:“/one/! s/RE1/RE2/”。这时
只需要把中间的空格去掉就行了。

速度优化:当由于某种原因(比如输入文件较大、处理器或硬盘较慢等)需要提高
命令执行速度时,可以考虑在替换命令(“s/.../.../”)前面加上地址表达式来
提高速度。举例来说:

   sed 's/foo/bar/g' filename         # 标准替换命令
   sed '/foo/ s/foo/bar/g' filename   # 速度更快
   sed '/foo/ s//bar/g' filename      # 简写形式

当只需要显示文件的前面的部分或需要删除后面的内容时,可以在脚本中使用“q”
命令(退出命令)。在处理大的文件时,这会节省大量时间。因此:

   sed -n '45,50p' filename           # 显示第45到50行
   sed -n '51q;45,50p' filename       # 一样,但快得多

如果你有其他的单行脚本想与大家分享或者你发现了本文档中错误的地方,请发电
子邮件给本文档的作者(Eric Pement)。邮件中请记得提供你所使用的sed版本、
该sed所运行的操作系统及对问题的适当描述。本文所指的单行脚本指命令行的长
度在65个字符或65个以下的sed脚本〔译注1〕。本文档的各种脚本是由以下所列作
者所写或提供:

 Al Aab                               # 建立了“seders”邮件列表
 Edgar Allen                          # 许多方面
 Yiorgos Adamopoulos                  # 许多方面
 Dale Dougherty                       # 《sed & awk》作者
 Carlos Duarte                        # 《do it with sed》作者
 Eric Pement                          # 本文档的作者
 Ken Pizzini                          # GNU sed v3.02 的作者
 S.G. Ravenhall                       # 去html标签脚本
 Greg Ubben                           # 有诸多贡献并提供了许多帮助
-------------------------------------------------------------------------

译注1:大部分情况下,sed脚本无论多长都能写成单行的形式(通过`-e'选项和`;'
号)——只要命令解释器支持,所以这里说的单行脚本除了能写成一行还对长度有
所限制。因为这些单行脚本的意义不在于它们是以单行的形式出现。而是让用户能
方便地在命令行中使用这些紧凑的脚本才是其意义所在。
推荐(0)
收藏

squid 日志分析

2008-05-14 12:38 pm

 

通过日志来查看 squid 的一些基本的运行状态:

1. access.log

配置语句:

logformat combined %>a %ui %un [%tl] "%rm %ru HTTP/%rv" %Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh:%tr
cache_access_log /data1/logs/access.log combined

打下的log格式为:

125.71.196.17 - - [14/May/2008:12:16:13 +0800] "GET http://you.video.sina.com.cn/b/13441121-1212188024.html HTTP/1.1" 200 8820 "http://you.video.sina.com.cn/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" TCP_MEM_HIT:NONE:21089

可以通过脚本查看一些统计信息,如各种反应状态所占的比例,通常较好的情况下HIT所占的比例(应该就是所谓的命中率)可以在70%~80%

阅读全文...

推荐(0)
收藏

C/C++头文件一览

2008-05-13 6:07 pm

C、传统 C++

#include <assert.h>    //设定插入点
#include <ctype.h>     //字符处理
#include <errno.h>     //定义错误码
#include <float.h>     //浮点数处理
#include <fstream.h>    //文件输入/输出
#include <iomanip.h>    //参数化输入/输出
#include <iostream.h>   //数据流输入/输出
#include <limits.h>    //定义各种数据类型最值常量
#include <locale.h>    //定义本地化函数
#include <math.h>     //定义数学函数
#include <stdio.h>     //定义输入/输出函数
#include <stdlib.h>    //定义杂项函数及内存分配函数
#include <string.h>    //字符串处理
#include <strstrea.h>   //基于数组的输入/输出
#include <time.h>     //定义关于时间的函数
#include <wchar.h>     //宽字符处理及输入/输出
#include <wctype.h>    //宽字符分类

//////////////////////////////////////////////////////////////////////////

标准 C++ (同上的不再注释)

#include <algorithm>    //STL 通用算法
#include <bitset>     //STL 位集容器
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>     //复数类
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>      //STL 双端队列容器
#include <exception>    //异常处理类
#include <fstream>
#include <functional>   //STL 定义运算函数(代替运算符)
#include <limits>
#include <list>      //STL 线性列表容器
#include <map>       //STL 映射容器
#include <iomanip>
#include <ios>       //基本输入/输出支持
#include <iosfwd>     //输入/输出系统使用的前置声明
#include <iostream>
#include <istream>     //基本输入流
#include <ostream>     //基本输出流
#include <queue>      //STL 队列容器
#include <set>       //STL 集合容器
#include <sstream>     //基于字符串的流
#include <stack>      //STL 堆栈容器
#include <stdexcept>    //标准异常类
#include <streambuf>    //底层输入/输出支持
#include <string>     //字符串类
#include <utility>     //STL 通用模板类
#include <vector>     //STL 动态数组容器
#include <cwchar>
#include <cwctype>

using namespace std;

//////////////////////////////////////////////////////////////////////////

C99 增加

#include <complex.h>   //复数处理
#include <fenv.h>    //浮点环境
#include <inttypes.h>  //整数格式转换
#include <stdbool.h>   //布尔环境
#include <stdint.h>   //整型环境
#include <tgmath.h>   //通用类型数学宏

推荐(0)
收藏

通用排序服务daemon

2008-05-12 1:22 pm

通用排序服务daemon,模型示例。

情景描述:

1. 需要排序的数据以 key=>value 的形式组织
2. 数据随时到达,尽可能的做到实时排序
3. 只需保留 Top n 的数据

典型应用:各种排行榜,搜索排行,点击排行等等

算法细节:

1. 数据使用 udp 包传送 (示例中还没有加上网络操作部分)
2. mmap 内存到文件。daemon 只负责排序部分的工作,输出排序结果由其他程序读取文件完成
3. 使用结构体数组存储 Top n 的已排序部分
4. 新数据到达时,使用插入排序

Todo list:
1. 增加网络接收部分
2. 增加网络输出 daemon
3. 使用链表存储 Top n ,方便插入排序。当需要输出时使用另外一个线程或子进程将链表拷贝到 mmap 内存区
4. 多 domain 测试
5. 支持 name 为字符串的情况,key 为 name 的 hash
6. 增量 value ,需要保存所有的数据,而不是 Top n
7. 配置文件支持


/**
 *  SinaSorter
 *
 *  by tangfulin#126.com
 *
 */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include 

#define MAXNAMELEN 31
#define MAXDOMAIN  31
#define DEBUG 1

typedef struct {
    unsigned long key;
    unsigned long value;
    //char name[MAXNAMELEN];
} RECORD;

int  sizer = sizeof(RECORD);
int  domainTotal=1;

char domainName[MAXDOMAIN][MAXNAMELEN]={};
int  totalRecord[MAXDOMAIN]={0};

RECORD *head[MAXDOMAIN]={NULL};
RECORD *tail[MAXDOMAIN]={NULL};
int file[MAXDOMAIN]={0};

int init()
{
	FILE *fp;
	RECORD *r;
	int i;

	if(DEBUG) printf("(init)RECORD size:%d\n", sizeof(RECORD));

	domainTotal = 1;

	for (i=0; i
	{
		if(DEBUG) printf("(init)domain:%d\n", i);

		sprintf(domainName[i], "domain_%d.dat", i);
		totalRecord[i] = 200;
		head[i] = NULL;

		if(DEBUG) printf("(init)file:%s\n", domainName[i]);
		if(DEBUG) printf("(init)totalRecord:%d\n", totalRecord[i]);

		//file init
		fp = fopen(domainName[i], "r");

		if (fp == NULL)
		{// file not exist
			if(DEBUG) printf("(init)file dose not exist, creat ... \n");

			r = (RECORD *) calloc(totalRecord[i], sizeof(RECORD));

			fp = fopen(domainName[i], "w+");
			fwrite(r, sizeof(RECORD), totalRecord[i], fp);
			fclose(fp);
			free(r);

		}
		else
		{
			fclose(fp);
		}

		file[i] = open(domainName[i], O_RDWR);
		head[i] = (RECORD *)mmap(0, totalRecord[i] * sizeof(RECORD),
             PROT_READ | PROT_WRITE, MAP_SHARED, file[i], 0);
        close(file[i]);

        tail[i] =  head[i] + totalRecord[i] - 1;

        if(DEBUG) printf("(init)mmap at addr: %x end at: %x\n", (int)head[i], (int)tail[i]);

	}

	return 0;
}

int clear()
{
	int i;
	for (i=0; i
	{
		if (-1 == msync((void *)head[i],  totalRecord[i] * sizeof(RECORD), MS_SYNC))
		{
			fprintf(stderr, "(clear)msync return error with errorno: %d (%s)\n", errno, strerror(errno));
		}
    	if (-1 == munmap((void *)head[i], totalRecord[i] * sizeof(RECORD)))
    	{
    		fprintf(stderr, "(clear)munmap return error with errorno: %d (%s)\n", errno, strerror(errno));
    	}
    	//close(file[i]);
	}
	if(DEBUG) printf("(clear) done ... \n");
	return 0;
}

int getRecord(RECORD* rp)
{
	srand((int) time(NULL));
	rp->key   = random()%1000;
	rp->value = random()%1000;
	return 0;
}

RECORD* findPosition(const int domain, const int value)
{
	RECORD *rp;

	rp = head[domain];

	while(rp->value > value)
	{
		rp += 1;
	}

	return rp;

}

RECORD* findRecord(const int domain, RECORD* const tpos, const int key)
{
	RECORD *rp;

	rp = tpos;
	while (rp < tail[domain])
	{
		if (rp->key == key)
		{
			return rp;
		}
		else
		{
			rp += 1;
		}
	}
	return rp;
}

int insertRecord(int domain, RECORD* rp)
{
	RECORD *fpos, *tpos;
	RECORD *endpos;
	void *to, *from;
	int size;

	endpos = tail[domain];

	if (rp->value < endpos->value)
	{
		if(DEBUG) printf("(insertRecord)value too small: %ld < %ld \n", rp->value, endpos->value);
		return 0;
	}

	// target position for this RECORD
	tpos = findPosition(domain, rp->value);
	// origin position of this key, be totalRecord[i]-1 if not exist
	// search from tpos
	fpos = findRecord(domain, tpos, rp->key);

	if (fpos == tpos)
	{
		if(DEBUG) printf("(insertRecord)fpos equal to tpos: %x \n", (int)fpos);
		tpos->key   = rp->key;
		tpos->value = rp->value;
		return 0;
	}

	if(DEBUG) printf("(insertRecord)target tpos: %x origin fpos: %x (endpos:%x)\n", (int)tpos, (int)fpos, (int)endpos );

	to   = (void *)(tpos+1);
	from = (void *)tpos;
	size = (int)((fpos - tpos)*sizeof(RECORD));

	memmove(to, from, size);

	if(DEBUG) printf("(insertRecord)memmove: from %x to: %x bytes:%d\n", (int)from, (int)to, size);

	tpos->key = rp->key;
	tpos->value = rp->value;

	return 0;

}

int showdomain(int domain)
{
	int j, ret;
	RECORD *rp;

	ret = msync((void *)head[domain], totalRecord[domain] * sizeof(RECORD), MS_SYNC);

	printf("(showdomain)domain:%d\n", domain);
	printf("(showdomain)msync return:%d\n", ret);

	rp = head[domain];

	for(j=0; j
	{
		printf("(showdomain)pos:%d %x key:%ld value:%ld\n", j, (int)rp, rp->key, rp->value);
		rp += 1;
	}

	return 0;

}

int main()
{
	RECORD rec;

	init();
	showdomain(0);

	while(getchar() != EOF )
	{
		getRecord(&rec);

		if(DEBUG) printf("\n(main)get RECORD: %ld %ld\n", rec.key, rec.value);

		insertRecord(0, &rec);
		showdomain(0);

	}

	clear();

    return 0;
}

推荐(0)
收藏

TCP 相关参数解释

2008-05-07 11:13 am

For Linux 2.6

原文:http://www.cnblogs.com/OnlyXP/archive/2007/09/29/911269.html

tcp_syn_retries :INTEGER
默认值是5
对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的连接,是由tcp_retries1 决定的)

tcp_synack_retries :INTEGER
默认值是5
对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。(可以根据上面的tcp_syn_retries来决定这个值)

tcp_keepalive_time :INTEGER
默认值是7200(2小时)
当keepalive打开的情况下,TCP发送keepalive消息的频率。(由于目前网络攻击等因素,造成了利用这个进行的攻击很频繁,曾经也有cu的朋友提到过,说如果2边建立了连接,然后不发送任何数据或者rst/fin消息,那么持续的时间是不是就是2小时,空连接攻击?tcp_keepalive_time就是预防此情形的.我个人在做nat服务的时候的修改值为1800秒)

tcp_keepalive_probes:INTEGER
默认值是9
TCP发送keepalive探测以确定该连接已经断开的次数。(注意:保持连接仅在SO_KEEPALIVE套接字选项被打开是才发送.次数默认不需要修改,当然根据情形也可以适当地缩短此值.设置为5比较合适)

tcp_keepalive_intvl:INTEGER
默认值为75
探测消息发送的频率,乘以tcp_keepalive_probes就得到对于从开始探测以来没有响应的连接杀除的时间。默认值为75秒,也就是没有活动的连接将在大约11分钟以后将被丢弃。(对于普通应用来说,这个值有一些偏大,可以根据需要改小.特别是web类服务器需要改小该值,15是个比较合适的值)

tcp_retries1 :INTEGER
默认值是3
放弃回应一个TCP连接请求前﹐需要进行多少次重试。RFC 规定最低的数值是3﹐这也是默认值﹐根据RTO的值大约在3秒 - 8分钟之间。(注意:这个值同时还决定进入的syn连接)

tcp_retries2 :INTEGER
默认值为15
在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试。默认值为15,根据RTO的值来决定,相当于13-30分钟(RFC1122规定,必须大于100秒).(这个值根据目前的网络设置,可以适当地改小,我的网络内修改为了5)

tcp_orphan_retries :INTEGER
默认值是7
在近端丢弃TCP连接之前﹐要进行多少次重试。默认值是7个﹐相当于 50秒 - 16分钟﹐视 RTO 而定。如果您的系统是负载很大的web服务器﹐那么也许需要降低该值﹐这类 sockets 可能会耗费大量的资源。另外参的考 tcp_max_orphans 。(事实上做NAT的时候,降低该值也是好处显著的,我本人的网络环境中降低该值为3)

tcp_fin_timeout :INTEGER
默认值是 60
对于本端断开的socket连接,TCP保持在FIN-WAIT-2状态的时间。对方可能会断开连接或一直不结束连接或不可预料的进程死亡。默认值为 60 秒。过去在2.2版本的内核中是 180 秒。您可以设置该值﹐但需要注意﹐如果您的机器为负载很重的web服务器﹐您可能要冒内存被大量无效数据报填满的风险﹐FIN-WAIT-2 sockets 的危险性低于 FIN-WAIT-1 ﹐因为它们最多只吃 1.5K 的内存﹐但是它们存在时间更长。另外参考 tcp_max_orphans。(事实上做NAT的时候,降低该值也是好处显著的,我本人的网络环境中降低该值为30)

tcp_max_tw_buckets :INTEGER
默认值是180000
系统在同时所处理的最大 timewait sockets 数目。如果超过此数的话﹐time-wait socket 会被立即砍除并且显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要人为的降低这个限制﹐不过﹐如果网络条件需要比默认值更多﹐则可以提高它(或许还要增加内存)。(事实上做NAT的时候最好可以适当地增加该值)

tcp_tw_recycle :BOOLEAN
默认值是0
打开快速 TIME-WAIT sockets 回收。除非得到技术专家的建议或要求﹐请不要随意修改这个值。(做NAT的时候,建议打开它)

tcp_tw_reuse:BOOLEAN
默认值是0
该文件表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接(这个对快速重启动某些服务,而启动后提示端口已经被使用的情形非常有帮助)

tcp_max_orphans :INTEGER
缺省值是8192
系统所能处理不属于任何进程的TCP sockets最大数量。假如超过这个数量﹐那么不属于任何进程的连接会被立即reset,并同时显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要依赖这个或是人为的降低这个限制(这个值Redhat AS版本中设置为32768,但是很多防火墙修改的时候,建议该值修改为2000)

tcp_abort_on_overflow :BOOLEAN
缺省值是0
当守护进程太忙而不能接受新的连接,就象对方发送reset消息,默认值是false。这意味着当溢出的原因是因为一个偶然的猝发,那么连接将恢复状态。只有在你确信守护进程真的不能完成连接请求时才打开该选项,该选项会影响客户的使用。(对待已经满载的sendmail,apache这类服务的时候,这个可以很快让客户端终止连接,可以给予服务程序处理已有连接的缓冲机会,所以很多防火墙上推荐打开它)

tcp_syncookies :BOOLEAN
默认值是0
只有在内核编译时选择了CONFIG_SYNCOOKIES时才会发生作用。当出现syn等候队列出现溢出时象对方发送syncookies。目的是为了防止syn flood攻击。
注意:该选项千万不能用于那些没有收到攻击的高负载服务器,如果在日志中出现synflood消息,但是调查发现没有收到synflood攻击,而是合法用户的连接负载过高的原因,你应该调整其它参数来提高服务器性能。参考:
tcp_max_syn_backlog
tcp_synack_retries
tcp_abort_on_overflow
syncookie严重的违背TCP协议,不允许使用TCP扩展,可能对某些服务导致严重的性能影响(如SMTP转发)。(注意,该实现与BSD上面使用的tcp proxy一样,是违反了RFC中关于tcp连接的三次握手实现的,但是对于防御syn-flood的确很有用.)

tcp_stdurg :BOOLEAN
默认值为0
使用 TCP urg pointer 字段中的主机请求解释功能。大部份的主机都使用老旧的 BSD解释,因此如果您在 Linux 打开它﹐或会导致不能和它们正确沟通。

tcp_max_syn_backlog :INTEGER
对于那些依然还未获得客户端确认的连接请求﹐需要保存在队列中最大数目。对于超过 128Mb 内存的系统﹐默认值是 1024 ﹐低于 128Mb 的则为 128。如果服务器经常出现过载﹐可以尝试增加这个数字。警告﹗假如您将此值设为大于 1024﹐最好修改 include/net/tcp.h 里面的 TCP_SYNQ_HSIZE ﹐以保持 TCP_SYNQ_HSIZE*16<=tcp_max_syn_backlog ﹐并且编进核心之内。(SYN Flood攻击利用TCP协议散布握手的缺陷,伪造虚假源IP地址发送大量TCP-SYN半打开连接到目标系统,最终导致目标系统Socket队列资源耗尽而无法接受新的连接。为了应付这种攻击,现代Unix系统中普遍采用多连接队列处理的方式来缓冲(而不是解决)这种攻击,是用一个基本队列处理正常的完全连接应用(Connect()和Accept() ),是用另一个队列单独存放半打开连接。这种双队列处理方式和其他一些系统内核措施(例如Syn-Cookies/Caches)联合应用时,能够比较有效的缓解小规模的SYN Flood攻击(事实证明<1000p/s)加大SYN队列长度可以容纳更多等待连接的网络连接数,所以对Server来说可以考虑增大该值.)

tcp_window_scaling :INTEGER
缺省值为1
该文件表示设置tcp/ip会话的滑动窗口大小是否可变。参数值为布尔值,为1时表示可变,为0时表示不可变。tcp/ip通常使用的窗口最大可达到 65535 字节,对于高速网络,该值可能太小,这时候如果启用了该功能,可以使tcp/ip滑动窗口大小增大数个数量级,从而提高数据传输的能力(RFC 1323)。(对普通地百M网络而言,关闭会降低开销,所以如果不是高速网络,可以考虑设置为0)

tcp_timestamps :BOOLEAN
缺省值为1
Timestamps 用在其它一些东西中﹐可以防范那些伪造的 sequence 号码。一条1G的宽带线路或许会重遇到带 out-of-line数值的旧sequence 号码(假如它是由于上次产生的)。Timestamp 会让它知道这是个 '旧封包'。(该文件表示是否启用以一种比超时重发更精确的方法(RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。)

tcp_sack :BOOLEAN
缺省值为1
使用 Selective ACK﹐它可以用来查找特定的遗失的数据报--- 因此有助于快速恢复状态。该文件表示是否启用有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段)。(对于广域网通信来说这个选项应该启用,但是这会增加对 CPU 的占用。)

tcp_fack :BOOLEAN
缺省值为1
打开FACK拥塞避免和快速重传功能。(注意,当tcp_sack设置为0的时候,这个值即使设置为1也无效)

tcp_dsack :BOOLEAN
缺省值为1
允许TCP发送"两个完全相同"的SACK。

tcp_ecn :BOOLEAN
缺省值为0
打开TCP的直接拥塞通告功能。

tcp_reordering :INTEGER
默认值是3
TCP流中重排序的数据报最大数量 。 (一般有看到推荐把这个数值略微调整大一些,比如5)

tcp_retrans_collapse :BOOLEAN
缺省值为1
对于某些有bug的打印机提供针对其bug的兼容性。(一般不需要这个支持,可以关闭它)

tcp_wmem(3个INTEGER变量): min, default, max
min:为TCP socket预留用于发送缓冲的内存最小值。每个tcp socket都可以在建议以后都可以使用它。默认值为4096(4K)。

default:为TCP socket预留用于发送缓冲的内存数量,默认情况下该值会影响其它协议使用的net.core.wmem_default 值,一般要低于net.core.wmem_default的值。默认值为16384(16K)。

max: 用于TCP socket发送缓冲的内存最大值。该值不会影响net.core.wmem_max,"静态"选择参数SO_SNDBUF则不受该值影响。默认值为131072(128K)。(对于服务器而言,增加这个参数的值对于发送数据很有帮助,在我的网络环境中,修改为了51200 131072 204800)

tcp_rmem (3个INTEGER变量): min, default, max
min:为TCP socket预留用于接收缓冲的内存数量,即使在内存出现紧张情况下tcp socket都至少会有这么多数量的内存用于接收缓冲,默认值为8K。

default:为TCP socket预留用于接收缓冲的内存数量,默认情况下该值影响其它协议使用的 net.core.wmem_default 值。该值决定了在tcp_adv_win_scale、tcp_app_win和tcp_app_win=0默认值情况下,TCP窗口大小为65535。默认值为87380

max:用于TCP socket接收缓冲的内存最大值。该值不会影响 net.core.wmem_max,"静态"选择参数 SO_SNDBUF则不受该值影响。默认值为 128K。默认值为87380*2 bytes。(可以看出,.max的设置最好是default的两倍,对于NAT来说主要该增加它,我的网络里为 51200 131072 204800)

tcp_mem(3个INTEGER变量):low, pressure, high
low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。(理想情况下,这个值应与指定给 tcp_wmem 的第 2 个值相匹配 - 这第 2 个值表明,最大页面大小乘以最大并发请求数除以页大小 (131072 * 300 / 4096)。 )

pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。(理想情况下这个值应该是 TCP 可以使用的总缓冲区大小的最大值 (204800 * 300 / 4096)。 )

high:允许所有tcp sockets用于排队缓冲数据报的页面量。(如果超过这个值,TCP 连接将被拒绝,这就是为什么不要令其过于保守 (512000 * 300 / 4096) 的原因了。在这种情况下,提供的价值很大,它能处理很多连接,是所预期的 2.5 倍;或者使现有连接能够传输 2.5 倍的数据。 我的网络里为192000 300000 732000)

一般情况下这些值是在系统启动时根据系统内存数量计算得到的。

tcp_app_win : INTEGER
默认值是31
保留max(window/2^tcp_app_win, mss)数量的窗口由于应用缓冲。当为0时表示不需要缓冲。

tcp_adv_win_scale : INTEGER
默认值为2
计算缓冲开销bytes/2^tcp_adv_win_scale(如果tcp_adv_win_scale > 0)或者bytes-bytes/2^(-tcp_adv_win_scale)(如果tcp_adv_win_scale <= 0)。

tcp_rfc1337 :BOOLEAN
缺省值为0
这个开关可以启动对于在RFC1337中描述的"tcp 的time-wait暗杀危机"问题的修复。启用后,内核将丢弃那些发往time-wait状态TCP套接字的RST 包.

tcp_low_latency : BOOLEAN
缺省值为0
允许 TCP/IP 栈适应在高吞吐量情况下低延时的情况;这个选项一般情形是的禁用。(但在构建Beowulf 集群的时候,打开它很有帮助)

tcp_westwood :BOOLEAN
缺省值为0
启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项。

tcp_bic :BOOLEAN
缺省值为0
为快速长距离网络启用 Binary Increase Congestion;这样可以更好地利用以 GB 速度进行操作的链接;对于 WAN 通信应该启用这个选项。

推荐(0)
收藏

Linux iptables Man 翻译

2008-05-06 2:08 pm

转自:tpxcer的空间 http://hi.baidu.com/tpxc

ps. 顺便做抓虾验证: {ZHUAXIA3c51521f96f54f3fcb53c6a7b30f7947Union}

用iptables -ADC 来指定链的规则,-A添加 -D删除 -C 修改

iptables - [RI] chain rule num rule-specification[option]

用iptables - RI 通过规则的顺序指定

iptables -D chain rule num[option]

删除指定规则

iptables -[LFZ] [chain][option]

用iptables -LFZ 链名 [选项]

iptables -[NX] chain

用 -NX 指定链

iptables -P chain target[options]

指定链的默认目标

iptables -E old-chain-name new-chain-name

-E 旧的链名 新的链名

用新的链名取代旧的链名

说明

Iptalbes 是用来设置、维护和检查Linux内核的IP包过滤规则的。

可以定义不同的表,每个表都包含几个内部的链,也能包含用户定义的链。每个链都是一个规则列表,对对应的包进行匹配:每条规则指定应当如何处理与之相匹配的包。这被称作'target'(目标),也可以跳向同一个表内的用户定义的链。

TARGETS

防火墙的规则指定所检查包的特征,和目标。如果包不匹配,将送往该链中下一条规则检查;如果匹配,那么下一条规则由目标值确定.该目标值可以是用户定义的 链名,或是某个专用值,如ACCEPT[通过], DROP[删除], QUEUE[排队], 或者 RETURN[返回]。

ACCEPT 表示让这个包通过。DROP表示将这个包丢弃。QUEUE表示把这个包传递到用户空间。RETURN表示停止这条链的匹配,到前一个链的规则重新开始。如 果到达了一个内建的链(的末端),或者遇到内建链的规则是RETURN,包的命运将由链准则指定的目标决定。

TABLES

当前有三个表(哪个表是当前表取决于内核配置选项和当前模块)。

-t table

这个选项指定命令要操作的匹配包的表。如果内核被配置为自动加载模块,这时若模块没有加载,(系统)将尝试(为该表)加载适合的模块。这些表如下: filter,这是默认的表,包含了内建的链INPUT(处理进入的包)、FORWORD(处理通过的包)和OUTPUT(处理本地生成的包)。nat, 这个表被查询时表示遇到了产生新的连接的包,由三个内建的链构成:PREROUTING (修改到来的包)、OUTPUT(修改路由之前本地的包)、POSTROUTING(修改准备出去的包)。mangle 这个表用来对指定的包进行修改。它有两个内建规则:PREROUTING(修改路由之前进入的包)和OUTPUT(修改路由之前本地的包)。

OPTIONS

这些可被iptables识别的选项可以区分不同的种类。

COMMANDS

这些选项指定执行明确的动作:若指令行下没有其他规定,该行只能指定一个选项.对于长格式的命令和选项名,所用字母长度只要保证iptables能从其他选项中区分出该指令就行了。

-A -append

在所选择的链末添加一条或更多规则。当源(地址)或者/与 目的(地址)转换为多个地址时,这条规则会加到所有可能的地址(组合)后面。

-D -delete

从所选链中删除一条或更多规则。这条命令可以有两种方法:可以把被删除规则指定为链中的序号(第一条序号为1),或者指定为要匹配的规则。

-R -replace

从选中的链中取代一条规则。如果源(地址)或者/与 目的(地址)被转换为多地址,该命令会失败。规则序号从1开始。

-I -insert

根据给出的规则序号向所选链中插入一条或更多规则。所以,如果规则序号为1,规则会被插入链的头部。这也是不指定规则序号时的默认方式。

-L -list

显示所选链的所有规则。如果没有选择链,所有链将被显示。也可以和z选项一起使用,这时链会被自动列出和归零。精确输出受其它所给参数影响。

-F -flush

清空所选链。这等于把所有规则一个个的删除。

--Z -zero

把所有链的包及字节的计数器清空。它可以和 -L配合使用,在清空前察看计数器,请参见前文。

-N -new-chain

根据给出的名称建立一个新的用户定义链。这必须保证没有同名的链存在。

-X -delete-chain

删除指定的用户自定义链。这个链必须没有被引用,如果被引用,在删除之前你必须删除或者替换与之有关的规则。如果没有给出参数,这条命令将试着删除每个非内建的链。

-P -policy

设置链的目标规则。

-E -rename-chain

根据用户给出的名字对指定链进行重命名,这仅仅是修饰,对整个表的结构没有影响。TARGETS参数给出一个合法的目标。只有非用户自定义链可以使用规则,而且内建链和用户自定义链都不能是规则的目标。

-h Help.

帮助。给出当前命令语法非常简短的说明。

PARAMETERS

参数

以下参数构成规则详述,如用于add、delete、replace、append 和 check命令。

-p -protocal [!]protocol

规则或者包检查(待检查包)的协议。指定协议可以是tcp、udp、icmp中的一个或者全部,也可以是数值,代表这些协议中的某一个。当然也可以使用在 /etc/protocols中定义的协议名。在协议名前加上"!"表示相反的规则。数字0相当于所有all。Protocol all会匹配所有协议,而且这是缺省时的选项。在和check命令结合时,all可以不被使用。

-s -source [!] address[/mask]

指定源地址,可以是主机名、网络名和清楚的IP地址。mask说明可以是网络掩码或清楚的数字,在网络掩码的左边指定网络掩码左边"1"的个数,因此, mask值为24等于255.255.255.0。在指定地址前加上"!"说明指定了相反的地址段。标志 --src 是这个选项的简写。

-d --destination [!] address[/mask]

指定目标地址,要获取详细说明请参见 -s标志的说明。标志 --dst 是这个选项的简写。

-j --jump target

-j 目标跳转

指定规则的目标;也就是说,如果包匹配应当做什么。目标可以是用户自定义链(不是这条规则所在的),某个会立即决定包的命运的专用内建目标,或者一个扩展 (参见下面的EXTENSIONS)。如果规则的这个选项被忽略,那么匹配的过程不会对包产生影响,不过规则的计数器会增加。

-i -in-interface [!] [name]

i -进入的(网络)接口 [!][名称]

这是包经由该接口接收的可选的入口名称,包通过该接口接收(在链INPUT、 FORWORD和PREROUTING中进入的包)。当在接口名前使用"!" 说明后,指的是相反的名称。如果接口名后面加上"+",则所有以此接口名开头的接口都会被匹配。如果这个选项被忽略,会假设为"+",那么将匹配任意接 口。

-o --out-interface [!][name]

-o --输出接口[名称]

这是包经由该接口送出的可选的出口名称,包通过该口输出(在链FORWARD、 OUTPUT和POSTROUTING中送出的包)。当在接口名前使用"! "说明后,指的是相反的名称。如果接口名后面加上"+",则所有以此接口名开头的接口都会被匹配。如果这个选项被忽略,会假设为"+",那么将匹配所有任 意接口。

[!] -f, --fragment

[!] -f --分片

这意味着在分片的包中,规则只询问第二及以后的片。自那以后由于无法判断这种把包的源端口或目标端口(或者是ICMP类型的),这类包将不能匹配任何指定对他们进行匹配的规则。如果"!"说明用在了"-f"标志之前,表示相反的意思。

OTHER OPTIONS

其他选项

还可以指定下列附加选项:

-v --verbose

-v --详细

详细输出。这个选项让list命令显示接口地址、规则选项(如果有)和TOS (Type of Service)掩码。包和字节计数器也将被显示,分别用K、M、G(前缀)表示1000、1,000,000和1,000,000,000倍(不过请参 看-x标志改变它),对于添加,插入,删除和替换命令,这会使一个或多个规则的相关详细信息被打印。

-n --numeric

-n --数字

数字输出。IP地址和端口会以数字的形式打印。默认情况下,程序试显示主机名、网络名或者服务(只要可用)。

-x -exact

-x -精确

扩展数字。显示包和字节计数器的精确值,代替用K,M,G表示的约数。这个选项仅能用于 -L 命令。

--line-numbers

当列表显示规则时,在每个规则的前面加上行号,与该规则在链中的位置相对应。

MATCH EXTENSIONS

对应的扩展

iptables能够使用一些与模块匹配的扩展包。以下就是含于基本包内的扩展包,而且他们大多数都可以通过在前面加上!来表示相反的意思。

tcp

当 --protocol tcp 被指定,且其他匹配的扩展未被指定时,这些扩展被装载。它提供以下选项:

--source-port [!] [port[]]

源端口或端口范围指定。这可以是服务名或端口号。使用格式端口:端口也可以指定包含的(端口)范围。如果首端口号被忽略,默认是"0",如果末端口号被忽略,默认是"65535",如果第二个端口号大于第一个,那么它们会被交换。这个选项可以使用 --sport的别名。

--destionation-port [!] [port:[port]]

目标端口或端口范围指定。这个选项可以使用 --dport别名来代替。

--tcp-flags [!] mask comp

匹配指定的TCP标记。第一个参数是我们要检查的标记,一个用逗号分开的列表,第二个参数是用逗号分开的标记表,是必须被设置的。标记如下:SYN ACK FIN RST URG PSH ALL NONE。因此这条命令:iptables -A FORWARD -p tcp --tcp-flags SYN, ACK, FIN, RST SYN只匹配那些SYN标记被设置而ACK、FIN和RST标记没有设置的包。

[!] --syn

只匹配那些设置了SYN位而清除了ACK和FIN位的TCP包。这些包用于TCP连接初始化时发出请求;例如,大量的这种包进入一个接口发生堵塞时会阻止 进入的TCP连接,而出去的TCP连接不会受到影响。这等于 --tcp-flags SYN, RST, ACK SYN。如果"--syn"前面有"!"标记,表示相反的意思。

--tcp-option [!] number

匹配设置了TCP选项的。

udp

当protocol udp 被指定,且其他匹配的扩展未被指定时,这些扩展被装载,它提供以下选项:

--source-port [!] [port:[port]]

源端口或端口范围指定。详见 TCP扩展的--source-port选项说明。

--destination-port [!] [port:[port]]

目标端口或端口范围指定。详见 TCP扩展的--destination-port选项说明。

icmp

当protocol icmp被指定,且其他匹配的扩展未被指定时,该扩展被装载。它提供以下选项:

--icmp-type [!] typename

这个选项允许指定ICMP类型,可以是一个数值型的ICMP类型,或者是某个由命令iptables -p icmp -h所显示的ICMP类型名。

mac

--mac-source [!] address

匹配物理地址。必须是XX:XX:XX:XX:XX这样的格式。注意它只对来自以太设备并进入PREROUTING、FORWORD和INPUT链的包有效。

limit

这个模块匹配标志用一个标记桶过滤器一一定速度进行匹配,它和LOG目标结合使用来给出有限的登陆数.当达到这个极限值时,使用这个扩展包的规则将进行匹配.(除非使用了"!"标记)

--limit rate

最大平均匹配速率:可赋的值有'/second', '/minute', '/hour', or '/day'这样的单位,默认是3/hour。

--limit-burst number

待匹配包初始个数的最大值:若前面指定的极限还没达到这个数值,则概数字加1.默认值为5

multiport

这个模块匹配一组源端口或目标端口,最多可以指定15个端口。只能和-p tcp 或者 -p udp 连着使用。

--source-port [port[, port]]

如果源端口是其中一个给定端口则匹配

--destination-port [port[, port]]

如果目标端口是其中一个给定端口则匹配

--port [port[, port]]

若源端口和目的端口相等并与某个给定端口相等,则匹配。

mark

这个模块和与netfilter过滤器标记字段匹配(就可以在下面设置为使用MARK标记)。

--mark value [/mask]

匹配那些无符号标记值的包(如果指定mask,在比较之前会给掩码加上逻辑的标记)。

owner

此模块试为本地生成包匹配包创建者的不同特征。只能用于OUTPUT链,而且即使这样一些包(如ICMP ping应答)还可能没有所有者,因此永远不会匹配。

--uid-owner userid

如果给出有效的user id,那么匹配它的进程产生的包。

--gid-owner groupid

如果给出有效的group id,那么匹配它的进程产生的包。

--sid-owner seessionid

根据给出的会话组匹配该进程产生的包。

state

此模块,当与连接跟踪结合使用时,允许访问包的连接跟踪状态。

--state state

这里state是一个逗号分割的匹配连接状态列表。可能的状态是:INVALID表示包是未知连接,ESTABLISHED表示是双向传送的连接,NEW 表示包为新的连接,否则是非双向传送的,而RELATED表示包由新连接开始,但是和一个已存在的连接在一起,如FTP数据传送,或者一个ICMP错误。

unclean

此模块没有可选项,不过它试着匹配那些奇怪的、不常见的包。处在实验中。

tos

此模块匹配IP包首部的8位tos(服务类型)字段(也就是说,包含在优先位中)。

--tos tos

这个参数可以是一个标准名称,(用iptables -m tos -h 察看该列表),或者数值。

TARGET EXTENSIONS

iptables可以使用扩展目标模块:以下都包含在标准版中。

LOG

为匹配的包开启内核记录。当在规则中设置了这一选项后,linux内核会通过printk()打印一些关于全部匹配包的信息(诸如IP包头字段等)。

--log-level level

记录级别(数字或参看 syslog.conf(5))。

--log-prefix prefix

在纪录信息前加上特定的前缀:最多14个字母长,用来和记录中其他信息区别。

--log-tcp-sequence

记录TCP序列号。如果记录能被用户读取那么这将存在安全隐患。

--log-tcp-options

记录来自TCP包头部的选项。

--log-ip-options

记录来自IP包头部的选项。

MARK

用来设置包的netfilter标记值。只适用于mangle表。

--set-mark mark

REJECT

作为对匹配的包的响应,返回一个错误的包:其他情况下和DROP相同。

此目标只适用于INPUT、FORWARD和OUTPUT链,和调用这些链的用户自定义链。这几个选项控制返回的错误包的特性:

--reject-with type

Type可以是icmp-net-unreachable、icmp-host- unreachable、icmp-port-nreachable、 icmp-proto-unreachable、 icmp-net-prohibited 或者 icmp-host-prohibited,该类型会返回相应的ICMP错误信息(默认是port-unreachable)。选项 echo-reply也是允许的;它只能用于指定ICMP ping包的规则中,生成ping的回应。最后,选项tcp-reset可以用于在INPUT链中,或自INPUT链调用的规则,只匹配TCP协议:将回 应一个TCP RST包。

TOS

用来设置IP包的首部八位tos。只能用于mangle表。

--set-tos tos

你可以使用一个数值型的TOS 值,或者用iptables -j TOS -h 来查看有效TOS名列表。

MIRROR

这是一个试验示范目标,可用于转换IP首部字段中的源地址和目标地址,再传送该包,并只适用于INPUT、FORWARD和OUTPUT链,以及只调用它们的用户自定义链。

SNAT

这个目标只适用于nat表的POSTROUTING链。它规定修改包的源地址(此连接以后所有的包都会被影响),停止对规则的检查,它包含选项:

--to-source ;[-;][:port-port]

可以指定一个单一的新的IP地址,一个IP地址范围,也可以附加一个端口范围(只能在指定-p tcp 或者-p udp的规则里)。如果未指定端口范围,源端口中512以下的(端口)会被安置为其他的512以下的端口;512到1024之间的端口会被安置为1024 以下的,其他端口会被安置为1024或以上。如果可能,端口不会被修改。

--to-destiontion ;[-;][:port-port]

可以指定一个单一的新的IP地址,一个IP地址范围,也可以附加一个端口范围(只能在指定-p tcp 或者-p udp的规则里)。如果未指定端口范围,目标端口不会被修改。

MASQUERADE

只用于nat表的POSTROUTING链。只能用于动态获取IP(拨号)连接:如果你拥有静态IP地址,你要用SNAT。伪装相当于给包发出时所经过接 口的IP地址设置一个映像,当接口关闭连接会终止。这是因为当下一次拨号时未必是相同的接口地址(以后所有建立的连接都将关闭)。它有一个选项:

--to-ports ;[-port>;]

指定使用的源端口范围,覆盖默认的SNAT源地址选择(见上面)。这个选项只适用于指定了-p tcp或者-p udp的规则。

REDIRECT

只适用于nat表的PREROUTING和OUTPUT链,和只调用它们的用户自定义链。它修改包的目标IP地址来发送包到机器自身(本地生成的包被安置为地址127.0.0.1)。它包含一个选项:

--to-ports ;[;]

指定使用的目的端口或端口范围:不指定的话,目标端口不会被修改。只能用于指定了-p tcp 或 -p udp的规则。

DIAGNOSTICS

诊断

不同的错误信息会打印成标准错误:退出代码0表示正确。类似于不对的或者滥用的命令行参数错误会返回错误代码2,其他错误返回代码为1。

BUGS

臭虫

Check is not implemented (yet).

检查还未完成。

COMPATIBILITY WITH IPCHAINS

与ipchains的兼容性

iptables和Rusty Russell的ipchains非常相似。主要区别是INPUT 链只用于进入本地主机的包,而OUTPUT只用于自本地主机生成的包。因此每个包只经过三个链的一个;以前转发的包会经过所有三个链。其他主要区别是 -i 引用进入接口;-o引用输出接口,两者都适用于进入FORWARD链的包。当和可选扩展模块一起使用默认过滤器表时,iptables是一个纯粹的包过滤 器。这能大大减少以前对IP伪装和包过滤结合使用的混淆,所以以下选项作了不同的处理:

-j MASQ

-M -S

-M -L

在iptables中有几个不同的链。

SEE ALSO

参见

iptables-HOWTO有详细的iptables用法,对netfilter-hacking-HOWTO也有详细的本质说明。

推荐(0)
收藏
得到OpenID
使用OpenID提供商
35OpenID 35OpenID MyOpenID MyOpenID Flickr Flickr
Google Google Yahoo Yahoo! AOL AOL
Blogger Blogger LiveJournal LiveJournal Verisign Verisign
ClaimID ClaimID Technorati Technorati Vidoop Vidoop
OpenID OpenID 帮助
您还没有登录,请登录后继续操作。
提示:您必需打开Cookie才能使用本系统
请输入您的 OpenID OpenID 登录:
例如:http://yourname.openid.35.com
close