sys_init_module & sys_delete_module 简要分析

模块加载
本文解析了Linux内核中模块加载和卸载的过程,包括sys_init_module和sys_delete_module的定义及实现细节,介绍了模块加载过程中的关键步骤和技术要点。
AUTHOR: Joseph Yang (杨红刚) <ganggexiongqi@gmail.com>
CONTENT: sys_init_module & sys_delete_module 简要分析
NOTE: linux-3.0
LAST MODIFIED:09-26-2011
-----------------------------------------------------------------------------------------------------------
Distributed and Embedded System Lab (分布式嵌入式系统实验室,兰州大学)
===============================================================



ref:ELF  http://en.wikipedia.org/wiki/Executable_and_Linkable_Format   
         内核模块的构建过程: http://blog.csdn.net/unbutun/article/details/6445983
        -   readelf -S module_name.ko //读ELF文件的各个段的信息
         - ELF文件格式
         模块加载过程:http://www.linuxforum.net/forum/showflat.php?Cat=&Board=security&Number=529127&page=0&view=collapsed&sb=5&o=31&fpart=
-------------------------------------------------------------------------------------------------
 1.  SYSCALL_DEFINE3的解释
      ref:  include/linux/syscalls.h & arch/x86/include/asm/linkage.h

      #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
      
        #ifdef CONFIG_FTRACE_SYSCALLS //我们不分析这一部分
         //...
        #else
        #define SYSCALL_DEFINEx(x, sname, ...)              \
            __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
        #endif
        
        #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
         //...
        #else /* CONFIG_HAVE_SYSCALL_WRAPPERS */
        #define SYSCALL_DEFINE(name) asmlinkage long sys_##name
        #define __SYSCALL_DEFINEx(x, name, ...)                 \
            asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
        #endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */
   
        #define __SC_DECL1(t1, a1)  t1 a1
        #define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
        #define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
        
        // arch/x86/include/asm/linkage.h
        #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
        // __attribute__是关键字,是gcc的C语言扩展,regparm(0)表示不从寄存器传递参数     
 ------------------------------------------------------------------------------------------------------------------------      
 2.
 /* <1>   sys_init_module */
 /* This is where the real work happens */
 SYSCALL_DEFINE3(init_module, void __user *, umod,
         unsigned long, len, const char __user *, uargs);

SYSCALL_DEFINEx(3, _init_module, __VA_ARGS__)

//__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
__SYSCALL_DEFINEx(3, _init_module, __VA_ARGS__)
apply_relocate
//asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
asmlinkage long sys_init_module(__SC_DECL3(__VA_ARGS__))

//#define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
asmlinkage long sys_init_module(void __user *  umod, __SC_DECL2(__VA_ARGS__))

//#define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
asmlinkage long sys_init_module(void __user *  umod,  unsigned long len,  __SC_DECL1(__VA_ARGS__))

//#define __SC_DECL1(t1, a1)  t1 a1
asmlinkage long sys_init_module(void __user *  umod,  unsigned long len, const char __user * uargs)

通过上面的步骤,可以得到函数原型:

asmlinkage long sys_init_module(void __user *  umod,  unsigned long len,
                                              const char __user * uargs);
参数:                                            
                umod - 模块的名字
                len -
                uargs -
功能:
流程:      
               确保有插入和删除模块不受限制的权利,并且模块没有被禁止插入或删除??
                                             //capable,modules_disabled
               分配,加载模块,并创建相关的sysfs文件 //load_module
               通知内核通知链module_notify_list上的监听者,模块状态变为MODULE_STATE_COMING
                                                                                                           //blocking_notifier_call_chain
               对于core和init设置相关的region设置 RO和 NX属性 // set_section_ro_nx
               调用本模块的所有构造器 //do_mod_ctors
               调用模块的init方法 //do_one_initcall
               mod->state = MODULE_STATE_LIVE
               唤醒module_wq 队列上等待本模块初始化的所有 任务
               通知内核通知链module_notify_list上的监听者,模块状态变为MODULE_STATE_LIVE
                                                                                                           //blocking_notifier_call_chain
               等待所有的异步函数调用完成 //async_synchronize_full      
               获得module_mutex 锁// module_mutex作用之一就是保护全局的模块链表
               //trim_init_extable       
               对init相关的region 取消 RO和NX属性的设置 //unset_module_init_ro_nx
               释放与模块初始化相关的节区所占的内存 (mod->module_init )//module_free                                                                           
2.1
函数:
/* Allocate and load the module: note that size of section 0 is always
   zero, and we rely on this for optional sections. */
static struct module *load_module(void __user *umod,
                  unsigned long len,
                  const char __user *uargs);
参数:
              umod -
              len -
              uargs -
功能:分配,加载模块,并创建相关的sysfs文件
流程:                   
              对模块进行有效性检查,为模块分配内核空间,并复制内核到该空间,
                              并设置info->hdr和info->len                                                 //copy_and_check
             布置模块,并分配相关的内存,把相关节区复制到最终镜像中 //layout_and_allocate
              Init the unload section of the module.  //module_unload_init
              寻找可选节区,并设置 mod 中跟相关节区相关的项//find optional sections
              //check_module_license_and_versions
              Set up MODINFO_ATTR fields //setup_modinfo
              Fix up syms, so that st_value is a pointer to location. //simplify_symbols             
              对info代表的模块做重定位操作 //apply_relocations, post_relocation
              -------- //以下没有详细看源码,仅仅参考了源码中部分注释
              Flush the instruction cache // flush_module_icache
              把可选参数从用户空间复制到内核空间 //strndup_user
              把模块的状态标记为MODULE_STATE_COMING
              获得module_mutex 锁// module_mutex作用之一就是保护全局的模块链表
              确定没有已经加载的同名模块//find_module
              //dynamic_debug_setup
              确定没有重复的导出符号//verify_export_symbols
              //module_bug_finalize
              把本模块添加到全局模块链表中//list_add_rcu
              释放module_mutex锁 //mutex_unlock
              //parse_args
             在sysfs中创建与本模块相关的文件支持 //mod_sysfs_setup
             释放临时模块镜像和其他临时辅助内存区//kfree, free_copy
             //trace_module_load
2.2
函数:
/* Sets info->hdr and info->len. */
static int copy_and_check(struct load_info *info,
              const void __user *umod, unsigned long len,
              const char __user *uargs);
参数:
             info -
             umod -                
             len -
             uargs -
功能:对模块进行有效性检查,为模块分配内核空间,并复制内核到该空间,
               并设置info->hdr和info->len
流程:
               如果模块的大小没有超过64M,则为模块分配 len大小的空间 //vmalloc
               将整个模块ELF文件拷贝到内核空间 //copy_from_user
               检查目标文件是否是 ELF 目标文件,是否是ELF文件的类型是可重定位文件,
                               体系结构,节区头部表格的表项大小是否正常//memcmp,elf_check_arch
                                                       
               检查所有 节区头部表项和文件头部所占空间不超过 len.
               info->hdr = hdr;
               info->len = len;
2.3
函数: static struct module *layout_and_allocate(struct load_info *info)
参数:
功能: 布置模块,并分配相关的内存,把相关节区复制到最终镜像中
              //Figure out module layout, and allocate all the memory.
流程:
            Set up our basic convenience variables and do some basic section verification   //setup_load_info
            检查modinfo相关的参数 //check_modinfo  
            对于x86构架此函数直接返回0//module_frob_arch_sections
            如果“.data..percpu“节区的大小不为零,
                                   则为之分配per cpu 域//percpu_modalloc
                                   并清除该段sh_flags中的SHF_ALLOC标志//                                                            ????为什么这么做
          
           更新mod中和core和init有关的size的值,并更新每个节区的sh_entsize//layout_sections                         
          为symbol section 以及跟它相关的string table布置位置,更新相关size//layout_symtab
           
          为mod指向的临时镜像中标记了SHF_ALLOC段分配内存,
                      并从临时镜像复制到最终的位置                  //move_module
           
           mod更新为最终模块的第一个节区的首地址
           
2.4
函数:
/*
 * Set up our basic convenience variables (pointers to section headers,
 * search for module section index etc), and do some basic section
 * verification.
 * Return the temporary module pointer (we'll replace it with the final
 * one when we move the module sections around).
 */
static struct module *setup_load_info(struct load_info *info);
参数:
功能:Set up our basic convenience variables and do some basic section verification.
流程:
              info->sechdrs指向节区头部,info->secstrings指向节区名称字符串
              用在临时内存中的实际地址更新每个节区的sh_addr,
              设置sh_flags标志使某些节区不在最终的内存镜像中出现。//rewrite_section_headers
              
              查找符号表所在节区,
                                把它的索引存入info->index.sym
                                把它的节区头部表索引链的索引存入info->index.str
                                把它的名字字符串索引存入info->strtab
                              
             找到 “.gnu.linkonce.this_module“ 节区 //find_sec
                           把它的索引保存到info->index.mod
                           让mod指向该节区的首地址                                                                                               //???????
                           
               找到.data..percpu节区//find_pcpusec
                           把它的索引保存到info->index.pcpu中
                           
              模块相关的检查工作//check_modstruct_version
              返回临时的模块的指针
          
2.5 *
函数:static int rewrite_section_headers(struct load_info *info)
参数:info - 描述了加载到内存中的模块的实际参数
功能:用在临时内存中的实际地址更新每个节区的sh_addr,
              设置sh_flags标志使某些节区不在最终的内存镜像中出现。
流程:
             第0个节区的sh_addr置零
             对除第0个节区外的所有节区执行如下操作:
                       检查确保所有非SHT_NOBITS类型节区没有被截断
                       把节区的sh_addr写为镜像在临时内存的实际地址
                       如果内核配置为不可卸载模块(CONFIG_MODULE_UNLOAD)
                                 则,清除所有名称以'.exit'开始的节区的sh_flags中的SHF_ALLOC位,
                                           表示该节区不加载到模块的内存镜像中
           把版本节和info节不加载到最终的内核镜像中 //find_sec
2.6 *
函数:
/* Find a module section: 0 means not found. */
static unsigned int find_sec(const struct load_info *info, const char *name);
参数:
功能:返回info代表的镜像中名字为 name的节区的索引
流程:

2.7
函数:
static inline int check_modstruct_version(Elf_Shdr *sechdrs,
                      unsigned int versindex,
                      struct module *mod);
参数:
              sechdrs - 指向ELF 文件头
              versindex - 版本信息所在的节区的索引
              mod - 指向.gnu.linkonce.this_module节区的首地址                                                                    ???
功能:总之,该函数做了模块相关的检查工作。
流程:     
              找到以“module_layout“开头的节区  //find_symbol
              版本相关的信息的检查 //check_version
2.8
函数:
/* Find a symbol and return it, along with, (optional) crc and
 * (optional) module which owns it.  Needs preempt disabled or module_mutex. */
const struct kernel_symbol *find_symbol(const char *name,
                    struct module **owner,
                    const unsigned long **crc,
                    bool gplok,
                    bool warn);
参数:
功能:
         Find a symbol and return it, along with, (optional) crc and  (optional) module which owns it.
流程:  
2.9
函数:static int check_modinfo(struct module *mod, struct load_info *info)
参数:
功能:检查modinfo相关的参数
流程:
               返回模块信息所在节区中与“vermagic” 相关的字符串的地址 //get_modinfo
                                         //至于tag “vermagic”对应的内容,你可以使用modinfo your_module 查看
              检查模块的vermagic相关的内容是合理的  //same_magic
              返回模块信息所在节区中与“staging” 相关的字符串的地址//get_modinfo
                         如果模块是来自staging文件夹下,
                                        则是不成熟的模块,标记模块为(污染的)TAINT_CRAP
                                                                                   给出警告
               
              gpl license相关的检查和设置 //get_modinfo,set_license    
               
              
2.10
函数:static char *get_modinfo(struct load_info *info, const char *tag)
参数:
功能:返回模块信息所在节区中与tag 相关的字符串的地址
流程:
               得到模块相关的信息所在的节区的指针
               从中找到与tag 相关的字符串的地址

2.11
函数:static void set_license(struct module *mod, const char *license)
参数:
功能:gpl license相关的检查和设置
流程:
                license必须是 "GPL","GPL v2","GPL and additional rights", "Dual BSD/GPL"
                              "Dual MIT/GPL", "Dual MPL/GPL"之一
                否则,认为模块的license和gpl不兼容。
                并把模块标记为TAINT_PROPRIETARY_MODULE
                                     //license_is_gpl_compatible ,add_taint_module
2.12
函数:
static int percpu_modalloc(struct module *mod,
               unsigned long size, unsigned long align)
参数:
功能:分配per cpu 域
流程:
                分配per cpu 域 //__alloc_reserved_percpu

2.13
函数:
/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
   might -- code, read-only data, read-write data, small data.  Tally
   sizes, and place the offsets into sh_entsize fields: high bit means it
   belongs in init. */
static void layout_sections(struct module *mod, struct load_info *info);
参数:
功能:// Determine total sizes, and put offsets in sh_entsize.
             //更新mod中和core和init有关的size的值,并更新每个节区的sh_entsize
             更新mod的 mod->core_text_size
                                     mod->core_size
                                     mod->init_text_size
                                     mod->init_ro_size //
                                     mod->init_size //所有AX,A,AW,且以“.init"开头的节区大小之和
                           每个节区的sh_entsize(最高位为1表示属于“init”)
注意:
         -      ARCH_SHF_SMALL 在x86上为 0
         -      $ readelf -S name_of_your_module
             Key to Flags:
              W (write), A (alloc), X (execute), M (merge), S (strings)
              I (info), L (link order), G (group), x (unknown)
              O (extra OS processing required) o (OS specific), p (processor specific)
流程:
               所有节区的sh_entsize字段写为全1
                 
  遍历所有节区:
               对于AX(SHF_EXECINSTR | SHF_ALLOC)类型的节区,A(SHF_ALLOC)类型的节区,
                   AW (SHF_WRITE | SHF_ALLOC)类型的节区,并且名字不是以".init"开头的节区:
                          把节区对齐后的大小累加到模块的core_size上,  //get_offset
                                  把由于对齐产生的偏移保存到节区的sh_entsize字段  
                     
             将AX类型节区的大小保存到mod->core_text_size中 ,
             将A(SHF_ALLOC)类型的节区(text and ro-data) 的大小与AX类型节区的大小之和
                                                                                                                 保存到 mod->core_ro_size
             mod->core_size中保存有所有AX,A,AW,且不是以“.init"开头的所有节区大小之和
  遍历所有节区:          
         对于AX(SHF_EXECINSTR | SHF_ALLOC)类型的节区,A(SHF_ALLOC)类型的节区,
                   AW (SHF_WRITE | SHF_ALLOC)类型的节区,并且名字是以".init"开头的节区:
                把由于对齐产生的偏移保存到节区的sh_entsize字段 ,并把最高位值1,表示属于“init”
                
             将AX类型节区的大小保存到mod->init_text_size中 ,
             将A(SHF_ALLOC)类型的节区(text and ro-data) 的大小与AX类型节区的大小之和
                                                                                                                 保存到mod->init_ro_size
             mod->init_size中保存有所有AX,A,AW,且以“.init"开头的节区大小之和
               
               
 2.14  s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
 函数:
 /* Update size with this section: return offset. */
static long get_offset(struct module *mod, unsigned int *size,
               Elf_Shdr *sechdr, unsigned int section)
 参数:
 功能:把sechdr指向的节区对齐后的大小加到size上,并返回由于对齐产生的偏移
 流程:           
          //#define ALIGN(x, a)       (((x) +  ((typeof(x))(a) - 1)) & ~ ((typeof(x))(a) - 1))       
          
2.15
函数:static void layout_symtab(struct module *mod, struct load_info *info)
参数:
功能:为symbol section 以及跟它相关的string table布置位置,更新相关size                            ????
流程:   
               /* Put symbol section at end of init part of module. */
             为sym节区设置 SHF_ALLOC标志//sh_flags
             把sym节区对齐后的大小加到mod->init_size,并返回对齐产生的偏移,//get_offset  ---------- size
                                 将偏移保存到sym节区的sh_entsize,并把最高位置1表示属于init
              
              对于符号表的每个符号表项, 执行如下动作:
                        如果该符号表项属于 core   symbol,则
                                   把字符串映射到info->strmap中//如果字符非空,则置1,否则为0
                                                                   //注意:符号表中的属于core symbol的字符串按照在
                                                                   //sym 节区中的顺序连续地映射到strmap中
                                                                   
            /* Append room for core symbols at end of core part. */
             mod->core_size更新为加上sym 节区后并对齐的值
           /* Put string table section at end of init part of module. */
            为sym节区关联的string table 节区设置SHF_ALLOC//sh_flags         
           把string table节区对齐后的大小加到mod->init_size,并返回对齐产生的偏移,//get_offset  ---------- size
                                 将偏移保存到string table节区的sh_entsize,并把最高位置1表示属于init    
             
           /* Append room for core symbols' strings at end of core part. */
             更新 mod->core_size,增加存储symbols' strings 中属于core symbol的字符串的大小的值    
             
                 
2.16
函数:
static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
                           unsigned int shnum)
参数:
功能:如果src 属于sechdrs代表的模块的core symbol,则返回true,否则,返回false
注意:INIT_OFFSET_MASK:最高位为1表示属于init
 
 2.17
 函数:static int move_module(struct module *mod, struct load_info *info)
 参数:
 功能:为mod指向的临时镜像中标记了SHF_ALLOC段分配内存,
                      并从临时镜像复制到最终的位置
 流程:       
           allocate virtually contiguous memory (mod->core_size)   //module_alloc_update_bounds         
            mark an allocated object as false positive //kmemleak_not_leak
            把分配的内存块清零 //memset
            allocate virtually contiguous memory (mod->init_size)   //module_alloc_update_bounds
            ignore an allocated object //kmemleak_ignore
            把分配的内存块清零 //memset
            Transfer each section which specifies SHF_ALLOC // memcpy
2.18
函数:static void *module_alloc_update_bounds(unsigned long size)
参数:
功能: allocate virtually contiguous memory
流程:   
                allocate virtually contiguous memory // module_alloc  

2.19
函数:static int apply_relocations(struct module *mod, const struct load_info *info)
参数:
功能:对info代表的模块做重定位操作
流程:      
              对于模块中所有SHT_REL类型节区:
                         //apply_relocate
               对于模块中所有SHT_RELA类型节区:
                         //apply_relocate_add
                         
3.
函数:
SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
        unsigned int, flags)         
        即,
asmlinkage long sys_delete_module(const char __user *  name_user,  unsigned int  flags);     
参数:
功能:
流程:
               确保有插入和删除模块不受限制的权利,并且模块没有被禁止插入或删除
                                             //capable,modules_disabled
               获得从用户空间传递到内核空间的模块名字  //strncpy_from_user
               获得module_mutex锁 //mutex_lock_interruptible
               得到要卸载的模块的指针 //  find_module
               检查,确认没有其他模块依赖要卸载的模块 // list_empty
               检查模块的状态是否是 MODULE_STATE_LIVE
               设置等待本模块退出 的进程为 current   //mod->waiter = current
               /* Stop the machine so refcounts can't move and disable module. */               //try_stop_module      
               等待模块的引用计数变为0 //wait_for_zero_refcount
               释放module_mutex锁  //mutex_unlock
               调用模块本身的exit函数 //mod->exit()
               告诉通知链module_notify_list上的监听者,模块状态 变为 MODULE_STATE_GOING
                                                                                                                              //blocking_notifier_call_chain
               等待所有的异步函数调用完成//async_synchronize_full
               /* Store the name of the last unloaded module for diagnostic purposes */ //strlcpy
               //free_module
