程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> early_param和__setup宏

early_param和__setup宏

編輯:C++入門知識

一.宏的定義 在/include/linux/Init.h中 [cpp]  #define __setup(str, fn)        \    __setup_param(str, fn, fn, 0)     [cpp]  #define early_param(str, fn)    \   __setup_param(str, fn, fn, 1)   兩個宏都會調用__setup_param   跟蹤進__setup_param宏的定義 [cpp]   #define __setup_param(str, unique_id, fn, early)            \       static const char __setup_str_##unique_id[] __initconst \           __aligned(1) = str;                 \       static struct obs_kernel_param __setup_##unique_id      \           __used __section(.init.setup)           \           __attribute__((aligned((sizeof(long)))))        \           = { __setup_str_##unique_id, fn, early }      這個宏裡面有個結構體obs_kernel_param [cpp]  struct obs_kernel_param {       const char *str;       int (*setup_func)(char *);       int early;   };     結合上面兩個宏和一個結構體展開__setup __setup(str, fn)宏定義了 一個static const char  __setup_str_fn[]變量=str 接著定義了 一個static struct obs_kernel_param __setup_fn結構體,並賦值(標記編譯進.init.setup段) {     str;     fn(char *);      0,或1 } 二.宏的作用 1.編譯相關 在/include/asm-generic/Vmlinux.lds.h文件中定義了__setup_start.....__setup_end段 [cpp]   #define INIT_SETUP(initsetup_align)         \           . = ALIGN(initsetup_align);     \           VMLINUX_SYMBOL(__setup_start) = .;  \           *(.init.setup)          \           VMLINUX_SYMBOL(__setup_end) = .;   標記了.init.setup的函數會被編譯進該段 2.內核啟動的相關調用關系 在start_kernel中調用parse_early_param() [cpp]   void __init parse_early_param(void)   {       static __initdata int done = 0;       static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];       if (done)           return;       strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); //復制啟動命令行數據       parse_early_options(tmp_cmdline);   //調用parse_early_options函數       done = 1;   }   parse_early_options函數 [cpp]   void __init parse_early_options(char *cmdline)   {       parse_args("early options", cmdline, NULL, 0, do_early_param);   }   接著調用parse_args函數 [cpp]   int parse_args(const char *name,char *args,const struct kernel_param *params,unsigned num,int (*unknown)(char *param, char *val))   {       char *param, *val;       DEBUGP("Parsing ARGS: %s\n", args);          args = skip_spaces(args);          while (*args) { //遍歷啟動命令行           int ret;           int irq_was_disabled;              args = next_arg(args, &param, &val);    //獲取下一個參數,填充param和val參數(例如:param--console;val--tty2,115200n8)           irq_was_disabled = irqs_disabled();           ret = parse_one(param, val, params, num, unknown);  //解析一個命令行參數           if (irq_was_disabled && !irqs_disabled()) {               printk(KERN_WARNING "parse_args(): option '%s' enabled ""irq's!\n", param);           }           switch (ret) {           case -ENOENT:               printk(KERN_ERR "%s: Unknown parameter `%s'\n",name, param);               return ret;           case -ENOSPC:               printk(KERN_ERR "%s: `%s' too large for parameter `%s'\n",name, val ?: "", param);               return ret;           case 0:               break;           default:               printk(KERN_ERR"%s: `%s' invalid for parameter `%s'\n",name, val ?: "", param);               return ret;           }       }          /* All parsed OK. */       return 0;   }   命令行參數的解析parse_one [cpp]   static int parse_one(char *param,char *val,const struct kernel_param *params,unsigned num_params,int (*handle_unknown)(char *param, char *val))   {       unsigned int i;       int err;          /* Find parameter */       for (i = 0; i < num_params; i++) {   //num_params=0           if (parameq(param, params[i].name)) {               if (!val && params[i].ops->set != param_set_bool)                   return -EINVAL;               DEBUGP("They are equal!  Calling %p\n",params[i].ops->set);               mutex_lock(&param_lock);               err = params[i].ops->set(val, &params[i]);               mutex_unlock(&param_lock);               return err;           }       }          if (handle_unknown) {   //若handle_unknown函數存在           DEBUGP("Unknown argument: calling %p\n", handle_unknown);           return handle_unknown(param, val);  //則調用handle_unknown函數,參數為param,val       }          DEBUGP("Unknown argument `%s'\n", param);       return -ENOENT;   }   回溯回去handle_unknow函數就是do_early_param [cpp]   static int __init do_early_param(char *param, char *val)   {       const struct obs_kernel_param *p;          for (p = __setup_start; p < __setup_end; p++) {           if ((p->early && strcmp(param, p->str) == 0) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0)) {               if (p->setup_func(val) != 0)                   printk(KERN_WARNING"Malformed early option '%s'\n", param);           }       }       /* We accept everything at this stage. */       return 0;   }   do_early_param函數從__setup_start遍歷到__setup_end段, 判斷參數,進入if函數體裡面 if (p->setup_func(val) != 0)這句調用了對應setup_func或early_param成員的函數,並將val作為其參數,val其實便是__setup(str, fn)或__early_param中的str 其實就是調用了fn(str) 這裡的第一條if會刷選掉__setup定義的情況(除了console和earlycon參數的),因為__setup定義的obs_kernel_param結構體p->early=0 __setup定義的fn會在start_kernel->parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,&unknown_bootoption); unknown_bootoption->obsolete_checksetup函數給調用 看start_kernel中調用順序 [cpp]   parse_early_param();   parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param,&unknown_bootoption);   可見先調用__early_param定義的解析參數函數及__setup定義的(console及earlycon)的參數解析函數 接著再調用__setup定義的其他解析參數函數        

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved