<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    一江春水向東流

    做一個有思想的人,期待與每一位熱愛思考的人交流,您的關(guān)注是對我最大的支持。

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      44 隨筆 :: 139 文章 :: 81 評論 :: 0 Trackbacks
    文摘出處:http://www.xfocus.net/

    創(chuàng)建時間:2003-11-21 更新時間:2003-11-23
    文章屬性:原創(chuàng)
    文章提交:firstdot (firstdot_at_163.com)


    作者:梁健(firstdot)
    E-MAIL:firstdot@163.com

    感謝王超、史曉龍的共同研究與大力幫助
    一.基本概念
    零 拷貝(zero-copy)基本思想是:數(shù)據(jù)報從網(wǎng)絡(luò)設(shè)備到用戶程序空間傳遞的過程中,減少數(shù)據(jù)拷貝次數(shù),減少系統(tǒng)調(diào)用,實現(xiàn)CPU的零參與,徹底消除 CPU在這方面的負(fù)載。實現(xiàn)零拷貝用到的最主要技術(shù)是DMA數(shù)據(jù)傳輸技術(shù)和內(nèi)存區(qū)域映射技術(shù)。如圖1所示,傳統(tǒng)的網(wǎng)絡(luò)數(shù)據(jù)報處理,需要經(jīng)過網(wǎng)絡(luò)設(shè)備到操作 系統(tǒng)內(nèi)存空間,系統(tǒng)內(nèi)存空間到用戶應(yīng)用程序空間這兩次拷貝,同時還需要經(jīng)歷用戶向系統(tǒng)發(fā)出的系統(tǒng)調(diào)用。而零拷貝技術(shù)則首先利用DMA技術(shù)將網(wǎng)絡(luò)數(shù)據(jù)報直接 傳遞到系統(tǒng)內(nèi)核預(yù)先分配的地址空間中,避免CPU的參與;同時,將系統(tǒng)內(nèi)核中存儲數(shù)據(jù)報的內(nèi)存區(qū)域映射到檢測程序的應(yīng)用程序空間(還有一種方式是在用戶空 間建立一緩存,并將其映射到內(nèi)核空間,類似于linux系統(tǒng)下的kiobuf技術(shù)),檢測程序直接對這塊內(nèi)存進(jìn)行訪問,從而減少了系統(tǒng)內(nèi)核向用戶空間的內(nèi) 存拷貝,同時減少了系統(tǒng)調(diào)用的開銷,實現(xiàn)了真正的“零拷貝”。


    圖1 傳統(tǒng)數(shù)據(jù)處理與零拷貝技術(shù)之比較
    二.實現(xiàn)
    在redhat7.3 上通過修改其內(nèi)核源碼中附帶的8139too.c完成零拷貝的試驗,主要想法是:在8139too網(wǎng)卡驅(qū)動模塊啟動時申請一內(nèi)核緩存,并建立一數(shù)據(jù)結(jié)構(gòu)對 其進(jìn)行管理,然后試驗性的向該緩存寫入多個字符串?dāng)?shù)據(jù),最后通過proc文件系統(tǒng)將該緩存的地址傳給用戶進(jìn)程;用戶進(jìn)程通過讀proc文件系統(tǒng)取得緩存地 址并對該緩存進(jìn)行地址映射,從而可以從其中讀取數(shù)據(jù)。哈哈,為了偷懶,本文只是對零拷貝思想中的地址映射部分進(jìn)行試驗,而沒有實現(xiàn)DMA數(shù)據(jù)傳輸(太麻煩 了,還得了解硬件),本試驗并不是一個IDS產(chǎn)品中抓包模塊的一部分,要想真正在IDS中實現(xiàn)零拷貝,除了DMA外,還有一些問題需考慮,詳見本文第三節(jié) 的分析。以下為實現(xiàn)零拷貝的主要步驟,詳細(xì)代碼見附錄。

    步驟一:修改網(wǎng)卡驅(qū)動程序
    a.在網(wǎng)卡驅(qū)動程序中申請一塊緩存:由于在linux2.4.X內(nèi)核中支持的最大可分配連續(xù)緩存大小為2M,所以如果需要存儲更大量的網(wǎng)絡(luò)數(shù)據(jù)報文,則需要分配多塊非連續(xù)的緩存,并使用鏈表、數(shù)組或hash表來對這些緩存進(jìn)行管理。

    #define PAGES_ORDER 9
    unsigned long su1_2
    su1_2 = __get_free_pages(GFP_KERNEL,PAGES_ORDER);

    b. 向緩存中寫入數(shù)據(jù):真正IDS產(chǎn)品中的零拷貝實現(xiàn)應(yīng)該是使用DMA數(shù)據(jù)傳輸把網(wǎng)卡硬件接收到的包直接寫入該緩存。作為試驗,我只是向該緩存中寫入幾個任意 的字符串,如果不考慮DMA而又想向緩存中寫入真正的網(wǎng)絡(luò)數(shù)據(jù)包,可以在8139too.c的rtl8139_rx_interrupt()中調(diào)用 netif_rx()后插入以下代碼:

    //put_pkt2mem_n++; //包個數(shù)
    //put_mem(skb->data,pkt_size);
    其中put_pkt2mem_n變量和put_mem函數(shù)見附錄。

    c. 把該緩存的物理地址傳到用戶空間:由于在內(nèi)核中申請的緩存地址為虛擬地址,而在用戶空間需要得到的是該緩存的物理地址,所以首先要進(jìn)行虛擬地址到物理地址 的轉(zhuǎn)換,在linux系統(tǒng)中可以使用內(nèi)核虛擬地址減3G來獲得對應(yīng)的物理地址。把緩存的地址傳到用戶空間需要在內(nèi)核與用戶空間進(jìn)行少量數(shù)據(jù)傳輸,這可以使 用字符驅(qū)動、proc文件系統(tǒng)等方式實現(xiàn),在這里采用了proc文件系統(tǒng)方式。

    int read_procaddr(char *buf,char **start,off_t offset,int count,int *eof,void *data)
    {
    ????sprintf(buf,"%u\n",__pa(su1_2));
    ????*eof = 1;
    ????return 9;
    }
    create_proc_read_entry("nf_addr",0,NULL,read_procaddr,NULL);

    步驟二:在用戶程序中實現(xiàn)對共享緩存的訪問
    a.讀取緩存地址:通過直接讀取proc文件的方式便可獲得。

    char addr[9];
    int fd_procaddr;
    unsigned long ADDR;
    fd_procaddr = open("/proc/nf_addr",O_RDONLY);
    read(fd_procaddr,addr,9);
    ADDR = atol(addr);

    b.把緩存映射到用戶進(jìn)程空間中:在用戶進(jìn)程中打開/dev/mem設(shè)備(相當(dāng)于物理內(nèi)存),使用mmap把網(wǎng)卡驅(qū)動程序申請的緩存映射到自己的進(jìn)程空間,然后就可以從中讀取所需要的網(wǎng)絡(luò)數(shù)據(jù)包了。

    char *su1_2;
    int fd;
    fd=open("/dev/mem",O_RDWR);????
    su1_2 = mmap(0,PAGES*4*1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, ADDR);

    三.分析
    ???? 零拷貝中存在的最關(guān)鍵問題是同步問題,一邊是處于內(nèi)核空間的網(wǎng)卡驅(qū)動向緩存中寫入網(wǎng)絡(luò)數(shù)據(jù)包,一邊是用戶進(jìn)程直接對緩存中的數(shù)據(jù)包進(jìn)行分析(注意,不是拷 貝后再分析),由于兩者處于不同的空間,這使得同步問題變得更加復(fù)雜。緩存被分成多個小塊,每一塊存儲一個網(wǎng)絡(luò)數(shù)據(jù)包并用一數(shù)據(jù)結(jié)構(gòu)表示,本試驗在包數(shù)據(jù) 結(jié)構(gòu)中使用標(biāo)志位來標(biāo)識什么時候可以進(jìn)行讀或?qū)懀?dāng)網(wǎng)卡驅(qū)動向包數(shù)據(jù)結(jié)構(gòu)中填入真實的包數(shù)據(jù)后便標(biāo)識該包為可讀,當(dāng)用戶進(jìn)程對包數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)分析完后 便標(biāo)識該包為可寫,這基本解決了同步問題。然而,由于IDS的分析進(jìn)程需要直接對緩存中的數(shù)據(jù)進(jìn)行入侵分析,而不是將數(shù)據(jù)拷貝到用戶空間后再進(jìn)行分析,這 使得讀操作要慢于寫操作,有可能造成網(wǎng)卡驅(qū)動無緩存空間可以寫,從而造成一定的丟包現(xiàn)象,解決這一問題的關(guān)鍵在于申請多大的緩存,太小的緩存容易造成丟 包,太大的緩存則管理麻煩并且對系統(tǒng)性能會有比較大的影響。

    四.附錄
    a.????8139too.c中加入的代碼

    /*add_by_liangjian for zero_copy*/
    #include <linux/wrapper.h>
    #include <asm/page.h>
    #include <linux/slab.h>
    #include <linux/proc_fs.h>
    #define PAGES_ORDER 9
    #define PAGES 512
    #define MEM_WIDTH????1500
    /*added*/

    /*add_by_liangjian for zero_copy*/
    struct MEM_DATA
    {
    ????//int key;
    ????unsigned short width;/*緩沖區(qū)寬度*/
    ????unsigned short length;/*緩沖區(qū)長度*/
    ????//unsigned short wtimes;/*寫進(jìn)程記數(shù),預(yù)留,為以后可以多個進(jìn)程寫*/
    ????//unsigned short rtimes;/*讀進(jìn)程記數(shù),預(yù)留,為以后可以多個進(jìn)程讀*/
    ????unsigned short wi;/*寫指針*/
    ????unsigned short ri;/*讀指針*/
    } * mem_data;
    struct MEM_PACKET
    {
    ????unsigned int len;
    ????unsigned char packetp[MEM_WIDTH - 4];/*sizeof(unsigned int) == 4*/
    };
    unsigned long su1_2;/*緩存地址*/
    /*added*/

    /*add_by_liangjian for zero_copy*/
    //刪除緩存
    void del_mem()
    {
    ????int pages = 0;
    ????char *addr;
    ????addr = (char *)su1_2;
    ????while (pages <=PAGES -1)
    ????{
    ????????mem_map_unreserve(virt_to_page(addr));
    ????????addr = addr + PAGE_SIZE;
    ????????pages++;
    ????}
    ????free_pages(su1_2,PAGES_ORDER);????
    }????
    void init_mem()
    /********************************************************
    *??????????????????初始化緩存
    *?????? 輸入:?? aMode:????緩沖區(qū)讀寫模式:??r,w????????*
    *?????? 返回:?? 00:???? 失敗????????????????????????*
    *?????????????? >0:???? 緩沖區(qū)地址??????????????????*
    ********************************************************/
    {
    ????int i;
    ????int pages = 0;
    ????char *addr;
    ????char *buf;
    ????struct MEM_PACKET * curr_pack;
    ????
    ????su1_2 = __get_free_pages(GFP_KERNEL,PAGES_ORDER);
    ????printk("[%x]\n",su1_2);
    ????addr = (char *)su1_2;
    ????while (pages <= PAGES -1)
    ????{
    ????????mem_map_reserve(virt_to_page(addr));//需使緩存的頁面常駐內(nèi)存
    ????????addr = addr + PAGE_SIZE;
    ????????pages++;
    ????}
    ????mem_data = (struct MEM_DATA *)su1_2;
    ????mem_data[0].ri = 1;
    ??????????mem_data[0].wi = 1;
    ??????????mem_data[0].length = PAGES*4*1024 / MEM_WIDTH;
    ??????????mem_data[0].width = MEM_WIDTH;
    ????/* initial su1_2 */
    ????for(i=1;i<=mem_data[0].length;i++)
    ????{
    ????????buf = (void *)((char *)su1_2 + MEM_WIDTH * i);
    ????????curr_pack = (struct MEM_PACKET *)buf;
    ????????curr_pack->len = 0;
    ????}????
    }
    int put_mem(char *aBuf,unsigned int pack_size)
    /****************************************************************
    *???????????????? 寫緩沖區(qū)子程序????????????????????????????????*
    *?????? 輸入?yún)?shù)????:?? aMem:?? 緩沖區(qū)地址??????????????????????*
    *?????????????????????? aBuf:?? 寫數(shù)據(jù)地址??????????????????????*
    *?????? 輸出參數(shù)????:?? <=00 :??錯誤????????????????????????????*
    *?????????????????????? XXXX :??數(shù)據(jù)項序號??????????????????????*
    *****************************************************************/
    {
    ????register int s,i,width,length,mem_i;
    ????char *buf;
    ????struct MEM_PACKET * curr_pack;

    ????s = 0;
    ????mem_data = (struct MEM_DATA *)su1_2;
    ????width??= mem_data[0].width;
    ????length = mem_data[0].length;
    ????mem_i??= mem_data[0].wi;
    ????buf = (void *)((char *)su1_2 + width * mem_i);

    ????for (i=1;i<length;i++){
    ????????curr_pack = (struct MEM_PACKET *)buf;
    ????????????if??(curr_pack->len == 0){
    ????????????????????memcpy(curr_pack->packetp,aBuf,pack_size);
    ????????????????????curr_pack->len = pack_size;;
    ????????????????s = mem_i;
    ????????????mem_i++;
    ????????????????????if??(mem_i >= length)
    ????????????????????????mem_i = 1;
    ????????????????mem_data[0].wi = mem_i;
    ????????????????break;
    ????????????}
    ????????????mem_i++;
    ????????????if??(mem_i >= length){
    ????????????????????mem_i = 1;
    ????????????????????buf = (void *)((char *)su1_2 + width);
    ????????????}
    ????????????else buf = (char *)su1_2 + width*mem_i;
    ????????}

    ????if(i >= length)
    ????????????s = 0;
    ????return s;
    }
    // proc文件讀函數(shù)
    int read_procaddr(char *buf,char **start,off_t offset,int count,int *eof,void *data)
    {
    ????sprintf(buf,"%u\n",__pa(su1_2));
    ????*eof = 1;
    ????return 9;
    }
    /*added*/

    在8139too.c的rtl8139_init_module()函數(shù)中加入以下代碼:
    /*add_by_liangjian for zero_copy*/
    ????put_pkt2mem_n = 0;
    ????init_mem();
    ????put_mem("data1dfadfaserty",16);
    ????put_mem("data2zcvbnm",11);
    ????put_mem("data39876543210poiuyt",21);
    ????create_proc_read_entry("nf_addr",0,NULL,read_procaddr,NULL);
    /*added */????

    在8139too.c的rtl8139_cleanup_module()函數(shù)中加入以下代碼:
    /*add_by_liangjian for zero_copy*/
    ????del_mem();
    ????remove_proc_entry("nf_addr",NULL);
    /*added*/????

    b.用戶空間讀取緩存代碼

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #define PAGES 512
    #define MEM_WIDTH 1500
    struct MEM_DATA
    {
    ????//int key;
    ????unsigned short width;/*緩沖區(qū)寬度*/
    ????unsigned short length;/*緩沖區(qū)長度*/
    ????//unsigned short wtimes;/*寫進(jìn)程記數(shù),預(yù)留,為以后可以多個進(jìn)程寫*/
    ????//unsigned short rtimes;/*讀進(jìn)程記數(shù),預(yù)留,為以后可以多個進(jìn)程讀*/
    ????unsigned short wi;/*寫指針*/
    ????unsigned short ri;/*讀指針*/
    } * mem_data;

    struct MEM_PACKET
    {
    ????unsigned int len;
    ????unsigned char packetp[MEM_WIDTH - 4];/*sizeof(unsigned int) == 4*/
    };

    int get_mem(char *aMem,char *aBuf,unsigned int *size)
    /****************************************************************
    *???????????????? 讀緩沖區(qū)子程序????????????????????????????????*
    *?????? 輸入?yún)?shù)????:?? aMem:?? 緩沖區(qū)地址??????????????????????*
    *?????????????????????? aBuf:?? 返回數(shù)據(jù)地址, 其數(shù)據(jù)區(qū)長度應(yīng)大于*
    *?????????????????????????????? 緩沖區(qū)寬度??????????????????????*
    *?????? 輸出參數(shù)????:?? <=00 :??錯誤????????????????????????????*
    *?????????????????????? XXXX :??數(shù)據(jù)項序號??????????????????????*
    *****************************************************************/
    {
    ????register int i,s,width,length,mem_i;
    ????char???? *buf;
    ????struct MEM_PACKET * curr_pack;

    ????s = 0;
    ????mem_data = (void *)aMem;
    ????width??= mem_data[0].width;
    ????length = mem_data[0].length;
    ????mem_i??= mem_data[0].ri;
    ????buf = (void *)(aMem + width * mem_i);

    ????curr_pack = (struct MEM_PACKET *)buf;
    ????if??(curr_pack->len != 0){/*第一個字節(jié)為0說明該部分為空*/
    ????????????memcpy(aBuf,curr_pack->packetp,curr_pack->len);
    ????????????*size = curr_pack->len;
    ????????????curr_pack->len = 0;
    ????????????s = mem_data[0].ri;
    ????????????mem_data[0].ri++;
    ????????????if(mem_data[0].ri >= length)
    ????????????????????mem_data[0].ri = 1;
    ????????????goto ret;
    ????????}
    ????
    ????for (i=1;i<length;i++){
    ????????????mem_i++;/*繼續(xù)向后找,最糟糕的情況是把整個緩沖區(qū)都找一遍*/
    ????????????if??(mem_i >= length)
    ????????????????mem_i = 1;
    ????????????buf = (void *)(aMem + width*mem_i);
    ????????????curr_pack = (struct MEM_PACKET *)buf;
    ????????????if??(curr_pack->len == 0)
    ????????????????????continue;
    ????????????memcpy(aBuf,curr_pack->packetp,curr_pack->len);
    ????????????*size = curr_pack->len;
    ????????????curr_pack->len = 0;
    ????????????s = mem_data[0].ri = mem_i;
    ????????????mem_data[0].ri++;
    ????????????if(mem_data[0].ri >= length)
    ????????????mem_data[0].ri = 1;
    ????????????break;
    ????????}

    ????ret:
    ????return s;
    }

    int main()
    {
    ????char *su1_2;
    ????char receive[1500];
    ????int i,j;
    ????int fd;
    ????int fd_procaddr;
    ????unsigned int size;
    ????char addr[9];
    ????unsigned long ADDR;
    ????
    ????j = 0;
    ????/*open device 'mem' as a media to access the RAM*/
    ????fd=open("/dev/mem",O_RDWR);????
    ????fd_procaddr = open("/proc/nf_addr",O_RDONLY);
    ????read(fd_procaddr,addr,9);
    ????ADDR = atol(addr);
    ????close(fd_procaddr);
    ????printf("%u[%8lx]\n",ADDR,ADDR);
    ????/*Map the address in kernel to user space, use mmap function*/
    ????su1_2 = mmap(0,PAGES*4*1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, ADDR);
    ????perror("mmap");
    ????while(1)
    ????{
    ????????bzero(receive,1500);
    ????????i = get_mem(su1_2,receive,&size);
    ????????if (i != 0)
    ????????{
    ????????????j++;
    ????????????printf("%d:%s[size = %d]\n",j,receive,size);
    ????????}????
    ????????else
    ????????{
    ????????????printf("there have no data\n");
    ????????????munmap(su1_2,PAGES*4*1024);
    ????????????close(fd);
    ????????????break;
    ????????}
    ????}
    ????while(1);
    }

    五.參考文獻(xiàn)
    1.CHRISTIAN KURMANN, FELIX RAUCH ,THOMAS M. STRICKER.
    Speculative Defragmentation - Leading Gigabit Ethernet to True Zero-Copy Communication
    2.ALESSANDRO RUBINI,JONATHAN CORBET.《LINUX DEVICE DRIVERS 2》,O’Reilly & Associates 2002.
    3.胡希明,毛德操.《LINUX 內(nèi)核源代碼情景分析》,浙江大學(xué)出版社 2001


    關(guān) 于作者:梁健,華北計算技術(shù)研究所在讀碩士研究生,研究方向:信息安全。論文開題為《基于系統(tǒng)調(diào)用分析的主機(jī)異常入侵檢測與防御》。對IDS有兩年多的研 究經(jīng)驗,熟悉linux內(nèi)核,熟悉linux c/c++編程、win32 API編程,對網(wǎng)絡(luò)和操作系統(tǒng)安全感興趣。
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    零拷貝技術(shù)分為兩步:
    1、硬件到內(nèi)核,實現(xiàn)的前提是網(wǎng)卡必須支持DMA,對于不支持DMA的網(wǎng)卡無法實現(xiàn)零拷貝。
    2、內(nèi)核到用戶層,將系統(tǒng)內(nèi)核中存儲數(shù)據(jù)報的內(nèi)存區(qū)域映射到檢測程序的應(yīng)用程序空間或者在用戶空間建立一緩存,并將其映射到內(nèi)核空間。
    很多相關(guān)公司都采用了這種技術(shù)Firewall/IDS等,這兩種技術(shù)已經(jīng)很成熟了
    posted on 2008-04-05 10:50 allic 閱讀(108) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
    博客園   IT新聞   Chat2DB   C++博客   博問  
     
    主站蜘蛛池模板: 亚洲国产精品午夜电影| 免费播放美女一级毛片| 免费高清在线爱做视频| 麻豆安全免费网址入口| 久久精品国产96精品亚洲| 无限动漫网在线观看免费| 九九久久国产精品免费热6| 亚洲国产一区在线| 国产无遮挡吃胸膜奶免费看视频 | 亚洲AV无码国产剧情| 国内精品99亚洲免费高清| 曰批全过程免费视频在线观看 | 男女一边桶一边摸一边脱视频免费| 亚洲成a人片77777kkkk| 超pen个人视频国产免费观看| 成全视频在线观看免费| 亚洲AV成人一区二区三区观看| 亚洲丁香色婷婷综合欲色啪| 国产又大又粗又硬又长免费| 91人人区免费区人人| 国产国产人免费人成成免视频| 精品亚洲国产成人| 亚洲高清国产拍精品26U| 国产亚洲精品免费| 在线观看特色大片免费视频| 最近免费mv在线观看动漫| 国产精品亚洲一区二区三区久久 | 亚洲av永久无码| 亚洲酒色1314狠狠做| 亚洲中文字幕无码爆乳av中文| 好大好深好猛好爽视频免费| 午夜免费福利片观看| 羞羞视频免费网站在线看| 性色av极品无码专区亚洲| 亚洲人成人77777在线播放| 亚洲日本一区二区三区在线| 亚洲国产成人a精品不卡在线 | 亚洲美女人黄网成人女| 亚洲日韩小电影在线观看| 亚洲福利精品一区二区三区| 在线观看视频免费完整版|