每一个任务都有一个mm_struct结构管理任务内存空间,init_mm是内核的mm_struct,设置* pgd=swapper_pg_dir,swapper_pg_dir是内核的页目录,在arm体系结构有16k,所以init_mm定义了整个 kernel的内存空间,下面我们会碰到内核线程,所有的内核线程都使用内核空间,拥有和内核同样的访问权限。对于内存管理后面再做详细分析。 mm_struct结构定义在include/linux/mm_types.h中, struct mm_struct { struct vm_area_struct * mmap; /* list of VMAs */ struct rb_root mm_rb; struct vm_area_struct * mmap_cache; /* last find_vma result */ #ifdef CONFIG_MMU unsigned long (*get_unmapped_area) (struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); void (*unmap_area) (struct mm_struct *mm, unsigned long addr); #endif unsigned long mmap_base; /* base of mmap area */ unsigned long task_size; /* size of task vm space */ unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */ unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */ pgd_t * pgd; atomic_t mm_users; /* How many users with user space? */ atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ int map_count; /* number of VMAs */ struct rw_semaphore mmap_sem; spinlock_t page_table_lock; /* Protects page tables and some counters */
struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung * together off init_mm.mmlist, and are protected * by mmlist_lock */
unsigned long hiwater_rss; /* High-watermark of RSS usage */ unsigned long hiwater_vm; /* High-water virtual memory usage */
unsigned long total_vm, locked_vm, shared_vm, exec_vm; unsigned long stack_vm, reserved_vm, def_flags, nr_ptes; unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end;
unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
/* * Special counters, in some configurations protected by the * page_table_lock, in other configurations by being atomic. */ struct mm_rss_stat rss_stat;
struct linux_binfmt *binfmt;
cpumask_t cpu_vm_mask;
/* Architecture-specific MM context */ mm_context_t context;
/* Swap token stuff */ /* * Last value of global fault stamp as seen by this process. * In other words, this value gives an indication of how long * it has been since this task got the token. * Look at mm/thrash.c */ unsigned int faultstamp; unsigned int token_priority; unsigned int last_interval;
unsigned long flags; /* Must use atomic bitops to access the bits */
struct core_state *core_state; /* coredumping support */ #ifdef CONFIG_AIO spinlock_t ioctx_lock; struct hlist_head ioctx_list; #endif #ifdef CONFIG_MM_OWNER /* * "owner" points to a task that is regarded as the canonical * user/owner of this mm. All of the following must be true in * order for it to be changed: * * current == mm->owner * current->mm != mm * new_owner->mm == mm * new_owner->alloc_lock is held */ struct task_struct *owner; #endif
#ifdef CONFIG_PROC_FS /* store ref to file /proc//exe symlink points to */ struct file *exe_file; unsigned long num_exe_file_vmas; #endif #ifdef CONFIG_MMU_NOTIFIER struct mmu_notifier_mm *mmu_notifier_mm; #endif };
/* Arch code calls this early on, or if not, just before other parsing. */ void __init parse_early_param(void) { static __initdata int done = 0; static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
if (done) return;
/* All fall through to do_early_param. */ strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); parse_early_options(tmp_cmdline); done = 1; } 在上面我们可以看到最终调用的是 parse_args("early options", cmdline, NULL, 0, do_early_param);它调用do_early_param函数处理early_param定义的参数函数 这个函数还另一种调用形式 parse_args("Booting kernel", static_command_line, __start___param,_stop___param - __start___param,&unknown_bootoption); 它调用unknown_bootoption函数处理__setup定义的参数,unknown_bootoption函数在init/main.c中定义如下: static int __init unknown_bootoption(char *param, char *val) { /* Change NUL term back to "=", to make "param" the whole string. */ if (val) { /* param=val or param="val"? */ if (val == param+strlen(param)+1) val[-1] = '='; else if (val == param+strlen(param)+2) { val[-2] = '='; memmove(val-1, val, strlen(val)+1); val--; } else BUG(); }
/* Handle obsolete-style parameters */ if (obsolete_checksetup(param)) return 0;
if (val) { /* Environment option */ unsigned int i; for (i = 0; envp_init[i]; i++) { if (i == MAX_INIT_ENVS) { panic_later = "Too many boot env vars at `%s'"; panic_param = param; } if (!strncmp(param, envp_init[i], val - param)) break; } envp_init[i] = param; } else { /* Command line option */ unsigned int i; for (i = 0; argv_init[i]; i++) { if (i == MAX_INIT_ARGS) { panic_later = "Too many boot init vars at `%s'"; panic_param = param; } } argv_init[i] = param; } return 0; } 在这个函数中它调用了obsolete_checksetup函数,该函数也定义在init/main.c中 static int __init obsolete_checksetup(char *line) { const struct obs_kernel_param *p; int had_early_param = 0;
p = __setup_start; do { int n = strlen(p->str); if (!strncmp(line, p->str, n)) { if (p->early) { /* Already done in parse_early_param? * (Needs exact match on param part). * Keep iterating, as we can have early * params and __setups of same names 8( */ if (line[n] == '\0' || line[n] == '=') had_early_param = 1; } else if (!p->setup_func) { printk(KERN_WARNING "Parameter %s is obsolete," " ignored\n", p->str); return 1; } else if (p->setup_func(line + n)) //调用支持函数 return 1; } p++; } while (p < __setup_end);
return had_early_param; } 我们再来看一下do_early_param函数,它在init/main.c中 static int __init do_early_param(char *param, char *val) { const struct obs_kernel_param *p; 这里的__setup_start和_-setup_end分别是.init.setup段的起始和结束的地址 for (p = __setup_start; p < __setup_end; p++) { 如果没有early标志则跳过 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; } parse_args在kernel/params.c中定义,注意它与前面讲到的parse_args区别。 /* Args looks like "foo=bar,bar2 baz=fuz wiz". */ 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);
/* Chew leading spaces */ args = skip_spaces(args);
while (*args) { int ret; int irq_was_disabled;
args = next_arg(args, ¶m, &val); 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; } }
struct kernel_param_ops { /* Returns 0, or -errno. arg is in kp->arg. */ int (*set)(const char *val, const struct kernel_param *kp);设置参数的函数 /* Returns length written or -errno. Buffer is 4k (ie. be short!) */ int (*get)(char *buffer, const struct kernel_param *kp);读取参数的函数 /* Optional function to free kp->arg when module unloaded. */ void (*free)(void *arg); };
/* Flag bits for kernel_param.flags */ #define KPARAM_ISBOOL 2
/* Special one for strings we want to copy into */ struct kparam_string { unsigned int maxlen; char *string; };
/* Special one for arrays */ struct kparam_array { unsigned int max; unsigned int *num; const struct kernel_param_ops *ops; unsigned int elemsize; void *elem; };
接下来来看parse_one函数,其中early和一般的pase都是通过这个函数来解析: kernel/params.c static inline char dash2underscore(char c) { if (c == '-') return '_'; return c; }
static inline int parameq(const char *input, const char *paramname) { unsigned int i; for (i = 0; dash2underscore(input[i]) == paramname[i]; i++) if (input[i] == '\0') return 1; return 0; }
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; 如果是early_param则直接跳过这步,而非early的,则要通过这步来设置一些内置模块的参数。 /* Find parameter */ for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { /* Noone handled NULL, so do it here. */ if (!val && params[i].ops->set != param_set_bool) 调用参数设置函数来设置对应的参数。 return -EINVAL; DEBUGP("They are equal! Calling %p\n", params[i].ops->set); mutex_lock(¶m_lock); err = params[i].ops->set(val, ¶ms[i]); mutex_unlock(¶m_lock); return err; } }