日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

Linux模塊

系統(tǒng) 3092 0

Linux模塊

一、為什么要使用模塊

? ? ? 由于linux使用的是整體結(jié)構(gòu),不是模塊化的結(jié)構(gòu),整體結(jié)構(gòu)實(shí)現(xiàn)的操作系統(tǒng)可擴(kuò)展性差。linux為了擴(kuò)展系統(tǒng),使用了模塊的技術(shù),模塊能夠從系統(tǒng)中動(dòng)態(tài)裝入和卸載,這樣使得linux也具有很好的可擴(kuò)展性。

?

二、linux中哪些代碼作為模塊實(shí)現(xiàn),哪些直接編譯進(jìn)內(nèi)核?

? ? ? 當(dāng)然我們是盡量把代碼編譯成模塊,這樣就可以根據(jù)需要進(jìn)行鏈接,內(nèi)核的代碼量也會少很多。幾乎所有的高層組件—文件系統(tǒng)、設(shè)備驅(qū)動(dòng)程序、可執(zhí)行格式、網(wǎng)絡(luò)層等等—都可以作為模塊進(jìn)行編譯。

? ? ? 然而有些代碼確必須直接編譯進(jìn)內(nèi)核。這些代碼通常是對數(shù)據(jù)結(jié)構(gòu)或者函數(shù)進(jìn)行修改。如內(nèi)核中已經(jīng)定義好了的數(shù)據(jù)結(jié)構(gòu),如果要改變這個(gè)數(shù)據(jù)結(jié)構(gòu),那么只有從新編譯內(nèi)核了。

?

三、管理模塊

? ? ?內(nèi)核主要完成管理模塊的兩個(gè)任務(wù)。第一個(gè)任務(wù)是確保內(nèi)核的其它部分可以訪問該模塊的全局符號,模塊還必須知道全局符號在內(nèi)核及其它模塊中的地址。因此,在鏈接模塊時(shí),一定要解決模塊間的引用關(guān)系。第二個(gè)任務(wù)是記錄模塊的使用情況,以便再其它模塊或者內(nèi)核的其它部分正在使用這個(gè)模塊時(shí),不能卸載這個(gè)模塊。

?

四、模塊使用的數(shù)據(jù)結(jié)構(gòu)

? ? ? 每個(gè)模塊都用一個(gè)module描述符描述,并且鏈接到一個(gè)以modules變量為鏈表頭的雙向循環(huán)鏈表中。

module描述符:

        
          struct
        
        
           module

{

    
        
        
          enum
        
         module_state state;  
        
          //
        
        
          模塊內(nèi)部狀態(tài)  
        
        
          struct
        
         list_head list;  
        
          //
        
        
          用于鏈接到鏈表中
        
        
          char
        
         name[MODULE_NAME_LEN];  
        
          //
        
        
          模塊名字
        
        
          struct
        
         module_kobject mkobj;  
        
          //
        
        
          用于Sysfs的kobject
        
        
          struct
        
         module_param_attrs *param_attrs;  
        
          //
        
        
          指向模塊參數(shù)描述符
        
        
          const
        
        
          struct
        
         kernel_symbol *syms;  
        
          //
        
        
          指向?qū)С龇枖?shù)組的指針
        
        

    unsigned 
        
          int
        
         num_syms;  
        
          //
        
        
          導(dǎo)出符號數(shù)
        
        
          const
        
         unsigned 
        
          long
        
         *crcs;  
        
          //
        
        
          指向?qū)С龇朇RC值數(shù)組指針
        
        
          const
        
        
          struct
        
         kernel_symbol *gpl_syms;  
        
          //
        
        
          GPL格式導(dǎo)出符號
        
        

    unsigned 
        
          int
        
        
           num_gpl_syms;

    
        
        
          const
        
         unsigned 
        
          long
        
         *
        
          gpl_crcs;

    unsigned 
        
        
          int
        
         num_exentries;  
        
          //
        
        
          模塊異常表項(xiàng)數(shù)
        
        
          const
        
        
          struct
        
         exception_table_entry *extable;  
        
          //
        
        
          指向模塊異常表的指針
        
        
          int
        
         (*init)(
        
          void
        
        );  
        
          //
        
        
          模塊初始化方法
        
        
          void
        
         *module_init;  
        
          //
        
        
          用于模塊初始化的動(dòng)態(tài)內(nèi)存區(qū)指針
        
        
          void
        
         *module_core;  
        
          //
        
        
          用于模塊核心函數(shù)與數(shù)據(jù)結(jié)構(gòu)的動(dòng)態(tài)內(nèi)存區(qū)指針
        
        

    unsigned 
        
          long
        
         init_size, core_size;  
        
          //
        
        
          模塊初始化動(dòng)態(tài)內(nèi)存區(qū)大小,模塊核心函數(shù)與數(shù)據(jù)結(jié)構(gòu)的動(dòng)態(tài)內(nèi)存區(qū)大小
        
        

    unsigned 
        
          long
        
         init_text_size, core_text_size;  
        
          //
        
        
          模塊初始化的可執(zhí)行代碼大小,模塊核心可執(zhí)行代碼的大小,只在連接模塊時(shí)使用
        
        
          struct
        
        
           mod_arch_specific arch;

    
        
        
          int
        
        
          unsafe
        
        
          ;

    
        
        
          int
        
        
           license_gplok;



#ifdef CONFIG_MODULE_UNLOAD

    
        
        
          struct
        
         module_ref 
        
          ref
        
        [NR_CPUS];  
        
          //
        
        
          每cpu使用計(jì)數(shù)器變量
        
        
          /*
        
        
           What modules depend on me? 
        
        
          */
        
        
          struct
        
         list_head modules_which_use_me;  
        
          //
        
        
          依賴于該模塊的模塊鏈表
        
        
          struct
        
         task_struct *waiter;  
        
          //
        
        
          正在等待模塊被卸載的進(jìn)程,即卸載模塊的進(jìn)程
        
        
          void
        
         (*exit)(
        
          void
        
        );  
        
          //
        
        
          模塊退出的方法
        
        
          #endif
        
        
          

#ifdef CONFIG_KALLSYMS

    Elf_Sym 
        
        *symtab;  
        
          //
        
        
          proc/kallsysms文件中所列模塊ELF符號數(shù)組指針
        
        

    unsigned 
        
          long
        
        
           num_symtab;

    
        
        
          char
        
         *strtab;  
        
          //
        
        
          proc/kallsysms文件中所列模塊ELF符號的字符串表
        
        
          struct
        
         module_sect_attrs *sect_attrs;  
        
          //
        
        
          模塊分節(jié)屬性描述符數(shù)組指針
        
        
          #endif
        
        
          void
        
         *
        
          percpu;

    
        
        
          char
        
         *args;  
        
          //
        
        
          模塊連接時(shí)使用的命令行參數(shù)
        
        

};
      

? ? ? module數(shù)據(jù)結(jié)構(gòu)主要描述了模塊導(dǎo)出符號,模塊使用的動(dòng)態(tài)內(nèi)存,模塊的加載和釋放函數(shù),模塊的引用等。

? ? ? 當(dāng)裝載一個(gè)模塊到內(nèi)核中時(shí),必須用合適的地址替換在模塊對象代碼中引用的所有全局內(nèi)核符號。這主要由insmod程序來完成。內(nèi)核使用一些專門的內(nèi)核符號表,用于保存模塊訪問的符號和相應(yīng)的地址。它們在內(nèi)核代碼分三節(jié):__kstrtab節(jié)(保存符號名)、__ksymtab節(jié)(所有模塊可使用的符號地址)和__ksymtab_gpl節(jié)(GPL兼容許可證下發(fā)布的模塊可以使用的符號地址)。

? ? ? 已經(jīng)裝載到內(nèi)核中的模塊也可以導(dǎo)出自己的符號,這樣其它模塊就可以訪問這些符號。模塊符號部分表保存在模塊代碼段__ksymtab、__ksymtab_gpl和__kstrtab部分中。可以使用宏EXPOPT_SYMBOL和EXPORT_SYMPOL_GPL來導(dǎo)出符號。當(dāng)模塊裝載進(jìn)內(nèi)核時(shí),模塊的導(dǎo)出符號被拷貝到兩個(gè)內(nèi)存數(shù)組中,而數(shù)組的地址保存在module描述符的syms和gpl_syms字段中。

? ? ? 一個(gè)模塊可以引用另一個(gè)模塊所導(dǎo)出的符號。module描述符中有個(gè)字段modules_which_use_me,它是一個(gè)依賴鏈表的頭部,該鏈表保存了使用該模塊的所有其他模塊。鏈表中每個(gè)元素都是一個(gè)module_use描述符,該描述符保存指向鏈表中相鄰元素的指針以及一個(gè)指向相應(yīng)模塊對象的指針。只有依賴鏈表不為空,就不能卸載該模塊。

?

五、模塊的裝載

? ? ? 模塊的裝載主要通過sys_init_module服務(wù)例程來實(shí)現(xiàn)的,是由insmod外部程序通過系統(tǒng)調(diào)用來調(diào)用該函數(shù)。下面我們來分析sys_init_module函數(shù):

        asmlinkage 
        
          long
        
        
          

sys_init_module(
        
        
          void
        
         __user *
        
          umod,

        unsigned 
        
        
          long
        
        
           len,

        
        
        
          const
        
        
          char
        
         __user *
        
          uargs)

{

    
        
        
          struct
        
         module *
        
          mod;

    
        
        
          int
        
         ret = 
        
          0
        
        
          ;

    …

    mod 
        
        =
        
           load_module(umod, len, uargs);

    …

    
        
        
          if
        
         (mod->init !=
        
           NULL)

        ret 
        
        = mod->init();   
        
          //
        
        
          調(diào)用模塊初始化函數(shù)初始化模塊
        
        
              …

    mod
        
        ->state =
        
           MODULE_STATE_LIVE;

    module_free(mod, mod
        
        ->module_init);  
        
          //
        
        
          釋放初始化使用的內(nèi)存
        
        

    mod->module_init =
        
           NULL;

    mod
        
        ->init_size = 
        
          0
        
        
          ;

    mod
        
        ->init_text_size = 
        
          0
        
        
          ;

…

}
        
      

? ? ? 這個(gè)函數(shù)主要是調(diào)用load_module函數(shù)加載模塊代碼到內(nèi)存中,并初始化該模塊對象mod;調(diào)用初始化模塊函數(shù)初始化模塊,釋放模塊中的初始化代碼動(dòng)態(tài)內(nèi)存空間。其中傳遞的參數(shù)umod是insmod程序在用戶態(tài)時(shí)將模塊文件拷貝到內(nèi)存中的起始地址,len是模塊文件的大小,uargs是調(diào)用命令insmod時(shí)的命令行參數(shù)。