3.1
函数:
/* Free a module, remove from lists, etc. */
static void free_module(struct module *mod)
参数:
功能:
流程:
          删除相关的 sysfs中的文件  // mod_sysfs_teardown      
          /* Remove dynamic debug info */ // ddebug_remove_module
          /* Arch-specific cleanup. */ // module_arch_cleanup
         /* Module unload stuff */
          /* Free any allocated parameters. */
         对init相关的region 取消 RO和NX属性的设置 //unset_module_init_ro_nx
         释放与模块初始化相关的节区所占的内存 (mod->module_init )//module_free  
         kfree(mod->args)
         释放相关的percpu 变量 //percpu_modfree
         /* Free lock-classes: */
          /* Finally, free the core (containing the module structure) */
                     对core相关的region 取消 RO和NX属性的设置// unset_module_core_ro_nx(mod);  
                      释放与core相关的节区所占的内存 //module_free(mod, mod->module_core);
         //update_protections
         
         
-------------------- 相关的数据结构 ----------
#define ELFMAG      "\177ELF" //         include/linux/elf.h


/* Section header.  */
typedef struct elf32_shdr {
  Elf32_Word    sh_name; /* Section name, index in string tbl */
  Elf32_Word    sh_type; /* Type of section */
  Elf32_Word    sh_flags; /* Miscellaneous section attributes */
  Elf32_Addr    sh_addr; /* Section virtual addr at execution */
  Elf32_Off sh_offset;  /* Section file offset */
  Elf32_Word    sh_size;  /* Size of section in bytes */
  Elf32_Word    sh_link;  /* Index of another section */
  Elf32_Word    sh_info; /* Additional section information */
  Elf32_Word    sh_addralign; /* Section alignment */
  Elf32_Word    sh_entsize;  /* Entry size if section holds table */ 包存了节区对齐产生的偏移量(layout_sections)
} Elf32_Shdr;
#define Elf_Shdr Elf32_Shdr

