http://blog.chinaunix.net/uid-20543672-id-3157283.html

在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段,

 
   下面我就顺这代码逐个函数的解释,但是这里并不会过于深入每个函数,因为这样就会只见树木,不见森林。分析代码首先要从构架上宏观地理解,然后再去考虑 细节问题(这和小时候学语文要概括中心思想和段落大意是一个道理)。今后对于每个子系统的初始化,特别是内存子系统,有空都会分析一遍。

  1. asmlinkage void __init start_kernel(void)
  2. {
  3. char * command_line;
  4. extern const struct kernel_param __start___param[], __stop___param[];
  5. 点击(此处)折叠或打开

    1. 这里关键是这两个变量的地址是如何确定的。
    2. 这两个变量为地址指针,指向内核启动参数处理相关结构体段在内存中的位置(虚拟地址)。
    3. 这里是外部变量,定义的位置在arch/../../vmlinux.lds.S,而大多数平台是放到kernel\include\asm-generic\vmlinux.lds.h中,定义如下:
    4. 点击(此处)折叠或打开

      1. 362     /* 内建模块的参数处理段. */               \
      2. 363
            __param : AT(ADDR(__param) - LOAD_OFFSET) {         \
      3. 364         VMLINUX_SYMBOL(__start___param) = .;            \
      4. 365         *(__param)                      \
      5. 366         VMLINUX_SYMBOL(__stop___param) = .;         \
      6. 367     }
    5. 对于ARM平台,似乎位于 kernel\include\asm-generic\vmlinux.lds.h
    6. 这个段中数据的数据结构:
    7. kernel_param结构体的定义是:

    点击(此处)折叠或打开

    1. 36 struct kernel_param_ops {
    2. 37      /* Returns 0, or -errno. arg is in kp->arg. */
    3. 38      int (*set)(const char *val, const struct kernel_param *kp);
    4. 39      /* Returns length written or -errno. Buffer is 4k (ie. be */
    5. 40      int (*get)(char *buffer, const struct kernel_param *kp);
    6. 41      /* Optional function to free kp->arg when module unloaded. */
    7. 42      void (*free)(void *arg);
    8. 43  };
    9. 44
    10. 45  /* Flag bits for kernel_param.flags */
    11. 46  #define KPARAM_ISBOOL 2
    12. 47
    13. 48 struct kernel_param {
    14. 49      const char *name;
    15. 50      const struct kernel_param_ops *ops;
    16. 51       u16 perm;
    17. 52      u16 flags;
    18. 53      union {
    19. 54          void *arg;
    20. 55          const struct kparam_string *str;
    21. 56          const struct kparam_array *arr;
    22. 57      };
    23. 58 };
    24. 59
    25. 60  /* Special one for strings we want to copy into */
    26. 61  struct kparam_string {
    27. 62      unsigned int maxlen;
    28. 63      char *string;
    29. 64  };
    30. 65
    31. 66  /* Special one for arrays */
    32. 67  struct kparam_array
    33. 68  {
    34. 69      unsigned int max;
    35. 70      unsigned int elemsize;
    36. 71      unsigned int *num;
    37. 72      const struct kernel_param_ops *ops;
    38. 73      void *elem;
    39. 74  };
     
  6. smp_setup_processor_id();
  7. 点击(此处)折叠或打开

    1. 这个函数是针对SMP处理器的,经查阅资料,其作用是获取当前CPU的的硬件ID。
    2. 如果不是多处理器构架,在其他文件中就不会定义这个函数,此时使用本文件定义的弱引用函数:
    3. void __init __weak smp_setup_processor_id(void)
    4. {
    5. }
  8. /*
  9.      * 必须尽早运行这个程序, 作用是初始化
  10.      * lockdep 模块的hash表:
  11. */
  12. lockdep_init();
  13. 点击(此处)折叠或打开

    1. lockdep是一个内核调试模块,用来检查内核互斥机制(尤其是自旋锁)潜在的死锁问题。
    2. 由于自旋锁以查询方式等待,不释放处理器,比一般互斥机制更容易死锁,故引入lockdep检查以下几种可能的死锁情况:
    • 同一个进程递归地加锁同一把锁;
    • 一把锁既在中断(或中断下半部)使能的情况下执行过加锁操作, 又在中断(或中断下半部)里执行过加锁操作。这样该锁有可能在锁定时由于中断发生又试图在同一处理器上加锁;
    • 加锁后导致依赖图产生成闭环,这是典型的死锁现象。
  14. debug_objects_early_init();
  15. 点击(此处)折叠或打开

    1. 在启动早期初始化hash buckets 和链接静态的 pool objects对象到 poll 列表. 在这个调用完成后 object tracker 已经开始完全运作了.
  16. /*
  17. *  初始化栈canary值:
  18. */
  19. boot_init_stack_canary();
  20. 点击(此处)折叠或打开

    1. canary值的是用于防止栈溢出攻击的堆栈的保护字
    2. 参考资料: GCC 中的编译器堆栈保护技术
  21. cgroup_init_early();
  22. 点击(此处)折叠或打开

    1. cgroup: 它的全称为control group.即一组进程的行为控制.
    2. 该函数主要是做数据结构和其中链表的初始化
    3. 参考资料: Linux cgroup机制分析之框架分析
  23. local_irq_disable();
  24. 点击(此处)折叠或打开

    1. 关闭系统总中断(底层调用汇编指令)
  25. early_boot_irqs_disabled = true;
  26. 点击(此处)折叠或打开

    1. 设置系统中断的关闭标志(bool全局变量)
  27. /*
  28. * 中断依然被禁用。做必要的设置后,
  29. * 再使能它
  30. */
  31. tick_init();
  32. 点击(此处)折叠或打开

    1. 初始化内核时钟系统
    2. -->clockevents_register_notifier(&tick_notifier)
    3. 往内核通知链中注册内核时钟时间的通知函数
    4. 参考资料:《Linux 时钟处理机制》  《Linux 时钟管理
  33. boot_cpu_init();
  34. 点击(此处)折叠或打开

    1. 激活当前CPU(在内核全局变量中将当前CPU的状态设为激活状态)
    2. 参考资料:《激活第一个CPU
  35. page_address_init();
  36. 点击(此处)折叠或打开

    1. 高端内存相关,未定义高端内存的话为空函数
  37.     printk(KERN_NOTICE "%s", linux_banner);
  38. 点击(此处)折叠或打开

    1. 打印内核版本信息,也就是平时我们在内核启动时在串口中看到的:
      1. Linux
        version 2.6.37+ (tekkaman@tekkaman-desktop) (gcc version 4.3.3
        (Sourcery G++ Lite 2009q1-203) ) #40 Tue Mar 20 17:49:58 CST 2012
  39.     setup_arch(&command_line);
  40. 点击(此处)折叠或打开

    1. 内核构架相关初始化函数,可以说是非常重要的一个初始化步骤。其中包含了处理器相关参数的初始化、内核启动参数(tagged list)的获取和前期处理、内存子系统的早期的初始化(bootmem分配器)
    2. 对于ARM构架来说,这个函数位于:arch/arm/kernel/setup.c
    3. 以后会详细分析这个函数。
  41. mm_init_owner(&init_mm, &init_task);
  42. mm_init_cpumask(&init_mm);
  43. 点击(此处)折叠或打开

    1. 初始化代表内核本身内存使用的管理结构体init_mm。
    2. ps:每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核的mm_struct,其中:
    3. 设置成员变量* mmap指向自己,意味着内核只有一个内存管理结构;
    4. 设置* pgd=swapper_pg_dir,swapper_pg_dir是内核的页目录(在arm体系结构有16k, 所以init_mm定义了整个kernel的内存空间)。
    5. 这些内容涉及到内存管理子系统,以后再仔细分析。
  44. setup_command_line(command_line);
  45. 点击(此处)折叠或打开

    1. 对cmdline进行备份和保存
    2. /* 为处理的command line备份 (例如eg. 用于 /proc) */
    3. char
      *saved_command_line;
    4. /* 用于参数处理的command line */
    5. static char *static_command_line;
  46. setup_nr_cpu_ids();
  47. setup_per_cpu_areas();
  48. smp_prepare_boot_cpu();    /* arch-specific boot-cpu hooks */
  49. 点击(此处)折叠或打开

    1. 针对SMP处理器的内存初始化函数,如果不是SMP系统则都为空函数。
    2. 他们的目的是给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间并为boot CPU设置一些数据。
    3. 在SMP系统中,在引导过程中使用的CPU称为boot CPU
  50. build_all_zonelists(NULL);
  51. page_alloc_init();
  52. 点击(此处)折叠或打开

    1. 设置内存管理相关的node(节点,每个CPU一个内存节点)和其中的zone(内存域,包含于节点中,如)数据结构,以完成内存管理子系统的初始化,并设置bootmem分配器。
    2. page_alloc_init函数暂时不知其目的
  53. printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
  54. parse_early_param();
  55. parse_args("Booting kernel", static_command_line, __start___param,
  56. __stop___param - __start___param,
  57. &unknown_bootoption);

点击(此处)折叠或打开

  1. 打印从内核启动参数中获取的cmdline字符串。
  2. 解析cmdline中的启动参数。
  1. /*
  2. * 使用大量bootmem分配,且必须先于
  3. * kmem_cache_init()
  4. */
  5. 点击(此处)折叠或打开

    1. 以上注释的含义在于bootmem是内核启动时使用的临时内存分配器。之后由slab接替。
    2. kmem_cache_init()初始化了内核高速缓存分配器(slab分配器),这个函数标标志着bootmem的终结,同时内核的内存管理系统正式启用了。
    3. 所以在kmem_cache_init()之后,bootmem的API不再可用,所以bootmem分配必须先于kmem_cache_init()。
  6. setup_log_buf(0);
  7. 点击(此处)折叠或打开

    1. 使用bootmem分配一个记录启动信息的缓存区
  8. pidhash_init();
  9. 点击(此处)折叠或打开

    1. 使用bootmem分配并初始化PID散列表,由PID分配器管理空闲和已指派的PID
  10. vfs_caches_init_early();
  11. 点击(此处)折叠或打开

  12. sort_main_extable();

    点击(此处)折叠或打开

    1. 对内核异常表( exception table )按照异常向量号大小进行排序。
  13. trap_init();

    点击(此处)折叠或打开

    1. 对内核陷阱异常进行初始化,在ARM构架中为空函数。
  14. mm_init();

    点击(此处)折叠或打开

    1. 初始化内核内存分配器,其包含6个子函数,作用如下:
    2. 1、page_cgroup_init_flatmem(); 获取page_cgroup 所需内存
    3. 2、mem_init(); 关闭并释放bootmem分配器,打印内存信息。在内核启动时看到的类似如下信息,就是其子函数mem_init输出的:
    4. 点击(此处)折叠或打开

      1. Memory: 86MB 39MB = 125MB total
      2. Memory: 120768k/120768k available, 99392k reserved, 0K highmem
      3. Virtual kernel memory layout:
      4. vector : 0xffff0000 - 0xffff1000 ( 4 kB)
      5. fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
      6. DMA : 0xffc00000 - 0xffe00000 ( 2 MB)
      7. vmalloc : 0xde800000 - 0xf8000000 ( 408 MB)
      8. lowmem : 0xc0000000 - 0xde400000 ( 484 MB)
      9. pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB)
      10. modules : 0xbf000000 - 0xbfe00000 ( 14 MB)
      11. .init : 0xc0008000 - 0xc003b000 ( 204 kB)
      12. .text : 0xc003b000 - 0xc04c0000 (4628 kB)
      13. .data : 0xc04c0000 - 0xc0501a80 ( 263 kB)
    5. 3、kmem_cache_init(); 初始化slab分配器
    6. 4、percpu_init_late(); PerCPU变量系统后期初始化
    7. 5、pgtable_cache_init();页表缓存初始化,对于ARM,“介是一个空函数”
    8. 6、vmalloc_init();初始化虚拟内存分配器
  15. /*
  16. * 在开启任何中断(比如定时器中断)前设置调度器。
  17. * 完整的拓扑设置发生在smp_init()中
  18. * -但与此同时,我们仍然有一个正常运作的调度器。
  19. */
  20. sched_init();

    点击(此处)折叠或打开

    1. 初始化调度器数据结构,并创建运行队列
  21. /*
  22. * 禁用强占 - 早期启动时的调度是极为脆弱的,
  23. * 直到cpu_idle()的首次运行。
  24. */
  25. preempt_disable();
  26. if (!irqs_disabled()) {
  27. printk(KERN_WARNING "start_kernel(): bug: interrupts were "
  28. "enabled *very* early, fixing it\n");
  29. local_irq_disable();
  30. }

    点击(此处)折叠或打开

    1. 在启动的初期关闭抢占和中断。
  31. idr_init_cache();
  32. 点击(此处)折叠或打开

    1. 为IDR机制分配缓存,主要是为struct idr_layer结构体分配空间
    2. 参考资料:《浅析linux内核中的idr机制》
  33. perf_event_init();
  34. 点击(此处)折叠或打开

    1. CPU性能监视机制初始化
    2. 此机制包括CPU同一时间执行指令数,cache miss数,分支预测失败次数等性能参数
  35. rcu_init();

    点击(此处)折叠或打开

    1. 内核RCU(Read-Copy Update:读取-复制-更新)机制初始化
  36. radix_tree_init();

    点击(此处)折叠或打开

    1. 内核radix树算法初始化
  37. /* 在init_ISA_irqs()之前初始化一些链接 */
  38. early_irq_init();
  39. init_IRQ();

    点击(此处)折叠或打开

    1. 硬件中断系统初始化:
    2. early_irq_init();前期外部中断描述符初始化,主要初始化数据结构。
    3. init_IRQ;对应构架特定的中断初始化函数,在ARM构架中:
    4. 点击(此处)折叠或打开

      1. void __init init_IRQ(void)
      2. {
      3. machine_desc->init_irq();
      4. }
    5. 也就是运行设备描述结构体中的init_irq函数,此函数一般在板级初始化文件(arch/*/mach-*/board-*.c)中定义。
  40. prio_tree_init();

    点击(此处)折叠或打开

    1. 初始化内核基于radix数的优先级搜索树(PST),主要是对其结构体进行初始化。
  41. init_timers();
  42. hrtimers_init();
  43. softirq_init();
  44. timekeeping_init();
  45. time_init();

    点击(此处)折叠或打开

    1. 以上几个函数主要是初始化内核的软中断及时钟机制:
    2. 前面几个函数主要是注册一些内核通知函数到cpu和hotcpu通知链,并开启部分软中断(tasklet等)。
    3. 最后的time_init是构架相关的,旨在开启一个硬件定时器,开始产生系统时钟。对于ARM构架:

      点击(此处)折叠或打开

      1. void __init time_init(void)
      2. {
      3. system_timer = machine_desc->timer;
      4. system_timer->init();
      5. #ifdef CONFIG_HAVE_SCHED_CLOCK
      6. sched_clock_postinit();
      7. #endif
      8. }
    4. 其实就是调用板级初始化文件(arch/arm/mach-*/board-*.c)中定义“设备描述结构体”中的timer成员的初始化函数。
  46. profile_init();

    点击(此处)折叠或打开

    1. 初始化内核profile子系统,她是内核的性能调试工具。
  47. call_function_init();

    点击(此处)折叠或打开

    1. 初始化所有CPU的call_single_queue(具体作用还没搞明白),并注册CPU热插拔通知函数到CPU通知链中。
  48. if (!irqs_disabled())
  49. printk(KERN_CRIT "start_kernel(): bug: interrupts were "
  50. "enabled early\n");
  51. early_boot_irqs_disabled = false;
  52. local_irq_enable();

    点击(此处)折叠或打开

    1. 检测硬件中断是否开启,如果开启了就打印出警告。
    2. 设置启动早期IRQ使能标志,允许IRQ使能。
    3. 最后开启总中断(ARM构架是这样,其他构架可能也是这个意思)。
  53. /*  中断已经开启,因此所有GFP分配是安全的. */
  54. gfp_allowed_mask = __GFP_BITS_MASK;
  55. 点击(此处)折叠或打开

    1. 开启所有GFP分配允许标志
    2. GFP(get free page)
  56. kmem_cache_init_late();

    点击(此处)折叠或打开

    1. slab分配器的后期初始化。如果使用的是slob或slub,则为空函数。
  57. /*
  58. * HACK ALERT! hack警告!这个是早期的。我们在完成PCI设置等工作前
  59. * 使能控制台,且console_init()必须意识到这个。
  60. * 但是我们的确想要早点输出信息,以防某些错误的发生。
  61. */
  62. console_init();

    点击(此处)折叠或打开

    1. 初始化控制台,这样可以早点看到启动信息,避免出错时无法查找原因。
  63. if (panic_later)
  64. panic(panic_later, panic_param);
  65. 点击(此处)折叠或打开

    1. 检查内核恐慌标志。如果出了问题,就打印信息。
  66. lockdep_info();

    点击(此处)折叠或打开

    1. 打印lockdep调试模块的信息。
  67. /*
  68. * 当irq使能的时候必须运行这个函数,因为它也要自检
  69. * [hard/soft]-irqs 开/关 锁反转的bug:
  70. *
  71. */
  72. locking_selftest();
  73. #ifdef CONFIG_BLK_DEV_INITRD
  74. if (initrd_start && !initrd_below_start_ok &&
  75. page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
  76. printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
  77. "disabling it.\n",
  78. page_to_pfn(virt_to_page((void *)initrd_start)),
  79. min_low_pfn);
  80. initrd_start = 0;
  81. }
  82. #endif

    点击(此处)折叠或打开

    1. 检查initrd的位置是否符合要求。
    2. min_low_pfn是系统可用的最小的pfn(页帧号)。
    3. 也就是判断传递进来initrd_start对应的物理地址是否正常。如果有误就打印错误信息,并清零initrd_start。
  83. page_cgroup_init();

    点击(此处)折叠或打开

    1. mem_cgroup是cgroup体系中提供的用于memory隔离的功能,此处对此功能进行初始化。
    2. 参考资料:《linux内核mem_cgroup浅析》
  84. enable_debug_pagealloc();

    点击(此处)折叠或打开

    1. 使能页分配的调试标志。
  85. debug_objects_mem_init();

    点击(此处)折叠或打开

    1. debug objects机制的内存分配初始化
  86. kmemleak_init();

    点击(此处)折叠或打开

    1. 内核内存泄漏检测机制初始化;
  87. setup_per_cpu_pageset();

    点击(此处)折叠或打开

    1. 设置每个CPU的页组,并初始化。此前只有启动页组。
  88. numa_policy_init();

    点击(此处)折叠或打开

    1. 非一致性内存访问(NUMA)初始化
  89. if (late_time_init)
  90. late_time_init();

    点击(此处)折叠或打开

    1. 如果构架存在此函数,就调用后期时间初始化。
  91. sched_clock_init();

    点击(此处)折叠或打开

    1. 对每个CPU,初始化调度时钟。
  92. calibrate_delay();

    点击(此处)折叠或打开

    1. 计算BogoMIPS值,他是衡量一个CPU性能的标志。
  93. pidmap_init();

    点击(此处)折叠或打开

    1. PID分配映射初始化。
  94. anon_vma_init();

    点击(此处)折叠或打开

    1. 匿名虚拟内存域( anonymous VMA)初始化
  95. #ifdef CONFIG_X86
  96. if (efi_enabled)
  97. efi_enter_virtual_mode();
  98. #endif
  99. thread_info_cache_init();

    点击(此处)折叠或打开

    1. 获取thread_info缓存空间,大部分构架为空函数(包括ARM)
  100. cred_init();

    点击(此处)折叠或打开

    1. 任务信用系统初始化。详见:Documentation/credentials.txt
  101. fork_init(totalram_pages);

    点击(此处)折叠或打开

    1. 进程创建机制初始化。为内核"task_struct"分配空间,计算最大任务数。
  102. proc_caches_init();

    点击(此处)折叠或打开

    1. 初始化进程创建机制所需的其他数据结构,为其申请空间。
  103. buffer_init();

    点击(此处)折叠或打开

    1. 缓存系统初始化,创建缓存头空间,并检查其大小限时。
  104. key_init();

    点击(此处)折叠或打开

    1. 内核密匙管理系统初始化。
  105. security_init();

    点击(此处)折叠或打开

    1. 内核安全框架初始化
  106. dbg_late_init();

    点击(此处)折叠或打开

    1. 内核调试系统后期初始化
  107. vfs_caches_init(totalram_pages);

    点击(此处)折叠或打开

    1. 虚拟文件系统(VFS)缓存初始化
  108. signals_init();

    点击(此处)折叠或打开

    1. 信号管理系统初始化
  109. /* 根文件系统的填充可能需要也回写机制 */
  110. page_writeback_init();

    点击(此处)折叠或打开

    1. 页回写机制初始化
  111. #ifdef CONFIG_PROC_FS
  112. proc_root_init();
  113. #endif

    点击(此处)折叠或打开

    1. proc文件系统初始化
  114. cgroup_init();

    点击(此处)折叠或打开

    1. control group的正式初始化
  115. cpuset_init();

    点击(此处)折叠或打开

    1. CPUSET初始化。
    2. 参考资料:《多核心計算環境—NUMA與CPUSET簡介》
  116. taskstats_init_early();

    点击(此处)折叠或打开

    1. 任务状态早期初始化函数:为结构体获取高速缓存,并初始化互斥机制。
  117. delayacct_init();

    点击(此处)折叠或打开

    1. 任务延迟机制初始化
  118. check_bugs();

    点击(此处)折叠或打开

    1. 检查CPU BUG的函数,通过软件规避BUG
  119. acpi_early_init(); /* 在 LAPIC 和 SMP 前初始化 */

    点击(此处)折叠或打开

    1. ACPI早期初始化函数。
    2. ACPI - Advanced Configuration and Power Interface高级配置及电源接口
  120. sfi_init_late();

    点击(此处)折叠或打开

    1. SFI 初始程序晚期设置函数,
    2. SFI - SIMPLE FIRMWARE INTERFACE。
  121. ftrace_init();

    点击(此处)折叠或打开

    1. 功能跟踪调试机制初始化,ftrace 是 function trace 的简称。
  122. /* 所剩下的非-__init的初始化, 内核现在已经启动了 */
  123.  rest_init();

    点击(此处)折叠或打开

    1. 虽然从名字上来说是剩余的初始化。
    2. 但是这个函数中的初始化包含了很多的内容,后面我回单独写一篇来分析。
  124. }