? ? ? 加載模塊的工作其實(shí)主要還是由函數(shù)load_module來完成,這個(gè)函數(shù)完成了將模塊文件從用戶空間加載到臨時(shí)內(nèi)核空間,對模塊文件進(jìn)行合法性檢查,并抽取出模塊文件中的核心函數(shù)和數(shù)據(jù)結(jié)構(gòu)到內(nèi)核的另一個(gè)動(dòng)態(tài)內(nèi)存區(qū),并重定位模塊中的符號,初始化module對象,將mod對象加入到sysfs文件系統(tǒng)中。

        
          static
        
        
          struct
        
         module *load_module(
        
          void
        
         __user *
        
          umod,

                  unsigned 
        
        
          long
        
        
           len,

                  
        
        
          const
        
        
          char
        
         __user *
        
          uargs)

{

    Elf_Ehdr 
        
        *
        
          hdr;

    Elf_Shdr 
        
        *
        
          sechdrs;

    
        
        
          char
        
         *secstrings, *args, *modmagic, *strtab =
        
           NULL;

    unsigned 
        
        
          int
        
         i, symindex = 
        
          0
        
        , strindex = 
        
          0
        
        
          , setupindex, exindex,

        exportindex, modindex, obsparmindex, infoindex, gplindex,

        crcindex, gplcrcindex, versindex, pcpuindex;

    
        
        
          long
        
        
           arglen;

    
        
        
          struct
        
         module *
        
          mod;

    
        
        
          long
        
         err = 
        
          0
        
        
          ;

    
        
        
          void
        
         *percpu = NULL, *ptr = NULL; 
        
          /*
        
        
           Stops spurious gcc warning 
        
        
          */
        
        
          struct
        
         exception_table_entry *
        
          extable;

    …

    
        
        
          if
        
         (len > 
        
          64
        
         * 
        
          1024
        
         * 
        
          1024
        
         || (hdr = vmalloc(len)) == NULL)  
        
          //
        
        
          超過64MB,或者分配內(nèi)存失敗,否則分配一個(gè)臨時(shí)的內(nèi)核空間來存放內(nèi)核模塊
        
        
          return
        
         ERR_PTR(-
        
          ENOMEM);

    
        
        
          if
        
         (copy_from_user(hdr, umod, len) != 
        
          0
        
        ) {  
        
          //
        
        
          用空間將模塊目標(biāo)代碼拷貝到內(nèi)核
        
        

        err = -
        
          EFAULT;

        
        
        
          goto
        
        
           free_hdr;

    }

    …  
        
        
          //
        
        
          省略的代碼為檢查模塊的合法性
        
        

    sechdrs = (
        
          void
        
         *)hdr + hdr->e_shoff;  
        
          //
        
        
          節(jié)的頭表
        
        

    secstrings = (
        
          void
        
         *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;  
        
          //
        
        
          節(jié)點(diǎn)頭字符串表
        
        

    sechdrs[
        
          0
        
        ].sh_addr = 
        
          0
        
        
          ;

    
        
        
          for
        
         (i = 
        
          1
        
        ; i < hdr->e_shnum; i++
        
          ) {

        
        
        
          if
        
         (sechdrs[i].sh_type != SHT_NOBITS  
        
          //
        
        
          SHT_NOBITS表示該節(jié)點(diǎn)在文件中無內(nèi)容
        
        

            && len < sechdrs[i].sh_offset +
        
           sechdrs[i].sh_size)

            
        
        
          goto
        
        
           truncated;



        
        
        
          /*
        
        
           Mark all sections sh_addr with their address in the

           temporary image. 
        
        
          */
        
        
          

        sechdrs[i].sh_addr 
        
        = (size_t)hdr + sechdrs[i].sh_offset;  
        
          //
        
        
          把每個(gè)節(jié)點(diǎn)的地址設(shè)置為在內(nèi)存中對應(yīng)的地址
        
        
          if
        
         (sechdrs[i].sh_type == SHT_SYMTAB) {  
        
          //
        
        
          節(jié)點(diǎn)為符號表
        
        

            symindex =
        
           i;

            strindex 
        
        = sechdrs[i].sh_link;  
        
          //
        
        
          字符串表在節(jié)點(diǎn)頭表中的索引
        
        

            strtab = (
        
          char
        
         *)hdr + sechdrs[strindex].sh_offset;  
        
          //
        
        
          字符串表
        
        
                  }

#ifndef CONFIG_MODULE_UNLOAD  
        
        
          //
        
        
          沒有定義模塊卸載
        
        
          /*
        
        
           Don't load .exit sections 
        
        
          */
        
        
          //
        
        
          不將.exit節(jié)加載到內(nèi)存
        
        
          if
        
         (strncmp(secstrings+sechdrs[i].sh_name, 
        
          "
        
        
          .exit
        
        
          "
        
        , 
        
          5
        
        ) == 
        
          0
        
        
          )

            sechdrs[i].sh_flags 
        
        &= ~(unsigned 
        
          long
        
        
          )SHF_ALLOC;


        
        
          #endif
        
        
          

    }

    modindex 
        
        = find_sec(hdr, sechdrs, secstrings, 
        
          "
        
        
          .gnu.linkonce.this_module
        
        
          "
        
        );  
        
          //
        
        
          .gnu.linkonce.this_module在節(jié)點(diǎn)頭表中的索引
        
        
              …

    mod 
        
        = (
        
          void
        
         *
        
          )sechdrs[modindex].sh_addr;

…  
        
        
          //
        
        
          省略代碼處理參數(shù)和處理每cpu變量
        
        

    mod->state =
        
           MODULE_STATE_COMING;

    layout_sections(mod, hdr, sechdrs, secstrings);  
        
        
          //
        
        
          節(jié)的從新布局,合并所有帶有SHF_ALLOC標(biāo)記的節(jié),并計(jì)算每個(gè)節(jié)的大小和偏移量,包括計(jì)算初始化代碼和核心代碼的空間大小
        
        

    ptr = module_alloc(mod->core_size);  
        
          //
        
        
          為模塊代碼分配動(dòng)態(tài)內(nèi)存
        
        
              …

    memset(ptr, 
        
        
          0
        
        , mod->
        
          core_size);

    mod
        
        ->module_core =
        
           ptr;

    ptr 
        
        = module_alloc(mod->init_size);  
        
          //
        
        
          為模塊初始化代碼分配動(dòng)態(tài)內(nèi)存
        
        
              …

    memset(ptr, 
        
        
          0
        
        , mod->
        
          init_size);

    mod
        
        ->module_init =
        
           ptr;

    …

    
        
        
          for
        
         (i = 
        
          0
        
        ; i < hdr->e_shnum; i++) {  
        
          //
        
        
          將臨時(shí)內(nèi)核模塊的數(shù)據(jù)拷貝到新的動(dòng)態(tài)內(nèi)存中
        
        
          void
        
         *
        
          dest;



        
        
        
          if
        
         (!(sechdrs[i].sh_flags &
        
           SHF_ALLOC))

            
        
        
          continue
        
        
          ;



        
        
        
          if
        
         (sechdrs[i].sh_entsize &
        
           INIT_OFFSET_MASK)

            dest 
        
        = mod->
        
          module_init

                
        
        + (sechdrs[i].sh_entsize & ~
        
          INIT_OFFSET_MASK);

        
        
        
          else
        
        
          

            dest 
        
        = mod->module_core +
        
           sechdrs[i].sh_entsize;



        
        
        
          if
        
         (sechdrs[i].sh_type !=
        
           SHT_NOBITS)

            memcpy(dest, (
        
        
          void
        
         *
        
          )sechdrs[i].sh_addr,

                   sechdrs[i].sh_size);

        sechdrs[i].sh_addr 
        
        = (unsigned 
        
          long
        
        )dest;  
        
          //
        
        
          更新節(jié)在內(nèi)存中的地址
        
        

        DEBUGP(
        
          "
        
        
          \t0x%lx %s\n
        
        
          "
        
        , sechdrs[i].sh_addr, secstrings +
        
           sechdrs[i].sh_name);

    }

    mod 
        
        = (
        
          void
        
         *)sechdrs[modindex].sh_addr;  
        
          //
        
        
          mod指向新內(nèi)存
        
        

    module_unload_init(mod);  
        
          //
        
        
          初始化mod的卸載字段


        
        
          //
        
        
          修正符號表中的地址值
        
        

    err =
        
           simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,

                   mod);

…

    
        
        
          for
        
         (i = 
        
          1
        
        ; i < hdr->e_shnum; i++) {  
        
          //
        
        
          重定位各個(gè)節(jié)中的符號
        
        
          const
        
        
          char
        
         *strtab = (
        
          char
        
         *
        
          )sechdrs[strindex].sh_addr;

        unsigned 
        
        
          int
        
         info =
        
           sechdrs[i].sh_info;

            
        
        
          if
        
         (info >= hdr->
        
          e_shnum)

            
        
        
          continue
        
        
          ;

        
        
        
          if
        
         (!(sechdrs[info].sh_flags &
        
           SHF_ALLOC))

            
        
        
          continue
        
        
          ;

        
        
        
          if
        
         (sechdrs[i].sh_type == SHT_REL)  
        
          //
        
        
          當(dāng)前節(jié)是重定位節(jié)
        
        

            err =
        
           apply_relocate(sechdrs, strtab, symindex, i,mod);

        
        
        
          if
        
         (err < 
        
          0
        
        
          )

            
        
        
          goto
        
        
           cleanup;

    }

    …

    vfree(hdr);  
        
        
          //
        
        
          釋放臨時(shí)分配的內(nèi)核空間
        
        
              …

}
        
      

? ? ? 代碼中的simplify_symbols主要就是查找內(nèi)核符號表,將模塊符號表中未決的符號修改為內(nèi)核符號表中對應(yīng)的符號的值,即符號對應(yīng)的線性地址。apply_relocate函數(shù)主要就是通過模塊中重定位節(jié)的信息將模塊中需要重定位的符號地址重新定位。

?

六、模塊的卸載

? ? ? 模塊卸載主要完成對模塊是否可以卸載,先是檢查用戶是否有這個(gè)權(quán)限,如果沒有權(quán)限是不能卸載模塊的。如果有其它模塊在引用該模塊,也不能卸載該模塊,根據(jù)用戶給的模塊名到模塊鏈表中查找模塊,如果引用模塊的計(jì)數(shù)不為0,則阻塞當(dāng)前進(jìn)程,否則將模塊從modules鏈中刪除;如果模塊自定義了exit函數(shù),則執(zhí)行該函數(shù),將模塊從文件系統(tǒng)sysfs注銷,釋放模塊占用的內(nèi)存區(qū)。

        asmlinkage 
        
          long
        
        
          

sys_delete_module(
        
        
          const
        
        
          char
        
         __user *name_user, unsigned 
        
          int
        
        
           flags)

{

    
        
        
          struct
        
         module *
        
          mod;

    
        
        
          char
        
        
           name[MODULE_NAME_LEN];

    
        
        
          int
        
         ret, forced = 
        
          0
        
        
          ;

    
        
        
          if
        
         (!
        
          capable(CAP_SYS_MODULE))

        
        
        
          return
        
         -
        
          EPERM;

    
        
        
          if
        
         (strncpy_from_user(name, name_user, MODULE_NAME_LEN-
        
          1
        
        ) < 
        
          0
        
        
          )

        
        
        
          return
        
         -
        
          EFAULT;

    name[MODULE_NAME_LEN
        
        -
        
          1
        
        ] = 
        
          '
        
        
          \0
        
        
          '
        
        
          ;

    mod 
        
        = find_module(name);  
        
          //
        
        
          查找模塊
        
        
          if
        
         (!list_empty(&mod->modules_which_use_me)) {  
        
          //
        
        
          查看是否有其它模塊使用當(dāng)前模塊
        
        

        ret = -
        
          EWOULDBLOCK;

        
        
        
          goto
        
        
          out
        
        
          ;

    }

    
        
        
          if
        
         (mod->state != MODULE_STATE_LIVE) {  
        
          //
        
        
          判斷模塊是否是正常運(yùn)行的
        
        
          /*
        
        
           FIXME: if (force), slam module count and wake up

                   waiter --RR 
        
        
          */
        
        
          

        DEBUGP(
        
        
          "
        
        
          %s already dying\n
        
        
          "
        
        , mod->
        
          name);

        ret 
        
        = -
        
          EBUSY;

        
        
        
          goto
        
        
          out
        
        
          ;

    }

    
        
        
          if
        
         ((mod->init != NULL && mod->exit == NULL)  
        
          //
        
        
          如果模塊有init卻沒有exit,則不能卸載模塊
        
        

        || mod->
        
          unsafe
        
        
          ) {

        forced 
        
        =
        
           try_force(flags);

        
        
        
          if
        
         (!
        
          forced) {

                ret 
        
        = -
        
          EBUSY;

            
        
        
          goto
        
        
          out
        
        
          ;

        }

    }

    mod
        
        ->waiter = current;  
        
          //
        
        
          卸載該模塊的進(jìn)程
        
        

    ret = try_stop_module(mod, flags, &
        
          forced);



    
        
        
          if
        
         (!forced && module_refcount(mod) != 
        
          0
        
        )  
        
          //
        
        
          等待模塊引用計(jì)數(shù)為0
        
        
                  wait_for_zero_refcount(mod);

    
        
        
          if
        
         (mod->exit != NULL) {  
        
          //
        
        
          調(diào)用該模塊定義的exit函數(shù)
        
        

        up(&
        
          module_mutex);

        mod
        
        ->
        
          exit();

        down(
        
        &
        
          module_mutex);

    }

    free_module(mod);  
        
        
          //
        
        
          將模塊從sysfs注銷和釋放模塊占用的內(nèi)存
        
        
          …

}
        
      