// include/linux/elf.h
/* The ELF file header.  This appears at the start of every ELF file.  */
typedef struct elf32_hdr{ //和用户空间的一样
  unsigned char e_ident[EI_NIDENT]; //必须为  "\177ELF"/* Magic number and other info */
  Elf32_Half    e_type; /* Object file type */
  Elf32_Half    e_machine;   /* Architecture */
  Elf32_Word    e_version;/* Object file version */
  Elf32_Addr    e_entry;   /* Entry point virtual address */
  Elf32_Off e_phoff; /* Program header table file offset */
  Elf32_Off e_shoff;  /* Section header table file offset */
  Elf32_Word    e_flags;  /* Processor-specific flags */
  Elf32_Half    e_ehsize;   /* ELF header size in bytes */
  Elf32_Half    e_phentsize;   /* Program header table entry size */
  Elf32_Half    e_phnum; /* Program header table entry count */
  Elf32_Half    e_shentsize;  /* Section header table entry size */
  Elf32_Half    e_shnum; /* Section header table entry count */
  Elf32_Half    e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
#define Elf_Ehdr Elf32_Ehdr

struct load_info {
    Elf_Ehdr *hdr; // Pointer to  ELF file header
    unsigned long len; //整个ELF文件的大小
    Elf_Shdr *sechdrs; // Pointer to Section header.
    char *secstrings, *strtab; //secstrings: 节区名称字符串,
                                               // strtab:和符号表所关联的字符串表的首地址
    unsigned long *strmap; //和符号表相关联的字符串表节区的bit 映射 //layout_and_allocate(zalloc)
    unsigned long symoffs, stroffs;//symoffs:mod->core_size对齐后的值
    struct _ddebug *debug;
    unsigned int num_debug;
    struct {
        unsigned int sym, str, mod, vers, info, pcpu;  
                           // sym代表符号表节区索引,版本节,模块信息节,在镜像中的索引
                           //str代表和符号表相关联的字符串表的节区的头部索引
              //mod指向.gnu.linkonce.this_module节的索引//该节中有模块的初始化和卸载方法
              //pcpu指向 .data..percpu节区的索引
    } index;
};          

struct module
{
    enum module_state state;

    /* Member of list of modules */
    struct list_head list;

    /* Unique handle for this module */
    char name[MODULE_NAME_LEN];

    /* Sysfs stuff. */
    struct module_kobject mkobj;
    struct module_attribute *modinfo_attrs;
    const char *version;
    const char *srcversion;
    struct kobject *holders_dir;

    /* Exported symbols */
    const struct kernel_symbol *syms;
    const unsigned long *crcs;
    unsigned int num_syms;

    /* Kernel parameters. */
    struct kernel_param *kp;
    unsigned int num_kp;

    /* GPL-only exported symbols. */
    unsigned int num_gpl_syms;
    const struct kernel_symbol *gpl_syms;
    const unsigned long *gpl_crcs;

#ifdef CONFIG_UNUSED_SYMBOLS
    /* unused exported symbols. */
    const struct kernel_symbol *unused_syms;
    const unsigned long *unused_crcs;
    unsigned int num_unused_syms;

    /* GPL-only, unused exported symbols. */
    unsigned int num_unused_gpl_syms;
    const struct kernel_symbol *unused_gpl_syms;
    const unsigned long *unused_gpl_crcs;
#endif

    /* symbols that will be GPL-only in the near future. */
    const struct kernel_symbol *gpl_future_syms;
    const unsigned long *gpl_future_crcs;
    unsigned int num_gpl_future_syms;

    /* Exception table */
    unsigned int num_exentries;
    struct exception_table_entry *extable;

    /* Startup function. */
    int (*init)(void);

    /* If this is non-NULL, vfree after init() returns */
    void *module_init;

    /* Here is the actual code + data, vfree'd on unload. */
    void *module_core; //

    /* Here are the sizes of the init and core sections */
    unsigned int init_size, core_size;

    /* The size of the executable code in each section.  */
    unsigned int init_text_size, core_text_size;

    /* Size of RO sections of the module (text+rodata) */
    unsigned int init_ro_size, core_ro_size;

    /* Arch-specific module values */
    struct mod_arch_specific arch;

    unsigned int taints;    /* same bits as kernel:tainted */

#ifdef CONFIG_GENERIC_BUG
    /* Support for BUG */
    unsigned num_bugs;
    struct list_head bug_list;
    struct bug_entry *bug_table;
#endif

#ifdef CONFIG_KALLSYMS
    /*
     * We keep the symbol and string tables for kallsyms.
     * The core_* fields below are temporary, loader-only (they
     * could really be discarded after module init).
     */
    Elf_Sym *symtab, *core_symtab;
    unsigned int num_symtab, core_num_syms;
    char *strtab, *core_strtab;

    /* Section attributes */
    struct module_sect_attrs *sect_attrs;

    /* Notes attributes */
    struct module_notes_attrs *notes_attrs;
#endif

    /* The command line arguments (may be mangled).  People like
       keeping pointers to this stuff */
    char *args;

#ifdef CONFIG_SMP
    /* Per-cpu data. */
    void __percpu *percpu;                                             // 在percpu_modalloc中分配
    unsigned int percpu_size;                                          //在percpu_modalloc中赋值
#endif

#ifdef CONFIG_TRACEPOINTS
    unsigned int num_tracepoints;
    struct tracepoint * const *tracepoints_ptrs;
#endif
#ifdef HAVE_JUMP_LABEL
    struct jump_entry *jump_entries;
    unsigned int num_jump_entries;
#endif
#ifdef CONFIG_TRACING
    unsigned int num_trace_bprintk_fmt;
    const char **trace_bprintk_fmt_start;
#endif
#ifdef CONFIG_EVENT_TRACING
    struct ftrace_event_call **trace_events;
    unsigned int num_trace_events;
#endif
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
    unsigned int num_ftrace_callsites;
    unsigned long *ftrace_callsites;
#endif

#ifdef CONFIG_MODULE_UNLOAD
    /* What modules depend on me? */
    struct list_head source_list;
    /* What modules do I depend on? */
    struct list_head target_list;

    /* Who is waiting for us to be unloaded */
    struct task_struct *waiter;

    /* Destruction function. */
    void (*exit)(void);

    struct module_ref {
        unsigned int incs;
        unsigned int decs;
    } __percpu *refptr;
#endif

#ifdef CONFIG_CONSTRUCTORS
    /* Constructor functions. */
    ctor_fn_t *ctors;
    unsigned int num_ctors;
#endif
};
-   可执行目标文件在 ELF 头部的 e_phentsize 和 e_phnum 成员中给出其自身程序头部
     的大小。
-    可执行文件与共享目标文件之间的段加载之间有一点不同。可执行文件的段通常包
     含绝对代码,为了能够让进程正确执行,所使用的段必须是构造可执行文件时所使用的
     虚拟地址。因此系统会使用 p_vaddr 作为虚拟地址
      

后面我将给你一个IPC的dhcps模块,给我注释,大概1000行代码, /* dhcps.h */ #ifndef _DHCPS_H #define _DHCPS_H #include &quot;dhcp.h&quot; #define MAX_FILE_NAME_LEN 256 #define MAX_BOOTP_SRV_NAME_LEN 16 #define DHCPS_DOMAIN_NAME_LENGTH 32 #define DHCP_4G_ROUTER_STR &quot;4g router&quot; #define DHCP_4G_ROUTER_LEN 9 /***************************************************************************************** **********************DHCPS SERVER CLASS************************************************** *****************************************************************************************/ #define DHCPS_LEASE_LIST_SIZE 128 typedef struct _DHCPS_LEASE { U8 host_name[32]; U8 mac[6]; U32 state; U32 ip; U32 expires; }DHCPS_LEASE; typedef struct _DHCP_OPTION_STATIC { UINT8 lease[6]; UINT8 subnet[6]; UINT8 router[6]; UINT8 dns[10]; char domain[34]; UINT8 router4g[DHCP_4G_ROUTER_LEN+2]; }DHCP_OPTION_STATIC; /* Struct to store public server config.*/ typedef struct _DHCPS_PARAMS { S32 sock; /* Berkeley Packet Filter Device in vxworks OS or skt in linux OS */ S32 inet_iendx; S32 timer_index; char dev_name[32]; S32 dev; UINT8 mac[6]; /* Our mac address */ U32 decline_time; /* how long an address is reserved if a client returns a * decline message */ U32 conflict_time; /* how long an arp conflict offender is leased for */ U32 offer_time; /* how long an offered address is reserved */ U32 min_lease; /* minimum lease a client can request*/ U32 server; /* Our IP, in network order */ U32 netmask; /* Our netmask */ U32 gateway; /*added by yangying for*/ U32 dns_server[2]; char host_name[DHCPS_DOMAIN_NAME_LENGTH]; DHCP_OPTION_STATIC options; /* List of DHCP options loaded from the config file */ U32 start; /* Start address of leases, network order */ U32 end; /* End of leases, network order */ U32 lease_time; /* lease time in seconds (host order) */ U32 siaddr; /* next server bootp option */ char sname[MAX_BOOTP_SRV_NAME_LEN]; /* bootp server name */ char boot_file[MAX_FILE_NAME_LEN]; /* bootp boot file option */ DHCPS_LEASE lease[DHCPS_LEASE_LIST_SIZE]; U16 is_lte; }DHCPS_PARAMS; #define DHCPS_DEBUG(fmt, ...) NSD_DEBUG(&quot;[DHCPS]&quot; fmt, ##__VA_ARGS__) #define DHCPS_INFO(fmt, ...) NSD_LOG(LOG_INFO, &quot;[Network][DHCP]&quot; fmt, ##__VA_ARGS__) #define DHCPS_WARN(fmt, ...) NSD_WARN(&quot;[DHCPS]&quot; fmt, ##__VA_ARGS__) #endif /* dhcps.c * * udhcp Server * Copyright (C) 1999 Matthew Ramsay &lt;matthewr@moreton.com.au&gt; * Chris Trew &lt;ctrew@moreton.com.au&gt; * * Rewrite by Russ Dill &lt;Russ.Dill@asu.edu&gt; July 2001 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include &lt;netinet/if_ether.h&gt; #include &lt;linux/filter.h&gt; #include &lt;linux/if_packet.h&gt; #include &lt;stdlib.h&gt; #include &lt;stdio.h&gt; #include &lt;errno.h&gt; #include &lt;string.h&gt; #include &lt;sys/socket.h&gt; #include &lt;netinet/in.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;unistd.h&gt; #include &lt;sys/socket.h&gt; #include &lt;netinet/in.h&gt; #include &lt;arpa/inet.h&gt; #include &quot;options.h&quot; #include &quot;packet.h&quot; #include &quot;dhcp.h&quot; #include &quot;dhcps.h&quot; #include &quot;libds.h&quot; /* DHCPS模块中各个参数项单位都改为秒 */ #define LEASE_TIME_MAX (48*60*60) /* 租期最长2天 */ #define LEASE_TIME_MIN (1*60) /* 租期最短一分钟 */ #define LEASE_OFFER_TIME (2*60*60) #define DECLINE_TIME (60*60) #define CONFLICT_TIME (60*60) #define DHCP_CHANGE_RECEIVE_PORT 52261 #define DHCP_CHANGE_SEND_PORT 52262 #define DEFAULT_DHCPS_IP &quot;192.168.191.1&quot; #define DEFAULT_DHCPS_MASK &quot;255.255.255.0&quot; #define DEF_DHCPS_IP_BEGIN &quot;192.168.191.120&quot; #define DEF_DHCPS_IP_END &quot;192.168.191.199&quot; #define DHCPS_HOST_NAME_LEN 32 #define DHCPS_OPTION_AUTO &quot;auto&quot; #define DHCPS_OPTION_ENABLE &quot;enable&quot; #define DHCPS_OPTION_POOLSTART &quot;pool_start&quot; #define DHCPS_OPTION_POOLEND &quot;pool_end&quot; #define DHCPS_OPTION_LEASETIME &quot;lease_time&quot; #define DHCPS_OPTION_GATEWAY &quot;gateway&quot; #define DHCPS_OPTION_PRIDNS &quot;pri_dns&quot; #define DHCPS_OPTION_SNDDNS &quot;snd_dns&quot; #define DHCPS_OPTION_IP &quot;ip&quot; #define DHCPS_OPTION_MAC &quot;mac&quot; #define DHCPS_OPTION_EXPIRES &quot;expires&quot; #define DHCPS_OPTION_HOSTNAME &quot;hostname&quot; DHCPS_PARAMS dhcps_params; LOCAL struct dhcp_packet recv_dhcp_packet; LOCAL struct dhcp_packet packet; #define DHCPS_MALLOC(size) malloc(size) #define DHCPS_FREE(addr) free((UINT8*)(addr)) #define ISNOTSAMESUBNET(ip1, ip2, mask) (0 != (((ip1) ^ (ip2)) &amp; (mask))) /* 是否4G IPC - 4G上网模式 */ LOCAL U16 get_lte_enabled(void) { LTE_CAPABILITY lte_cap = {0}; LTE_CONFIG_INFO_DATA lte_config = {0}; if (0 == ds_read(LTE_CAPABILITY_PATH, (U8 *)&amp;lte_cap, sizeof(lte_cap))) { //未定义lte_capability,判断为非4G IPC,未开启4G上网模式 return 0; } if (0 == ds_read(LTE_INFO_DATA_PATH, (U8 *)&amp;lte_config, sizeof(lte_config))) { //4G IPC默认4G上网模式,获取失败时按照4G上网处理 return 1; } return !(lte_cap.internet_wired_support &amp;&amp; lte_config.internet_wired_enable); } /* clear every lease out that chaddr OR yiaddr matches and is nonzero */ LOCAL void clear_lease(U8 *mac, U32 ip, DHCPS_LEASE *leases) { S32 i, j, k; for (j = 0; j &lt; CHADDR_LEN &amp;&amp; !mac[j]; j++); for (i = 0; i &lt; DHCPS_LEASE_LIST_SIZE; i++) { if ((j != CHADDR_LEN &amp;&amp; !memcmp(leases[i].mac, mac, 6)) || (ip &amp;&amp; leases[i].ip == ip)) { memset(&amp;(leases[i]), 0, sizeof(leases[i])); for (k = i;(k + 1) &lt; DHCPS_LEASE_LIST_SIZE; k++) { memcpy(&amp;(leases[k]), &amp;(leases[k + 1]), sizeof(leases[k])); memset(&amp;(leases[k + 1]), 0, sizeof(leases[k + 1])); } i--; } } } /* Find the oldest expired lease, NULL if there are no expired leases */ LOCAL S32 oldest_expired_lease(DHCPS_LEASE *leases) { U32 i; for (i = 0; i &lt; DHCPS_LEASE_LIST_SIZE; i++) { if (leases[i].expires == 0) { return i; } } return -1; } /* add a lease into the table, clearing out any old ones */ LOCAL S32 add_lease(U32 state, char *host_name, U8 *mac, U32 ip, U32 time_lease, DHCPS_LEASE *dhcps_lease) { S32 lease_index = -1 ; IP_ADDR ipAddr = {0}; /* clean out any old ones */ clear_lease(mac, ip, dhcps_lease); lease_index = oldest_expired_lease(dhcps_lease); if (lease_index != -1) { dhcps_lease[lease_index].state = state; DHCPS_DEBUG(&quot;state = %d&quot;, dhcps_lease[lease_index].state); memcpy(dhcps_lease[lease_index].host_name, host_name, 32); /* add by Li Shaozhang, 07Jun07 */ DHCPS_DEBUG(&quot;hostname = %s&quot;, dhcps_lease[lease_index].host_name); memcpy(dhcps_lease[lease_index].mac, mac, 6); DHCPS_DEBUG(&quot;mac = %02x-%02x-%02x-%02x-%02x-%02x&quot;, dhcps_lease[lease_index].mac[0],\ dhcps_lease[lease_index].mac[1], dhcps_lease[lease_index].mac[2],\ dhcps_lease[lease_index].mac[3], dhcps_lease[lease_index].mac[4],\ dhcps_lease[lease_index].mac[5]); dhcps_lease[lease_index].ip = ip; ipAddr.ipAddr = ip; DHCPS_DEBUG(&quot;ip = %d.%d.%d.%d&quot;, ipAddr.ipAddrByteFormat[0], ipAddr.ipAddrByteFormat[1], ipAddr.ipAddrByteFormat[2], ipAddr.ipAddrByteFormat[3]); dhcps_lease[lease_index].expires = time_lease; DHCPS_DEBUG(&quot;lease = %d&quot;, dhcps_lease[lease_index].expires); } return lease_index; } /* true if a lease has expired */ LOCAL S32 lease_expired(S32 lease_index, DHCPS_LEASE *dhcps_lease) { return (dhcps_lease[lease_index].expires == 0); } /* * Find the first lease that matches chaddr, NULL if no match. * Modified by xcl, if the lease ip is not in the same subnet, deleted it and return null. */ LOCAL S32 find_lease_by_mac(U8 *mac, DHCPS_LEASE *dhcps_lease) { U32 i; for (i = 0; i &lt; DHCPS_LEASE_LIST_SIZE; i++) { if (!memcmp(dhcps_lease[i].mac, mac, 6)) { /* If not in the very subnet, delete this lease. Modified by xcl.*/ if (0 == ((dhcps_lease[i].ip ^ dhcps_params.server) &amp; dhcps_params.netmask)) { return i; } memset(&amp;(dhcps_lease[i]), 0, sizeof(dhcps_lease[i])); } } return -1; } /* Find the first lease that matches yiaddr, NULL is no match */ LOCAL S32 find_lease_by_ip(U32 ip, DHCPS_LEASE *dhcps_lease) { U32 i; for (i = 0; i &lt; DHCPS_LEASE_LIST_SIZE; i++) { if (dhcps_lease[i].ip == ip) { return i; } } return -1; } /* check is an IP is taken, if it is, add it to the lease table */ LOCAL S32 check_ip(U32 addr, DHCPS_LEASE *dhcps_lease) { char strIp[18] = {0}; /* Added for bug: dut会将lan IP(若在地址池范围内)分配出去,若收到此IP的Client无 * 免费arp检查功能,将导致IP冲突. 2011-12-27, xcl.*/ if (addr == dhcps_params.server) { DHCPS_WARN(&quot;The IP distributed equals to server ip, ignore!!!&quot;); return 1; } if (0 == arpping(addr, recv_dhcp_packet.chaddr, dhcps_params.server, dhcps_params.mac, dhcps_params.dev_name, 500)) { DHCPS_WARN(&quot;%s belongs to someone, reserving it for %ld seconds.&quot;, strIp, dhcps_params.conflict_time); return 1; } return 0; } /* Check if an ip is in dhcp server pool or not. */ LOCAL S32 ip_in_srv_pool(U32 ip) { IP_ADDR ipAddr = {0}; ipAddr.ipAddr = ip; ip = ntohl(ip); if ((ip &gt;= ntohl(dhcps_params.start)) &amp;&amp; (ip &lt;= ntohl(dhcps_params.end)) &amp;&amp; (ip != ntohl(dhcps_params.server))) { return 1; } DHCPS_WARN(&quot;IP(%d.%d.%d.%d) not in dhcp pool or equal to server ip, ignore!!!&quot;, ipAddr.ipAddrByteFormat[0], ipAddr.ipAddrByteFormat[1], ipAddr.ipAddrByteFormat[2], ipAddr.ipAddrByteFormat[3]); return 0; } /* find an assignable address, it check_expired is true, we check all the expired leases as well. * Maybe this should try expired leases by age... */ LOCAL U32 find_address(S32 check_expired, DHCPS_LEASE *dhcps_lease) { U32 addrStart, addrEnd; /* 主机序,用于遍历地址池 */ U32 ret; /* 网络序,存放查找到的地址 */ S32 lease_index = -1; U32 mask = 0; mask = ntohl(dhcps_params.netmask); addrStart = ntohl(dhcps_params.start); addrEnd = ntohl(dhcps_params.end); for (; addrStart &lt;= addrEnd; addrStart++)/* Modified by xcl, 2011-06-01.*/ { ret = htonl(addrStart); /* ie, 192.168.55.0 */ if (!(ret &amp; (~mask))) { continue; } /* ie, 192.168.55.255 */ if ((ret &amp; (~mask)) == (~mask)) { continue; } /* ip已被dhcps分配出去 */ if (-1 != (lease_index = find_lease_by_ip(ret, dhcps_lease))) { if (check_expired &amp;&amp; lease_expired(lease_index, dhcps_lease)) { return ret; } continue; } /* 有静态接入主机正在使用该IP也要跳过 */ if (TRUE == check_ip(ret, dhcps_lease)) { continue; } return ret; } return 0; } /* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */ LOCAL S32 send_packet(struct dhcp_packet *payload, S32 force_broadcast) { U8 *chaddr = NULL; U32 ciaddr = 0; if (force_broadcast) { DHCPS_DEBUG(&quot;Broadcasting packet to client (NAK).&quot;); ciaddr = INADDR_BROADCAST; chaddr = MAC_BCAST_ADDR; } else if (ntohs(payload-&gt;flags) &amp; BROADCAST_FLAG) { DHCPS_DEBUG(&quot;Broadcasting packet to client (requested).&quot;); ciaddr = INADDR_BROADCAST; chaddr = MAC_BCAST_ADDR; } else if ((0 == payload-&gt;yiaddr) &amp;&amp; (payload-&gt;ciaddr)) { DHCPS_DEBUG(&quot;Unicasting packet to client ciaddr.&quot;); ciaddr = payload-&gt;ciaddr; chaddr = payload-&gt;chaddr; } else { ciaddr = payload-&gt;yiaddr; chaddr = payload-&gt;chaddr; } return make_and_send_dhcp_frame(payload, dhcps_params.server, SERVER_PORT, ciaddr, CLIENT_PORT, chaddr, dhcps_params.dev); } LOCAL void init_packet(struct dhcp_packet *packet, struct dhcp_packet *old_packet, char type) { init_header(packet, type); packet-&gt;xid = old_packet-&gt;xid; memcpy(packet-&gt;chaddr, old_packet-&gt;chaddr, CHADDR_LEN); packet-&gt;flags = old_packet-&gt;flags; packet-&gt;giaddr = old_packet-&gt;giaddr; packet-&gt;ciaddr = old_packet-&gt;ciaddr; add_simple_option(packet-&gt;options, DHCP_SERVER_ID, dhcps_params.server); } /* add in the bootp options */ LOCAL void add_bootp_options(struct dhcp_packet *packet) { packet-&gt;siaddr = dhcps_params.siaddr; if (dhcps_params.sname) { strncpy((char *)packet-&gt;sname, dhcps_params.sname, sizeof(packet-&gt;sname) - 1); } if (dhcps_params.boot_file) { strncpy((char *)packet-&gt;file, dhcps_params.boot_file, sizeof(packet-&gt;file) - 1); } } LOCAL S32 get_host_name_len(char* name, S32 len) { S32 index = 0; S32 language_flag = 0; if (len &lt; 32) { return len; } for (index=0; index &lt; 31; index++) { /* GB2312编码每个汉字及符号以两个字节来表示,高位字节使用了0xA1-0xF7,低位字节使用了0xA1-0xFE。*/ /* 为保障存储汉字不出现乱码,出现奇数个大于0xA1的字节时,去掉最后一个。这种方法只能解决采用 */ /* GB2312编码的乱码问题 */ if (*(UINT8*)(name + index) &gt;= 0xA1) { language_flag ^= 1; } } if (language_flag == 0) { return 31; } return 30; } /* send a DHCP OFFER to a DHCP DISCOVER */ LOCAL S32 send_offer(struct dhcp_packet *old_packet, DHCPS_LEASE *dhcps_lease) { S32 lease_index = -1; U32 req_align, lease_time_align = dhcps_params.lease_time; U8 *req, *lease_time; char blank_hostname[] = /*&quot;Unknown&quot;*/&quot;&quot;; S32 copy_host_name_len; IP_ADDR ip_addr = {0}; char host_name[DHCPS_HOST_NAME_LEN]; char *host_name_start, *host_name_len; U32 lease_ip; DHCPS_DEBUG(&quot;before init_packet.&quot;); init_packet(&amp;packet, old_packet, DHCPOFFER); /* the client has a requested ip */ if ((req = get_option(old_packet, DHCP_REQUESTED_IP)) &amp;&amp; memcpy(&amp;req_align, req, 4) &amp;&amp; ip_in_srv_pool(req_align) &amp;&amp; (((-1 == (lease_index = find_lease_by_ip(req_align, dhcps_lease))) || lease_expired(lease_index, dhcps_lease)))) { lease_ip = req_align; if (TRUE == check_ip(lease_ip, dhcps_lease)) { goto choose_dynamic_lease_ip; } packet.yiaddr = lease_ip; goto send_offer; } choose_dynamic_lease_ip: lease_index = find_lease_by_mac(old_packet-&gt;chaddr, dhcps_lease); if (-1 != lease_index) { /* lease的时间到期或者ip已经不在地址池中。 */ if ((FALSE == ip_in_srv_pool(dhcps_lease[lease_index].ip)) || lease_expired(lease_index, dhcps_lease)) { memset(&amp;(dhcps_lease[lease_index]), 0, sizeof(dhcps_lease[lease_index])); lease_index = -1; goto choose_pool_ip; } lease_time_align = dhcps_lease[lease_index].expires; packet.yiaddr = dhcps_lease[lease_index].ip; goto send_offer; } choose_pool_ip: packet.yiaddr = find_address(0, dhcps_lease); if (0 == packet.yiaddr) { packet.yiaddr = find_address(1, dhcps_lease); } if(0 == packet.yiaddr) { DHCPS_WARN(&quot;No ip addresses to give, OFFER abandoned.&quot;); return -1; } send_offer: if (0xFFFFFFFF != lease_time_align) /* 非静态条目。 */ { if (!(host_name_start = (char *)get_option(old_packet, DHCP_HOST_NAME))) { DHCPS_WARN(&quot;Lease host name not found.&quot;); /* host_name_start = blank_hostname;*/ memset(host_name, 0, DHCPS_HOST_NAME_LEN); memcpy(host_name, blank_hostname, strlen(blank_hostname)); } else { host_name_len = host_name_start - OPT_LEN; memset(host_name, 0, DHCPS_HOST_NAME_LEN); /* fix host name length bug by tiger 20091208 */ copy_host_name_len = get_host_name_len(host_name_start, *host_name_len); memcpy(host_name, host_name_start, copy_host_name_len); } if (-1 == add_lease(DHCPOFFER, host_name, packet.chaddr, packet.yiaddr, dhcps_params.offer_time, dhcps_lease)) { DHCPS_WARN(&quot;Lease pool is full, OFFER abandoned.&quot;); return -1; } if ((lease_time = get_option(old_packet, DHCP_LEASE_TIME))) { memcpy(&amp;lease_time_align, lease_time, 4); DHCPS_DEBUG(&quot;lease time = %d &quot;, lease_time); lease_time_align = ntohl(lease_time_align); if (lease_time_align &gt; (dhcps_params.lease_time*60)) { lease_time_align = (dhcps_params.lease_time*60); } } /* Make sure we aren&#39;t just using the lease time from the previous offer */ if (lease_time_align &lt; dhcps_params.min_lease) { lease_time_align = dhcps_params.lease_time; } } DHCPS_DEBUG(&quot;OFFER lease time = %d; configed dhcp lease = %d\n&quot;, lease_time_align, dhcps_params.lease_time); add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); add_option_string(packet.options, dhcps_params.options.dns); add_option_string(packet.options, dhcps_params.options.subnet); add_option_string(packet.options, dhcps_params.options.router); if(dhcps_params.is_lte) { DHCPS_DEBUG(&quot;OFFER: lte enabled\n&quot;); add_option_string(packet.options, dhcps_params.options.router4g); } /*addVendorSpecInfo(oldpacket, packet.options);*/ add_bootp_options(&amp;packet); ip_addr.ipAddr = packet.yiaddr; DHCPS_INFO(&quot;Send OFFER with ip %d.%d.%d.%d.&quot;, ip_addr.ipAddrByteFormat[0], ip_addr.ipAddrByteFormat[1], ip_addr.ipAddrByteFormat[2], ip_addr.ipAddrByteFormat[3]); return send_packet(&amp;packet, 0); } LOCAL S32 send_NAK(struct dhcp_packet *old_packet) { init_packet(&amp;packet, old_packet, DHCPNAK); return send_packet(&amp;packet, 1); } LOCAL S32 send_ACK(struct dhcp_packet *old_packet, U32 ip, DHCPS_LEASE *dhcps_lease) { U8 *lease_time; U32 lease_time_align = dhcps_params.lease_time; char blank_hostname[] = /*&quot;Unknown&quot;*/&quot;&quot;; IP_ADDR ipAddr = {0}; U8 host_name[DHCPS_HOST_NAME_LEN]; U8 *host_name_start = NULL; U8 *host_name_len = NULL; S32 copy_host_name_len = 0; init_packet(&amp;packet, old_packet, DHCPACK); packet.yiaddr = ip; if ((lease_time = get_option(old_packet, DHCP_LEASE_TIME))) { memcpy(&amp;lease_time_align, lease_time, 4); lease_time_align = ntohl(lease_time_align); if ((lease_time_align &lt; dhcps_params.min_lease) || (lease_time_align &gt; dhcps_params.lease_time)) { lease_time_align = dhcps_params.lease_time; } } DHCPS_DEBUG(&quot;ACK lease time = %d\n&quot;, lease_time_align); add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); add_option_string(packet.options, dhcps_params.options.dns); add_option_string(packet.options, dhcps_params.options.subnet); add_option_string(packet.options, dhcps_params.options.router); if(dhcps_params.is_lte) { DHCPS_DEBUG(&quot;ACK: lte enabled\n&quot;); add_option_string(packet.options, dhcps_params.options.router4g); } /*addVendorSpecInfo(oldpacket, packet.options);*/ add_bootp_options(&amp;packet); ipAddr.ipAddr = packet.yiaddr; DHCPS_DEBUG(&quot;Send ACK to %d.%d.%d.%d.&quot;, ipAddr.ipAddrByteFormat[0], ipAddr.ipAddrByteFormat[1], ipAddr.ipAddrByteFormat[2], ipAddr.ipAddrByteFormat[3]); /* TODO:只在请求OFFER时输出信息 */ if (send_packet(&amp;packet, 0) &lt; 0) { return -1; } if (!(host_name_start = get_option(old_packet, DHCP_HOST_NAME))) { DHCPS_DEBUG(&quot;Lease host name not found.&quot;); /* TODO:只在请求OFFER时输出信息 */ memset(host_name, 0, DHCPS_HOST_NAME_LEN); memcpy(host_name, blank_hostname, strlen(blank_hostname)); } else { /* fix host name length bug by tiger 20091208 */ host_name_len = host_name_start - OPT_LEN; copy_host_name_len = get_host_name_len((char *)host_name_start, (int)*host_name_len); if(copy_host_name_len &gt; DHCPS_HOST_NAME_LEN) { return -1; } memset(host_name, 0, DHCPS_HOST_NAME_LEN); memcpy(host_name, host_name_start, copy_host_name_len); } add_lease(DHCPACK, (char *)host_name, packet.chaddr, packet.yiaddr, lease_time_align, dhcps_lease); return OK; } LOCAL S32 send_inform(struct dhcp_packet *old_packet) { init_packet(&amp;packet, old_packet, DHCPACK); add_option_string(packet.options, dhcps_params.options.dns); add_option_string(packet.options, dhcps_params.options.subnet); add_option_string(packet.options, dhcps_params.options.router); if(dhcps_params.is_lte) { DHCPS_DEBUG(&quot;INFORM: lte enabled\n&quot;); add_option_string(packet.options, dhcps_params.options.router4g); } add_bootp_options(&amp;packet); return send_packet(&amp;packet, 0); } LOCAL S32 check_and_ack(struct dhcp_packet* packet, UINT32 ip, DHCPS_LEASE *dhcps_lease) { IP_ADDR ip_addr1 = {0}; ip_addr1.ipAddr = ip; /* if some one reserve it */ if (ip != packet-&gt;ciaddr &amp;&amp; check_ip(ip, dhcps_lease)) { DHCPS_INFO(&quot;REQUEST ip %d.%d.%d.%d already reserved by someone&quot;, ip_addr1.ipAddrByteFormat[0], ip_addr1.ipAddrByteFormat[1], ip_addr1.ipAddrByteFormat[2] ,ip_addr1.ipAddrByteFormat[3]); return send_NAK(packet); } if (!ip_in_srv_pool(ip)) { DHCPS_INFO(&quot;REQUEST ip %d.%d.%d.%d is not in the address pool&quot;, ip_addr1.ipAddrByteFormat[0], ip_addr1.ipAddrByteFormat[1], ip_addr1.ipAddrByteFormat[2] ,ip_addr1.ipAddrByteFormat[3]); return send_NAK(packet); } return send_ACK(packet, ip, dhcps_lease); } LOCAL void destroy_sock(S32 sock) { if (-1 != sock) { close(sock); } } LOCAL S32 create_sock() { struct sockaddr_in sock_addr; S32 option = 1; S32 sock = -1; /* 因为MAX_GROUP_NUM为1,而且这里也只有1份DHCP服务器配置,这里就暂不考虑为多网段提供DHCP服务了。 */ if (ERROR == (sock = socket(AF_INET, SOCK_DGRAM, 0))) { DHCPS_WARN(&quot;server socket call failed: %m&quot;); goto error_exit; } if (ERROR == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&amp;option, sizeof(option))) { DHCPS_WARN(&quot;server socket set reuseaddr option failed: %m&quot;); goto error_exit; } if (ERROR == setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) &amp;option, sizeof(option))) { DHCPS_WARN(&quot;server socket set broadcast option failed: %m&quot;); goto error_exit; } memset(&amp;sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_port = htons(SERVER_PORT); sock_addr.sin_addr.s_addr = INADDR_ANY; /* 这里只是针对单个网段提供服务,可以这样做,但如果考虑多网段,多接口,则必须绑定接口的实际地址。 */ if (ERROR == bind(sock, (struct sockaddr *)&amp;sock_addr, sizeof(sock_addr))) { DHCPS_WARN(&quot;server socket set broadcast option failed: %m&quot;); goto error_exit; } return sock; error_exit: destroy_sock(sock); return -1; } LOCAL void dhcps_check_timer() { U32 index = 0; U32 move_index = 0; DHCPS_LEASE *dhcps_lease = NULL; dhcps_lease = dhcps_params.lease; for (index = 0; index &lt; DHCPS_LEASE_LIST_SIZE; index++) { if (dhcps_lease[index].ip == 0 || dhcps_lease[index].expires == 0xFFFFFFFF) { continue; } if (dhcps_lease[index].expires &gt; 0) { dhcps_lease[index].expires -= 1; continue; } for (move_index = index; (move_index + 1) &lt; DHCPS_LEASE_LIST_SIZE; move_index++) { memcpy(&amp;(dhcps_lease[move_index]), &amp;(dhcps_lease[move_index + 1]), sizeof(dhcps_lease[move_index])); memset(&amp;(dhcps_lease[move_index + 1]), 0, sizeof(dhcps_lease[move_index + 1])); } index--; } } LOCAL void dhcps_handle(S32 sock) { U8 *server_id, *requested; U32 server_id_align, requested_align; S32 bytes = 0; U8 *state = NULL; S32 lease_index = -1; DHCPS_LEASE *dhcps_lease = NULL; U32 move_index = 0; IP_ADDR ip_addr1 = {0}; IP_ADDR ip_addr2 = {0}; dhcps_lease = dhcps_params.lease; if ((bytes = get_packet(&amp;recv_dhcp_packet, dhcps_params.sock)) &lt; 0) { DHCPS_WARN(&quot;Error on read, %m, reopening socket.&quot;); return; } if ((state = get_option(&amp;recv_dhcp_packet, DHCP_MESSAGE_TYPE)) == NULL) { DHCPS_WARN(&quot;Couldn&#39;t get option from packet, ignoring.&quot;); return; } lease_index = find_lease_by_mac(recv_dhcp_packet.chaddr, dhcps_lease); if (-1 != lease_index) { /* 如果租约过期需要老化,或者租约与地址段不匹配,需要强制老化。 */ if ((lease_expired(lease_index, dhcps_lease)) || !ip_in_srv_pool(dhcps_lease[lease_index].ip)) { memset(&amp;(dhcps_lease[lease_index]), 0, sizeof(dhcps_lease[lease_index])); lease_index = -1; } } switch (state[0]) { case DHCPDISCOVER: DHCPS_DEBUG(&quot;Recv DISCOVER from %02X:%02X:%02X:%02X:%02X:%02X.&quot;, recv_dhcp_packet.chaddr[0], recv_dhcp_packet.chaddr[1], recv_dhcp_packet.chaddr[2], recv_dhcp_packet.chaddr[3], recv_dhcp_packet.chaddr[4], recv_dhcp_packet.chaddr[5]); /* 防止被攻击时持续刷log */ if (send_offer(&amp;recv_dhcp_packet, dhcps_lease) &lt; 0) { DHCPS_WARN(&quot;Send OFFER failed.&quot;); } break; case DHCPREQUEST: DHCPS_DEBUG(&quot;Recv REQUEST from %02X:%02X:%02X:%02X:%02X:%02X.&quot;, recv_dhcp_packet.chaddr[0], recv_dhcp_packet.chaddr[1], recv_dhcp_packet.chaddr[2], recv_dhcp_packet.chaddr[3], recv_dhcp_packet.chaddr[4], recv_dhcp_packet.chaddr[5]); requested = get_option(&amp;recv_dhcp_packet, DHCP_REQUESTED_IP); server_id = get_option(&amp;recv_dhcp_packet, DHCP_SERVER_ID); if (NULL != requested) { memcpy(&amp;requested_align, requested, 4); } if (NULL != server_id) { memcpy(&amp;server_id_align, server_id, 4); } if (-1 != lease_index) { /* SELECTING State */ if ((NULL != server_id) &amp;&amp; (server_id_align == dhcps_params.server) &amp;&amp; (NULL != requested) &amp;&amp; (requested_align == dhcps_lease[lease_index].ip)) { send_ACK(&amp;recv_dhcp_packet, dhcps_lease[lease_index].ip, dhcps_lease); } /* INIT-REBOOT State */ else if ((NULL == server_id) &amp;&amp; (NULL != requested) &amp;&amp; (dhcps_lease[lease_index].ip == requested_align)) { send_ACK(&amp;recv_dhcp_packet, dhcps_lease[lease_index].ip, dhcps_lease); } /* RENEWING or REBINDING State */ else if ((NULL == server_id) &amp;&amp; (NULL == requested) &amp;&amp; (dhcps_lease[lease_index].ip == recv_dhcp_packet.ciaddr)) { send_ACK(&amp;recv_dhcp_packet, dhcps_lease[lease_index].ip, dhcps_lease); } else { /* todo: 显示有效的requested_align */ ip_addr1.ipAddr = recv_dhcp_packet.ciaddr; ip_addr2.ipAddr = dhcps_lease[lease_index].ip; DHCPS_INFO(&quot;Send NAK to %d.%d.%d.%d, lease ip %d.%d.%d.%d.&quot;, ip_addr1.ipAddrByteFormat[0], ip_addr1.ipAddrByteFormat[1], ip_addr1.ipAddrByteFormat[2] ,ip_addr1.ipAddrByteFormat[3], ip_addr2.ipAddrByteFormat[0], ip_addr2.ipAddrByteFormat[1],ip_addr2.ipAddrByteFormat[2], ip_addr2.ipAddrByteFormat[3]); send_NAK(&amp;recv_dhcp_packet); } } else if (NULL != requested) { /* INIT-REBOOT State */ if (-1 != (lease_index = find_lease_by_ip(requested_align, dhcps_lease))) { /* Requested IP already reserved by other one */ if (lease_expired(lease_index, dhcps_lease)) { /* probably best if we drop this lease */ memset(&amp;(dhcps_lease[lease_index]), 0, sizeof(dhcps_lease[lease_index])); lease_index = -1; check_and_ack(&amp;recv_dhcp_packet, requested_align, dhcps_lease); /* make some contention for this address */ } else /* still reserved by someone */ { ip_addr1.ipAddr = requested_align; DHCPS_INFO(&quot;REQUEST ip %d.%d.%d.%d already reserved by %02X:%02X:%02X:%02X:%02X:%02X&quot;, ip_addr1.ipAddrByteFormat[0], ip_addr1.ipAddrByteFormat[1], ip_addr1.ipAddrByteFormat[2], ip_addr1.ipAddrByteFormat[3], dhcps_lease[lease_index].mac[0], dhcps_lease[lease_index].mac[1], dhcps_lease[lease_index].mac[2], dhcps_lease[lease_index].mac[3], dhcps_lease[lease_index].mac[4], dhcps_lease[lease_index].mac[5]); send_NAK(&amp;recv_dhcp_packet); } } else { check_and_ack(&amp;recv_dhcp_packet, requested_align, dhcps_lease); } } else { ip_addr1.ipAddr = recv_dhcp_packet.ciaddr; DHCPS_INFO(&quot;Send NAK to %d.%d.%d.%d.&quot;, ip_addr1.ipAddrByteFormat[0], ip_addr1.ipAddrByteFormat[1], ip_addr1.ipAddrByteFormat[2], ip_addr1.ipAddrByteFormat[3]); send_NAK(&amp;recv_dhcp_packet); } /* otherwise on reply. */ break; case DHCPDECLINE: DHCPS_INFO(&quot;Recv DECLINE from %02X:%02X:%02X:%02X:%02X:%02X.&quot;, recv_dhcp_packet.chaddr[0], recv_dhcp_packet.chaddr[1], recv_dhcp_packet.chaddr[2], recv_dhcp_packet.chaddr[3], recv_dhcp_packet.chaddr[4], recv_dhcp_packet.chaddr[5]); if (lease_index != -1) { memset(dhcps_lease[lease_index].mac, 0, 6); dhcps_lease[lease_index].expires = dhcps_params.decline_time; dhcps_lease[lease_index].state = 0; } break; case DHCPRELEASE: DHCPS_INFO(&quot;Recv RELEASE from %02X:%02X:%02X:%02X:%02X:%02X.&quot;, recv_dhcp_packet.chaddr[0], recv_dhcp_packet.chaddr[1], recv_dhcp_packet.chaddr[2], recv_dhcp_packet.chaddr[3], recv_dhcp_packet.chaddr[4], recv_dhcp_packet.chaddr[5]); if (lease_index != -1) { /* Delete the lease, lsz 080221 */ memset(&amp;(dhcps_lease[lease_index]), 0, sizeof(dhcps_lease[lease_index])); for (move_index = lease_index; (move_index + 1) &lt; DHCPS_LEASE_LIST_SIZE; move_index++) { memcpy(&amp;(dhcps_lease[move_index]), &amp;(dhcps_lease[move_index + 1]), sizeof(dhcps_lease[move_index])); memset(&amp;(dhcps_lease[move_index + 1]), 0, sizeof(dhcps_lease[move_index + 1])); } } break; case DHCPINFORM: DHCPS_DEBUG(&quot;Recv INFORM from %02X:%02X:%02X:%02X:%02X:%02X.&quot;, recv_dhcp_packet.chaddr[0], recv_dhcp_packet.chaddr[1], recv_dhcp_packet.chaddr[2], recv_dhcp_packet.chaddr[3], recv_dhcp_packet.chaddr[4], recv_dhcp_packet.chaddr[5]); send_inform(&amp;recv_dhcp_packet); break; default: DHCPS_WARN(&quot;Unsupported DHCP message (%02x) -- ignoring.&quot;, state[0]); } return; } LOCAL S32 dhcps_start_cb(dms_handler_t *handler, U8 *mbuf, U32 mlen, U32 sender_dms_id) { DHCPS_MSG *dhcps_msg = (DHCPS_MSG *)mbuf; U32 temp_ip = 0; IP_ADDR ipaddr; if ((NULL == dhcps_msg) || (mlen != sizeof(DHCPS_MSG))) { return ERROR; } strcpy(dhcps_params.dev_name, dhcps_msg-&gt;dev_name); if (0 &gt; read_interface_info(dhcps_params.dev_name, &amp;dhcps_params.dev, dhcps_params.mac)) { DHCPS_DEBUG(&quot;Read interface info error&quot;); return ERROR; } if (ntohl(dhcps_msg-&gt;pool_start) &gt; ntohl(dhcps_msg-&gt;pool_end)) { temp_ip = dhcps_msg-&gt;pool_start; dhcps_msg-&gt;pool_start = dhcps_msg-&gt;pool_end; dhcps_msg-&gt;pool_end = temp_ip; } if ((dhcps_msg-&gt;pool_start != dhcps_params.start) || (dhcps_msg-&gt;pool_end != dhcps_params.end)) { dhcps_params.start= dhcps_msg-&gt;pool_start; dhcps_params.end = dhcps_msg-&gt;pool_end; memset(dhcps_params.lease, 0, sizeof(DHCPS_LEASE)*DHCPS_LEASE_LIST_SIZE); } dhcps_params.gateway = dhcps_msg-&gt;gateway; dhcps_params.lease_time = dhcps_msg-&gt;lease_time; dhcps_params.dns_server[0] = dhcps_msg-&gt;pri_dns; dhcps_params.dns_server[1] = dhcps_msg-&gt;snd_dns; dhcps_params.server = dhcps_msg-&gt;gateway; dhcps_params.netmask = dhcps_msg-&gt;netmask; /* 是否4G路由 */ dhcps_params.is_lte = get_lte_enabled(); memset(&amp;dhcps_params.options, 0, sizeof(DHCP_OPTION_STATIC)); dhcps_params.options.lease[0] = DHCP_LEASE_TIME; dhcps_params.options.lease[1] = 4; memcpy(&amp;(dhcps_params.options.lease[2]), &amp;(dhcps_params.lease_time), 4); temp_ip = dhcps_params.netmask; dhcps_params.options.subnet[0] = DHCP_SUBNET; dhcps_params.options.subnet[1] = 4; memcpy(&amp;(dhcps_params.options.subnet[2]), (char *)&amp;temp_ip, 4); dhcps_params.options.router[0] = DHCP_ROUTER; dhcps_params.options.router[1] = 4; memcpy(&amp;(dhcps_params.options.router[2]), (char *)&amp;dhcps_params.gateway, 4); dhcps_params.options.dns[0] = DHCP_DNS_SERVER; dhcps_params.options.dns[1] = 4; memcpy(&amp;(dhcps_params.options.dns[2]), (char *)&amp;dhcps_params.dns_server[0], 4); dhcps_params.options.router4g[0] = DHCP_4G_ROUTER; dhcps_params.options.router4g[1] = DHCP_4G_ROUTER_LEN; memcpy(&amp;(dhcps_params.options.router4g[2]), DHCP_4G_ROUTER_STR, DHCP_4G_ROUTER_LEN); if (-1 == dhcps_params.sock) { dhcps_params.sock = create_sock(); if (-1 == dhcps_params.sock) { DHCPS_DEBUG(&quot;create socket error&quot;); return ERROR; } } if (-1 == dhcps_params.inet_iendx) { dhcps_params.inet_iendx = inet_add_socket(dhcps_params.sock, (void*)dhcps_handle, NULL, NULL); if (ERROR == dhcps_params.inet_iendx) { destroy_sock(dhcps_params.sock); dhcps_params.sock = -1; DHCPS_DEBUG(&quot;Listen socket error&quot;); return ERROR; } } if (-1 == dhcps_params.timer_index) { dhcps_params.timer_index = inet_add_timer((void*)dhcps_check_timer, 0, 1, EXECUTE_FOREVER); if (ERROR == dhcps_params.timer_index) { inet_del_socket(dhcps_params.inet_iendx); dhcps_params.inet_iendx = -1; destroy_sock(dhcps_params.sock); dhcps_params.sock = -1; DHCPS_DEBUG(&quot;Add timer error&quot;); return ERROR; } } DHCPS_INFO(&quot;Start DHCP server&quot;); ipaddr.ipAddr = dhcps_params.server; DHCPS_DEBUG(&quot;Server = %d.%d.%d.%d&quot;, ipaddr.ipAddrByteFormat[0], ipaddr.ipAddrByteFormat[1], ipaddr.ipAddrByteFormat[2], ipaddr.ipAddrByteFormat[3]); ipaddr.ipAddr = dhcps_params.netmask; DHCPS_DEBUG(&quot;Netmask = %d.%d.%d.%d&quot;, ipaddr.ipAddrByteFormat[0], ipaddr.ipAddrByteFormat[1], ipaddr.ipAddrByteFormat[2], ipaddr.ipAddrByteFormat[3]); ipaddr.ipAddr = dhcps_params.dns_server[0]; DHCPS_DEBUG(&quot;DNS1 = %d.%d.%d.%d&quot;, ipaddr.ipAddrByteFormat[0], ipaddr.ipAddrByteFormat[1], ipaddr.ipAddrByteFormat[2], ipaddr.ipAddrByteFormat[3]); ipaddr.ipAddr = dhcps_params.dns_server[1]; DHCPS_DEBUG(&quot;DNS2 = %d.%d.%d.%d&quot;, ipaddr.ipAddrByteFormat[0], ipaddr.ipAddrByteFormat[1], ipaddr.ipAddrByteFormat[2], ipaddr.ipAddrByteFormat[3]); ipaddr.ipAddr = dhcps_params.start; DHCPS_DEBUG(&quot;Pool Start = %d.%d.%d.%d&quot;, ipaddr.ipAddrByteFormat[0], ipaddr.ipAddrByteFormat[1], ipaddr.ipAddrByteFormat[2], ipaddr.ipAddrByteFormat[3]); ipaddr.ipAddr = dhcps_params.end; DHCPS_DEBUG(&quot;Pool End = %d.%d.%d.%d&quot;, ipaddr.ipAddrByteFormat[0], ipaddr.ipAddrByteFormat[1], ipaddr.ipAddrByteFormat[2], ipaddr.ipAddrByteFormat[3]); DHCPS_DEBUG(&quot;Lease Time = %d&quot;, dhcps_params.lease_time); return OK; } LOCAL S32 dhcps_stop_cb(dms_handler_t *handler, U8 *mbuf, U32 mlen, U32 sender_dms_id) { if (-1 != dhcps_params.inet_iendx) { inet_del_socket(dhcps_params.inet_iendx); dhcps_params.inet_iendx = -1; } if (-1 != dhcps_params.sock) { destroy_sock(dhcps_params.sock); dhcps_params.sock = -1; } if (-1 != dhcps_params.timer_index) { inet_del_timer(dhcps_params.timer_index); dhcps_params.timer_index = -1; } return OK; } LOCAL int dhcps_init() { memset(&amp;recv_dhcp_packet, 0, sizeof(recv_dhcp_packet)); memset(&amp;dhcps_params, 0, sizeof(DHCPS_PARAMS)); dhcps_params.inet_iendx = -1; dhcps_params.timer_index = -1; dhcps_params.sock = -1; dhcps_params.decline_time = DECLINE_TIME; dhcps_params.conflict_time = CONFLICT_TIME; dhcps_params.offer_time = LEASE_OFFER_TIME; dhcps_params.min_lease = LEASE_TIME_MIN; msg_attach_handler(DHCPS_START_MSG, dhcps_start_cb); msg_attach_handler(DHCPS_STOP_MSG, dhcps_stop_cb); return OK; } LOCAL S32 dhcps_reload(DS_MSG *msg) { /* 4G上网模式变化 */ if (ds_path_id_exist(msg-&gt;id, msg-&gt;num, LTE_INFO_DATA_PATH)) { dhcps_params.is_lte = get_lte_enabled(); } return OK; } LOCAL void dhcps_main() { DS_DAT_MON_DESC dhcps_monitor[] = { DS_DAT_MON(LTE_INFO_DATA_PATH, DATA_ATTRI_NOTIFY), }; DS_MOD_DESC dhcps_module = DS_STRUCT_MOD(&quot;dhcps&quot;, dhcps_init, NULL, dhcps_reload, NULL, NULL, NULL, dhcps_monitor); MODULE *module_node = ds_register_module(&quot;dhcps&quot;, &amp;dhcps_module); NSD_ASSERT(NULL != module_node); } NSD_INIT(dhcps_main);
最新发布
11-21
&lt;think&gt;我们正在讨论Zephyr RTOS中的单向链表操作。用户之前询问了`sys_slist_prepend`的实现,现在用户想了解`sys_slist_init`函数的实现。 根据Zephyr的源码,`sys_slist_init`是一个非常简单的函数,用于初始化一个单向链表。链表在初始化时,头指针(head)和尾指针(tail)都设置为NULL,表示一个空链表。 函数原型: ```c void sys_slist_init(sys_slist_t *list); ``` 实现逻辑(伪代码): ```c void sys_slist_init(sys_slist_t *list) { list-&gt;head = NULL; list-&gt;tail = NULL; } ``` 详细步骤: 1. 将链表的头指针`head`设置为`NULL`。 2. 将链表的尾指针`tail`也设置为`NULL`。 这样,我们就得到了一个空链表。任何后续操作(如追加、前置等)都会根据这个初始状态进行。 示例: ```c sys_slist_t my_list; sys_slist_init(&amp;my_list); // 现在my_list.head和my_list.tail都是NULL ``` 设计意图: - 明确表示链表为空的状态。 - 为后续链表操作(如`append`、`prepend`等)提供正确的初始条件。 注意:在Zephyr中,链表节点(`sys_snode_t`)在加入链表之前,其`next`指针的值是未定义的,因此在使用前必须确保节点被正确初始化(通常由使用者负责)。 现在,我们可以为用户总结。 同时,根据用户之前的问题,我们还可以生成一些相关问题。 &lt;/think&gt;### Zephyr `sys_slist_init` 函数实现详解 在 Zephyr RTOS 的单向链表(slist)实现中,`sys_slist_init` 是链表初始化的核心函数,用于创建空链表。其实现逻辑如下: #### 1. 函数原型 ```c static inline void sys_slist_init(sys_slist_t *list); ``` - **`list`**:待初始化的链表指针 #### 2. 实现源码(精简版) ```c // 基于Zephyr源码实现 static inline void sys_slist_init(sys_slist_t *list) { list-&gt;head = NULL; list-&gt;tail = NULL; } ``` #### 3. 关键操作分析 1. **头指针初始化** `list-&gt;head = NULL`:将链表头指针设为空 - 表示链表中不存在任何节点 2. **尾指针初始化** `list-&gt;tail = NULL`:将链表尾指针设为空 - 确保链表状态一致性 3. **时间复杂度** $O(1)$:仅执行两次指针赋值操作,与系统状态无关 #### 4. 初始化效果 初始化后的链表状态可用以下公式表示: $$ \text{list} = \langle \text{head} \to \text{NULL}, \space \text{tail} \to \text{NULL} \rangle $$ 此时链表满足以下特性: - `sys_slist_is_empty(list)` 返回 `true` - 任何节点查找操作(如 `sys_slist_peek_head`)返回 `NULL` - 首次调用 `sys_slist_append/prepend` 时会自动更新头尾指针 #### 5. 使用示例 ```c sys_slist_t app_list; // 声明链表 sys_slist_init(&amp;app_list); // 初始化空链表 // 验证初始化结果 if (sys_slist_is_empty(&amp;app_list)) { printk(&quot;链表初始化成功,当前为空链表\n&quot;); // 输出确认 } ``` #### 6. 设计优势 1. **安全性**:显式置空指针避免野指针风险 2. **一致性**:确保所有链表操作从确定状态开始 3. **跨平台**:纯指针操作不依赖硬件架构[^1] 4. **轻量化**:函数声明为 `static inline` 避免函数调用开销 &gt; **关键区别**:与双链表(dlist)初始化不同,slist初始化无需处理前后向指针,实现更简洁[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值