在看完上面的代码之后,你会发现内容很多。但是归纳起来,我认为需要注意的有以下几点:

  1. 内核启动参数的获取和处理
  2. setup_arch(&command_line);函数
  3. 内存管理的初始化(从bootmem到slab)
  4. rest_init();函数
 
其他的部分都是对内核各个组件的数据结构申请内存,并初始化。
 
 
-----------------------------------------------------------
参考资料:
  1. 一个很棒的台湾博士生 陳育書关于Linux启动分析的博文

  2. 《深入Linux内核构架》附录D 系统启动

Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)的更多相关文章

  1. 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源 | 百篇博客分析OpenHarmonyOS | v2.07

    百篇博客系列篇.本篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核 ...

  2. Linux内核源码分析 day01——内存寻址

    前言 Linux内核源码分析 Antz系统编写已经开始了内核部分了,在编写时同时也参考学习一点Linux内核知识. 自制Antz操作系统 一个自制的操作系统,Antz .半图形化半命令式系统,同时嵌入 ...

  3. 鸿蒙内核源码分析(根文件系统) | 先挂到`/`上的文件系统 | 百篇博客分析OpenHarmony源码 | v66.01

    百篇博客系列篇.本篇为: v66.xx 鸿蒙内核源码分析(根文件系统) | 先挂到/上的文件系统 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...

  4. 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 百篇博客分析OpenHarmony源码 | v62.01

    百篇博客系列篇.本篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 51.c.h.o 本篇开始说文件系统,它是内核五大模块之一,甚至有Linux的设计哲学是" ...

  5. 鸿蒙内核源码分析(GN应用篇) | GN语法及在鸿蒙的使用 | 百篇博客分析OpenHarmony源码 | v60.01

    百篇博客系列篇.本篇为: v60.xx 鸿蒙内核源码分析(gn应用篇) | gn语法及在鸿蒙的使用 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...

  6. 鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main | 百篇博客分析OpenHarmony源码 | v51.04

    百篇博客系列篇.本篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | ...

  7. 鸿蒙内核源码分析(信号生产篇) | 信号安装和发送过程是怎样的? | 百篇博客分析OpenHarmony源码 | v48.03

    百篇博客系列篇.本篇为: v48.xx 鸿蒙内核源码分析(信号生产篇) | 年过半百,依然活力十足 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管 ...

  8. 鸿蒙内核源码分析(特殊进程篇) | 龙生龙,凤生凤,老鼠生儿会打洞 | 百篇博客分析OpenHarmony源码 | v46.02

    百篇博客系列篇.本篇为: v46.xx 鸿蒙内核源码分析(特殊进程篇) | 龙生龙凤生凤老鼠生儿会打洞 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁 ...

  9. 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02

    百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...

  10. 鸿蒙内核源码分析(管道文件篇) | 如何降低数据流动成本 | 百篇博客分析OpenHarmony源码 | v70.01

    百篇博客系列篇.本篇为: v70.xx 鸿蒙内核源码分析(管道文件篇) | 如何降低数据流动成本 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...