?

?
?
?

在Linux下和Windows下遍歷目錄的方法及如何達(dá)成一致性操作

?
?

最近因?yàn)闇y試目的需要遍歷一個(gè)目錄下面的所有文件進(jìn)行操作,主要是讀每個(gè)文件的內(nèi)容,只要知道文件名就OK了。在Java中直接用File類就可以搞定,因?yàn)镴ava中使用了組合模式,使得客戶端對單個(gè)文件和文件夾的使用具有一致性,非常方便。但在C中就不一樣了,而且在不同的平臺下使用方法也不同。在Linux下實(shí)現(xiàn)該功能就非常方便,因?yàn)樽詭в蠥PI庫,幾個(gè)函數(shù)用起來得心應(yīng)手(雖然有些小問題,后面說),在Windows下實(shí)現(xiàn)就不是那么方便,雖然也有自己的API,但用法有些晦澀難懂,因?yàn)闆]有封裝起來,需要自己一步一步進(jìn)行操作,因?yàn)橛玫氖荳indows API庫函數(shù)所以如果對Windows編程不熟悉的話,照搬網(wǎng)上的代碼錯(cuò)了也不易調(diào)試。為此,我把這些操作都封裝成類似Linux下的庫函數(shù),一方面簡化透明了操作,另一方面(也許更重要)就是移植性,這樣將包含該功能的程序從Windows上移植到Linux下就無需改動(dòng)代碼了(刪掉實(shí)現(xiàn)封裝的文件,因?yàn)長inux下自帶了),當(dāng)然從Linux下移植到Windows下同樣方便(增加實(shí)現(xiàn)封裝的文件即可),這就是所謂的OCP原則吧(開放封閉原則,具體見: 程序員該有的藝術(shù)氣質(zhì)—SOLID原則 )。好了,首先看下Linux下是如何實(shí)現(xiàn)這個(gè)功能的。

一、Linux下遍歷目錄的方法

?Linux下實(shí)現(xiàn)目錄操作的API函數(shù)都在頭文件dirent.h中,截取部分該文件內(nèi)容如下:

          
            /*
          
          
            * structure describing an open directory. 
          
          
            */
          
          
            

typedef 
          
          
            struct
          
          
             _dirdesc {

    
          
          
            int
          
              dd_fd;          
          
            /*
          
          
            * file descriptor associated with directory 
          
          
            */
          
          
            long
          
              dd_loc;        
          
            /*
          
          
            * offset in current buffer 
          
          
            */
          
          
            long
          
              dd_size;       
          
            /*
          
          
            * amount of data returned by getdirentries 
          
          
            */
          
          
            char
          
              *dd_buf;       
          
            /*
          
          
            * data buffer 
          
          
            */
          
          
            int
          
              dd_len;         
          
            /*
          
          
            * size of data buffer 
          
          
            */
          
          
            long
          
              dd_seek;       
          
            /*
          
          
            * magic cookie returned by getdirentries 
          
          
            */
          
          
            long
          
              dd_rewind;     
          
            /*
          
          
            * magic cookie for rewinding 
          
          
            */
          
          
            int
          
              dd_flags;       
          
            /*
          
          
            * flags for readdir 
          
          
            */
          
          
            struct
          
           pthread_mutex    *dd_lock;    
          
            /*
          
          
            * lock 
          
          
            */
          
          
            struct
          
           _telldir *dd_td;    
          
            /*
          
          
            * telldir position recording 
          
          
            */
          
          
            

} DIR;



typedef    
          
          
            void
          
           *
          
                DIR;



DIR    
          
          *opendir(
          
            const
          
          
            char
          
           *
          
            );

DIR    
          
          *fdopendir(
          
            int
          
          
            );


          
          
            struct
          
           dirent *
          
            readdir(DIR 
          
          *
          
            );
            
void?? ? seekdir(DIR *, long);
long?? ? telldir(DIR *);
void rewinddir(DIR * ); int closedir(DIR *);

          
            struct
          
          
             dirent

{

     
          
          
            long
          
           d_ino;              
          
            /*
          
          
             inode number
          
          
            */
          
          
            

     off_t d_off;             
          
          
            /*
          
          
             offset to this dirent
          
          
            */
          
          
            

     unsigned 
          
          
            short
          
           d_reclen; 
          
            /*
          
          
             length of this d_name
          
          
            */
          
          
            

     unsigned 
          
          
            char
          
           d_type;    
          
            /*
          
          
             the type of d_name
          
          
            */
          
          
            char
          
           d_name[
          
            1
          
          ];          
          
            /*
          
          
             file name (null-terminated)
          
          
            */
          
          
            

};
          
        

關(guān)鍵部分就是 DIR 這個(gè)結(jié)構(gòu)體的定義,包括文件描述符、緩沖區(qū)偏移、大小、緩沖區(qū)內(nèi)容等,下面定義的就是具體的目錄操作函數(shù)了,有打開目錄、讀目錄、重置讀取位置、關(guān)閉目錄等,這里我所需要的就是打開、讀和關(guān)閉這三個(gè)最基本的目錄操作,下面是使用例子:

          #include <stdio.h>
          
             

#include 
          
          <stdlib.h>
          
                

#include 
          
          <
          
            string
          
          .h>
          
              

#include 
          
          <dirent.h> 




          
            #define
          
           MAX_LEN 65535




          
            int
          
           main(
          
            void
          
          
            ) 

{ 

    DIR 
          
          *
          
            dir; 

    
          
          
            struct
          
           dirent *
          
            ptr; 

    
          
          
            char
          
           *
          
            flow[MAX_LEN];

    
          
          
            int
          
           num = 
          
            0
          
          , i = 
          
            0
          
          
            ;

   

    
          
          
            if
          
           ((dir=opendir(
          
            ".
          
          
            /data
          
          
            "
          
          )) ==
          
             NULL) 

    { 

        perror(
          
          
            "
          
          
            Open dir error...
          
          
            "
          
          
            ); 

        exit(
          
          
            1
          
          
            );        

    } 

    
          
          
            //
          
          
             readdir() return next enter point of directory dir
          
          
            while
          
           ((ptr=readdir(dir)) !=
          
             NULL) 

    { 

        flow[num
          
          ++] = ptr->
          
            d_name;


          
          
            //
          
          
                  printf("%s\n", flow[num - 1]);
          
          
                } 



    
          
          
            for
          
          (i = 
          
            0
          
          ; i < num; i++
          
            )

    {

        printf(
          
          
            "
          
          
            %s\n
          
          
            "
          
          
            , flow[i]);

    }

   

    closedir(dir); 

}
          
        

運(yùn)行結(jié)果如下:

?

一看這結(jié)果就不對,輸出的都是同一個(gè)文件名(最后一個(gè)文件的文件名), 哪里出了問題呢?將代碼中 // ?printf("%s\n", flow[num - 1]); 這行注釋去掉再運(yùn)行,發(fā)現(xiàn)注釋處輸出的是正確的,兩者都是輸出的flow數(shù)組元素怎么結(jié)果不一樣呢?經(jīng)過調(diào)試發(fā)現(xiàn)是 flow[num ++] = ptr-> d_name; 這句代碼的問題,因?yàn)檫@是引用拷貝(地址拷貝),所有的flow元素全部指向同一個(gè)對象ptr-> d_name ,雖然ptr-> d_name 對象每次的內(nèi)容不同(也就是前面正確輸出的原因),但所有內(nèi)容都共享一個(gè)地址,用一個(gè)簡單的圖說明就是:

當(dāng)然這個(gè)問題也比較好解決,也是比較常見的問題,用字符串拷貝或內(nèi)存拷貝就行了,給flow每個(gè)元素重新申請一塊內(nèi)存。

          #include <stdio.h>
          
             

#include 
          
          <stdlib.h>
          
                

#include 
          
          <
          
            string
          
          .h>
          
              

#include 
          
          <dirent.h> 




          
            #define
          
           MAX_LEN 65535




          
            int
          
           main(
          
            void
          
          
            ) 

{ 

    DIR 
          
          *
          
            dir; 

    
          
          
            struct
          
           dirent *
          
            ptr; 

    
          
          
            char
          
           *
          
            flow[MAX_LEN];

    
          
          
            int
          
           num = 
          
            0
          
          , i = 
          
            0
          
          
            ;

   

    
          
          
            if
          
           ((dir=opendir(
          
            "
          
          
            ./data
          
          
            "
          
          )) ==
          
             NULL) 

    { 

        perror(
          
          
            "
          
          
            Open dir error...
          
          
            "
          
          
            ); 

        exit(
          
          
            1
          
          
            );        

    } 

    
          
          
            //
          
          
             readdir() return next enter point of directory dir
          
          
            while
          
           ((ptr=readdir(dir)) !=
          
             NULL) 

    { 

        flow[num] 
          
          = (
          
            char
          
          *)malloc(
          
            sizeof
          
          (
          
            char
          
          
            ));

        strcpy(flow[num], ptr
          
          ->
          
            d_name);

        num
          
          ++
          
            ;

    } 



    
          
          
            for
          
          (i = 
          
            0
          
          ; i < num; i++
          
            )

    {

        printf(
          
          
            "
          
          
            %s\n
          
          
            "
          
          
            , flow[i]);

    }

   

    closedir(dir); 

}
          
        

?最終結(jié)果就正確了。

二、Windows下遍歷目錄的方法

?在Windows下就比較麻煩了,所要用到的函數(shù)都在windows.h中,Windows編程本來就比較繁瑣,下面就不一一介紹所用到的函數(shù)了,直接給出封裝的過程。

1. 首先模擬Linux下自帶的頭文件dirent.h

不同的是DIR中去掉了一些不需要的屬性,及只定義了三個(gè)我所需要的操作(按需定義)。

          
            
              // dirent.h
            
            
#ifndef _SYS_DIRENT_H
#define _SYS_DIRENT_H typedef struct _dirdesc { int dd_fd; / * * file descriptor associated with directory */ long dd_loc; /* * offset in current buffer */ long dd_size; /* * amount of data returned by getdirentries */ char *dd_buf; /* * data buffer */ int dd_len; /* * size of data buffer */ long dd_seek; /* * magic cookie returned by getdirentries */ } DIR; # define __dirfd(dp) ((dp) -> dd_fd) DIR *opendir ( const char * ); struct dirent *readdir (DIR * ); void rewinddir (DIR * ); int closedir (DIR * ); #include <sys/types.h> struct dirent { long d_ino; /* inode number */ off_t d_off; /* offset to this dirent */ unsigned short d_reclen; /* length of this d_name */ unsigned char d_type; /* the type of d_name */ char d_name[ 1 ]; /* file name (null-terminated) */ }; #endif

?

2. 三個(gè)目錄操作函數(shù)的實(shí)現(xiàn)

當(dāng)然這是最關(guān)鍵的部分,我不知道Linux下是怎么實(shí)現(xiàn)的(找了下沒找到),Windows下實(shí)現(xiàn)如下,主要是FindFirstFile()和FindNextFile()這兩個(gè)Windows函數(shù),對Windows編程不精,也不好解釋什么,需要搞明白為啥這樣實(shí)現(xiàn)請上網(wǎng)搜或MSDN。

          
            // dirent.c
          
          
#include <stdio.h> #include <windows.h> #include " dirent.h " static HANDLE hFind; DIR *opendir( const char * name) { DIR * dir; WIN32_FIND_DATA FindData; char namebuf[ 512 ]; sprintf(namebuf, " %s\\*.* " ,name); hFind = FindFirstFile(namebuf, & FindData ); if (hFind == INVALID_HANDLE_VALUE) { printf( " FindFirstFile failed (%d)\n " , GetLastError()); return 0 ; } dir = (DIR *)malloc( sizeof (DIR)); if (! dir) { printf( " DIR memory allocate fail\n " ); return 0 ; } memset(dir, 0 , sizeof (DIR)); dir ->dd_fd = 0 ; // simulate return return dir; } struct dirent *readdir(DIR * d) { int i; static struct dirent dirent; BOOL bf; WIN32_FIND_DATA FileData; if (! d) { return 0 ; } bf = FindNextFile(hFind,& FileData); // fail or end if (! bf) { return 0 ; } for (i = 0 ; i < 256 ; i++ ) { dirent.d_name[i] = FileData.cFileName[i]; if (FileData.cFileName[i] == ' \0 ' ) break ; } dirent.d_reclen = i; dirent.d_reclen = FileData.nFileSizeLow; // check there is file or directory if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { dirent.d_type = 2 ; } else { dirent.d_type = 1 ; } return (& dirent); } int closedir(DIR * d) { if (!d) return - 1 ; hFind = 0 ; free(d); return 0 ; }