随机推荐

  1. docker certificates

    x509: certificate is valid for mmtrkjy.com, *.mmtrkjy.com, *.mmtrkmc.com, *.mmtrkpd.com, *.mmtrksg.c ...

  2. HttpWebRequest 抓取页面异常处理办法

    抓取页面异常处理办法 public static string GetHtmlTest(string URI) { string fullhtml = null; while (true) { try ...

  3. sql语句操作表

    "create table mytable (m_id integer identity(1,1) primary key,m_class varchar(50) not null defa ...

  4. SQL中的左连接与右连接有什么区别,点解返回值会不同?(转)

    例子,相信你一看就明白,不需要多说 A表(a1,b1,c1) B表(a2,b2) a1 b1 c1 a2 b2 01 数学 95 01 张三 02 语文 90 02 李四 03 英语 80 04 王五 ...

  5. wpf计时器

    wpf应用程序间隔一段时间触发一个事件,代码如下: public MainWindow() { InitializeComponent(); DispatcherTimer timer = new D ...

  6. EditText的一点深入的了解

    最近在开发android下的记事本程序时,频繁的使用EditText控件,折腾来折腾去,算是对其的了解更深入了一些.特将这些收获记录如下: 一.几个属性的介绍 android:gravity=&quo ...

  7. Webkit之HTML解析

    加载部分HTML文本(即主资源)后便可以开始解析HTML元素(对输入字节流进行逐字扫描,识别HTML元素),最后生成DOM树,本文只讲HTML解析. HTML解析部分时序图: 其中最为重要的过程是(1 ...

  8. 在win7/8/10鼠标右键添加带管理员权限的“在此处打开命令窗口”

    Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Drive\shell\runas]@="@shell32.dll,-8506 ...

  9. 转:HTML错误编号大全

    HTML错误编号大全 状态行包含HTTP版本.状态代码.与状态代码对应的简短说明信息.在大多数情况下,除了Content-Type之外的所有应答头都是可选的.但Content-Type是必需的,它描述 ...

  10. android webview处理下载内容

    url = "http://m.mumayi.com/"; WebView = (WebView) findViewById(R.id.webView1); WebView.get ...