?

3. 使用方法

與Linux下使用一模一樣,不需要改動(dòng)一句代碼就可應(yīng)用,但卻發(fā)現(xiàn)了與Linux下自帶實(shí)現(xiàn)同樣的問題,即也是引用拷貝,如下。

?因?yàn)檫@是我們自己實(shí)現(xiàn)的代碼,所以字符串拷貝不是最佳解決方案,修改原實(shí)現(xiàn)代碼才是最好的方法,當(dāng)然如果是為了可移植性,就不需要改動(dòng)了,就用字符串拷貝這樣代碼到Linux下就不需要改動(dòng)了。下面看如何修改原實(shí)現(xiàn)解決:

a. 首先定位問題,可以很明顯的知道是readdir這個(gè)函數(shù)的問題;

b. 然后找出問題根源,通過前面的分析可知問題的根源在于每次ptr->d_name使用的是同一內(nèi)存地址,即ptr地址不變,而ptr是readdir返回的struct dirent指針,所以問題的根源在于readdir返回的dirent結(jié)構(gòu)體地址問題,從上面代碼中可以看到static struct dirent dirent; 這句代碼,其中dirent的地址就是返回的地址,注意到dirent被定義為static,大家都知道C中static聲明的變量調(diào)用一次后地址就不變了,存在靜態(tài)存儲區(qū),也就是每次readdir返回的地址都是不變的,但指向的內(nèi)容每次都被覆寫,這就是問題所在;

c. 最后解決問題,知道問題根源后,問題就比較容易解決了,就是每次給dirent重新申請內(nèi)存,看如下我的做法,注意我這里不能簡單的 struct dirent *dirent = (struct dirent *)malloc(sizeof(struct dirent) )就結(jié)束了,看前面dirent結(jié)構(gòu)體定義中char d_name[1];這里我只給d_name一個(gè)內(nèi)存空間,顯然不夠,所以也要給它申請內(nèi)存,我這里是按需申請內(nèi)存,如果定義為char d_name[256];這樣的就不需要了(一般文件名不是太長吧)。

          
            struct
          
           dirent *readdir(DIR *
          
            d)  

{  

    
          
          
            int
          
          
             i;  

    

    BOOL bf;  

    WIN32_FIND_DATA FileData;  

    
          
          
            if
          
          (!
          
            d)  

    {  

        
          
          
            return
          
          
            0
          
          
            ;  

    }  

  

    bf
          
          =FindNextFile(hFind,&
          
            FileData);  

    
          
          
            //
          
          
            fail or end  
          
          
            if
          
          (!
          
            bf)  

    {  

        
          
          
            return
          
          
            0
          
          
            ;  

    }



    
          
          
            
              struct
            
             dirent *dirent = (
            
              struct
            
             dirent *)malloc(
            
              sizeof
            
            (
            
              struct
            
             dirent)+
            
              sizeof
            
            
              (FileData.cFileName)); 
            
          
          
            for
          
          (i = 
          
            0
          
          ; i < 
          
            256
          
          ; i++
          
            )  

    {  

        dirent
          
          ->d_name[i] =
          
             FileData.cFileName[i];  

        
          
          
            if
          
          (FileData.cFileName[i] == 
          
            '
          
          
            \0
          
          
            '
          
          ) 
          
            break
          
          
            ;  

    }  

    dirent
          
          ->d_reclen =
          
             i;  

    dirent
          
          ->d_reclen =
          
             FileData.nFileSizeLow;  

  

    
          
          
            //
          
          
            check there is file or directory  
          
          
            if
          
          (FileData.dwFileAttributes &
          
             FILE_ATTRIBUTE_DIRECTORY)  

    {  

        dirent
          
          ->d_type = 
          
            2
          
          
            ;  

    }  

    
          
          
            else
          
          
              

    {  

        dirent
          
          ->d_type = 
          
            1
          
          
            ;  

    }  

      

    
          
          
            return
          
          
             dirent;

} 


          
        

?最終Windows運(yùn)行結(jié)果如下:

PS:不知道這里大家有沒有注意一個(gè)很小的細(xì)節(jié),就是輸出的不同(用的是一個(gè)相同的目錄結(jié)構(gòu)),Linux下輸出了當(dāng)前目錄.和上層目錄..而Windows下只輸出了上層目錄..,當(dāng)然這沒關(guān)系,因?yàn)槲乙闹皇窍旅娴奈募纯伞K,終于完成了,中間找bug花了不少時(shí)間,嘿嘿~~~

?

參考資料:

http://blog.csdn.net/lindabell/article/details/8181866

Linux模塊


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 肥东县| 高碑店市| 克什克腾旗| 凤山县| 西和县| 阿拉善左旗| 响水县| 塔河县| 宣城市| 卢龙县| 图木舒克市| 辽源市| 冀州市| 渭源县| 巴东县| 安平县| 乐山市| 句容市| 和田县| 苍南县| 呈贡县| 乐清市| 岳西县| 南川市| 临泽县| 津南区| 新绛县| 焦作市| 日照市| 白玉县| 六枝特区| 宜宾市| 颍上县| 永安市| 仙游县| 卓尼县| 新乡县| 邯郸县| 东山县| 华池县| 瑞安市|