Linux-Android启动之Init进程前传

      对Linux-Android系统的启动做了一些分析,下面的一篇文章侧重讲述Linux启动过程中函数Start_kernel()被调用之前的一些分析,同时也对函数Start_kernel()之后的代码流程作了概述,我希望关于Linux-Android系统的启动的专题能够继续地写下去,哈哈。如果有不正确或者不完善的地方,欢迎前来拍砖留言或者发邮件到guopeixin@126.com进行讨论,现行谢过。

 

一. 内核自引导程序


1. 内核zimage自解压

 这部分代码在arch/${arch}/boot/compressed/head.S中,该文件的代码在zimage的生成过程中,将会被打包到zimage中。
 head.S会首先初始化自解压相关的如内存等环境,接下来就去调用decompress_kernel去解压,并调用call_kernel函数去启动vmlinux。
 去下面仅仅列举一下head.S文件中最重要的部分:
----------------------------------------------------------------
/*
 * We're not in danger of overwriting ourselves.  Do this the simple way.
 *
 * r4     = kernel execution address
 * r7     = architecture ID
 */
wont_overwrite: mov r0, r4
  mov r3, r7
  bl decompress_kernel
  b call_kernel
...
call_kernel: bl cache_clean_flush
  bl cache_off
  mov r0, #0   @ must be zero
  mov r1, r7   @ restore architecture number
  mov r2, r8   @ restore atags pointer
  mov pc, r4   @ call kernel
----------------------------------------------------------------
 其中函数decompress_kernel在arch/${arch}/boot/compressed/misc.c中实现,功能就是完成zimage镜像的自解压,显然该自解压的过程需

要配置相应的解压地址等,这部分代码如下:
----------------------------------------------------------------
ulg
decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,
    int arch_id)
{
 output_data  = (uch *)output_start; /* Points to kernel start */
 free_mem_ptr  = free_mem_ptr_p; /* 显然,这个地址是从通过寄存器传进来的 */
 free_mem_end_ptr = free_mem_ptr_end_p;
 __machine_arch_type = arch_id;

 arch_decomp_setup();

 makecrc();
 putstr("Uncompressing Linux...");
 gunzip();
 putstr(" done, booting the kernel./n");
 return output_ptr;
}
----------------------------------------------------------------
 调用call_kernel后首先关闭cache,然后就跳转到vmlinux入口去执行并将系统的控制权交给了vmlinux。


2. 内核vmlinux入口

>> vmlinux的编译简单描述
 因为这里会牵扯到两个文件head.S和head-nommu.S,所以下面简单的描述一下vmlinux的生成过程。来看一下/arch/${arch}/kernel/makefile

,在该文件的最后脚本如下:
----------------------------------------------------------------
#
# Makefile for the linux kernel.
#

AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)

ifdef CONFIG_DYNAMIC_FTRACE
CFLAGS_REMOVE_ftrace.o = -pg
endif

# Object file lists.

obj-y  := compat.o elf.o entry-armv.o entry-common.o irq.o /
     process.o ptrace.o setup.o signal.o /
     sys_arm.o stacktrace.o time.o traps.o

obj-$(CONFIG_ISA_DMA_API) += dma.o
obj-$(CONFIG_ARCH_ACORN) += ecard.o
obj-$(CONFIG_FIQ)  += fiq.o
obj-$(CONFIG_MODULES)  += armksyms.o module.o
obj-$(CONFIG_ARTHUR)  += arthur.o
obj-$(CONFIG_ISA_DMA)  += dma-isa.o
obj-$(CONFIG_PCI)  += bios32.o isa.o
obj-$(CONFIG_SMP)  += smp.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_KEXEC)  += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_KPROBES)  += kprobes.o kprobes-decode.o
obj-$(CONFIG_ATAGS_PROC) += atags.o
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
obj-$(CONFIG_KGDB)  += kgdb.o

obj-$(CONFIG_CRUNCH)  += crunch.o crunch-bits.o
AFLAGS_crunch-bits.o  := -Wa,-mcpu=ep9312

obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o
obj-$(CONFIG_CPU_XSC3)  += xscale-cp0.o
obj-$(CONFIG_IWMMXT)  += iwmmxt.o
AFLAGS_iwmmxt.o   := -Wa,-mcpu=iwmmxt

ifneq ($(CONFIG_ARCH_EBSA110),y)
  obj-y  += io.o
endif

head-y   := head$(MMUEXT).o
obj-$(CONFIG_DEBUG_LL) += debug.o

extra-y := $(head-y) init_task.o vmlinux.lds
----------------------------------------------------------------
 可以看到,文件的结束位置有一行代码“head-y   := head$(MMUEXT).o”,其中MMUEXT在/arch/${arch}/makefile中

定义,实际上对于没有mmu的处理器,MMUEXT就是nommu,而对于包含mmu的处理器,它的值是空,参照MMUEXT在/arch/${arch}/makefile中的相关代码

如下:
----------------------------------------------------------------
# defines filename extension depending memory manement type.
ifeq ($(CONFIG_MMU),)
MMUEXT  := -nommu
endif
----------------------------------------------------------------
 所以对于诸如S3C6410之类的包含MMU的处理器,实际上最终vmlinux开始位置的代码就是/arch/${arch}/kernel/head.S.

>> head.S文件的分析
 需要注意的是,对于该文件的描述,一般的书籍上可能是仅仅对老版本的linux系统进行了分析,就是说该文件结束位置直接调用了

start_kernel 函数,至此开始执行c代码。其实,并不是这样的。
 下面简单的列写一下head.S的内容:
----------------------------------------------------------------/*
 * Kernel startup entry point.
 * ---------------------------
 *
 * This is normally called from the decompressor code.  The requirements
 * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
 * r1 = machine nr, r2 = atags pointer.
 *
 * This code is mostly position independent, so if you link the kernel at
 * 0xc0008000, you call this at __pa(0xc0008000).
 *
 * See linux/arch/arm/tools/mach-types for the complete list of machine
 * numbers for r1.
 *
 * We're trying to keep crap to a minimum; DO NOT add any machine specific
 * crap here - that's what the boot loader (or in extreme, well justified
 * circumstances, zImage) is for.
 */
 .section ".text.head", "ax"
ENTRY(stext)
 msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
      @ and irqs disabled
 mrc p15, 0, r9, c0, c0  @ get processor id
 bl __lookup_processor_type  @ r5=procinfo r9=cpuid
 movs r10, r5    @ invalid processor (r5=0)?
 beq __error_p   @ yes, error 'p'
 bl __lookup_machine_type  @ r5=machinfo
 movs r8, r5    @ invalid machine (r5=0)?
 beq __error_a   @ yes, error 'a'
 bl __vet_atags
 bl __create_page_tables

 /*
  * The following calls CPU specific code in a position independent
  * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
  * xxx_proc_info structure selected by __lookup_machine_type
  * above.  On return, the CPU will be ready for the MMU to be
  * turned on, and r0 will hold the CPU control register value.
  */
 ldr r13, __switch_data  @ address to jump to after
      @ mmu has been enabled
 adr lr, __enable_mmu  @ return (PIC) address
 add pc, r10, #PROCINFO_INITFUNC
ENDPROC(stext)

#if defined(CONFIG_SMP)
ENTRY(secondary_startup)
 /*
  * Common entry point for secondary CPUs.
  *
  * Ensure that we're in SVC mode, and IRQs are disabled.  Lookup
  * the processor type - there is no need to check the machine type
  * as it has already been validated by the primary processor.
  */
 msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
 mrc p15, 0, r9, c0, c0  @ get processor id
 bl __lookup_processor_type
 movs r10, r5    @ invalid processor?
 moveq r0, #'p'   @ yes, error 'p'
 beq __error

 /*
  * Use the page tables supplied from  __cpu_up.
  */
 adr r4, __secondary_data
 ldmia r4, {r5, r7, r13}  @ address to jump to after
 sub r4, r4, r5   @ mmu has been enabled
 ldr r4, [r7, r4]   @ get secondary_data.pgdir
 adr lr, __enable_mmu  @ return address
 add pc, r10, #PROCINFO_INITFUNC @ initialise processor
      @ (return control reg)
ENDPROC(secondary_startup)

 /*
  * r6  = &secondary_data
  */
ENTRY(__secondary_switched)
 ldr sp, [r7, #4]   @ get secondary_data.stack
 mov fp, #0
 b secondary_start_kernel
ENDPROC(__secondary_switched)

 .type __secondary_data, %object
__secondary_data:
 .long .
 .long secondary_data
 .long __secondary_switched
#endif /* defined(CONFIG_SMP) */

/*
 * Setup common bits before finally enabling the MMU.  Essentially
 * this is just loading the page table pointer and domain access
 * registers.
 */
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
 orr r0, r0, #CR_A
#else
 bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
 bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
 bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
 bic r0, r0, #CR_I
#endif
 mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | /
        domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | /
        domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | /
        domain_val(DOMAIN_IO, DOMAIN_CLIENT))
 mcr p15, 0, r5, c3, c0, 0  @ load domain access register
 mcr p15, 0, r4, c2, c0, 0  @ load page table pointer
 b __turn_mmu_on
ENDPROC(__enable_mmu)

/*
 * Enable the MMU.  This completely changes the structure of the visible
 * memory space.  You will not be able to trace execution through this.
 * If you have an enquiry about this, *please* check the linux-arm-kernel
 * mailing list archives BEFORE sending another post to the list.
 *
 *  r0  = cp#15 control register
 *  r13 = *virtual* address to jump to upon completion
 *
 * other registers depend on the function called upon completion
 */
 .align 5
__turn_mmu_on:
 mov r0, r0
 mcr p15, 0, r0, c1, c0, 0  @ write control reg
 mrc p15, 0, r3, c0, c0, 0  @ read id reg
 mov r3, r3
 mov r3, r3
 mov pc, r13
ENDPROC(__turn_mmu_on)

#include "head-common.S"
----------------------------------------------------------------
 可能大家注意到,上面有大段的文字是secondary_startup以及CONFIG_SMP等,其实这个是对于SMP系统才会采用的代码。众所周知,SMP是对

称多处理的简称,是指系统中使用了一组处理器,各CPU之间共享内存子系统和总线结构,对应的有非对称多处理,嵌入式设备上我们并不会使用到

SMP的功能。
 乍一看,无论如何也调用不到网上所谓的start_kernel函数中,大家注意看“ldr r13, __switch_data”,这里就是将函数__switch_data的

地址保存到r13,并在函数__enable_mmu-->__turn_mmu_on结束位置的“mov pc, r13”中将__switch_data调用起来。而函数__switch_data是实

现在/arch/${arch}/kernel/head-common.S中的一个函数,而函数start_kernel就是由__switch_data调用起来的。
 你一定在奇怪,那么函数__enable_mmu是怎么调用起来的呢,呵呵,你简直是太聪明、太细心了。那赶紧听我跟你说吧,代码“add pc,

r10, #PROCINFO_INITFUNC”将会跳转到/arch/${arch}/mm/proc-arn-926.S中的初始化函数__arm926_setup中,并在该函数结束的位置以“mov pc,

lr”的方式调用__enable_mmu,千万别告诉我你忘记了前面提到的__enable_mmu的值保存在lr中哦。
 至于为什么代码“add pc, r10, #PROCINFO_INITFUNC”将会跳转到/arch/${arch}/mm/proc-arn-926.S中的初始化函数__arm926_setup

中,我这里就不列举了。可以参照后面我转载的一篇文章。
----------------------------------------------------------------
 .type __arm926_setup, #function
__arm926_setup:
 mov r0, #0
 mcr p15, 0, r0, c7, c7  @ invalidate I,D caches on v4
 mcr p15, 0, r0, c7, c10, 4  @ drain write buffer on v4
#ifdef CONFIG_MMU
 mcr p15, 0, r0, c8, c7  @ invalidate I,D TLBs on v4
#endif


#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
 mov r0, #4    @ disable write-back on caches explicitly
 mcr p15, 7, r0, c15, c0, 0
#endif

 adr r5, arm926_crval
 ldmia r5, {r5, r6}
 mrc p15, 0, r0, c1, c0  @ get control register v4
 bic r0, r0, r5
 orr r0, r0, r6
#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN
 orr r0, r0, #0x4000   @ .1.. .... .... ....
#endif
 mov pc, lr
----------------------------------------------------------------
 好了,终于调用到start_kernel了,这是任何版本的linux内核通用的初始化函数。


3. Linux系统初始化

 前面已经提到,函数start_kernel是任何版本的linux内核通用的初始化函数,也是汇编代码执行结束后的第一个c函数,它实现在

init/main.c中。
 有关start_kernel的代码很长,初始化了很多东西,比如调用了setup_arch()、timer_init()、init_IRQ、console_init()、

pgtable_cache_init()、security_init()、signals_init()和rest_init()等,这里只对rest_init()做简单的分析。
 下面首先列写一下rest_init()的代码:
----------------------------------------------------------------
/*
 * We need to finalize in a non-__init function or else race conditions
 * between the root thread and the init thread may cause start_kernel to
 * be reaped by free_initmem before the root thread has proceeded to
 * cpu_idle.
 *
 * gcc-3.4 accidentally inlines this function, so use noinline.
 */

static noinline void __init_refok rest_init(void)
 __releases(kernel_lock)
{
 int pid;

 kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
 numa_default_policy();
 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
 unlock_kernel();

 /*
  * The boot idle thread must execute schedule()
  * at least once to get things moving:
  */
 init_idle_bootup_task(current);
 rcu_scheduler_starting();
 preempt_enable_no_resched();
 schedule();
 preempt_disable();

 /* Call into cpu_idle with preempt disabled */
 cpu_idle();
}
----------------------------------------------------------------
 可以看到,函数rest_init()首先会去创建线程kernel_init(注意:这里和网上或者相关书籍中描述的也不一样,可能是Linux版本的问题)

,有些文档中描述这里创建的是Init线程,虽然名字不一致,但是具体的实现是基本一致的,基本上都是完成根文件系统的挂载、初始化所有Linux的

设备驱动(就是调用驱动的初始化函数,类似于CE/Mobile中的Device Manager对设备驱动的初始化)以及启动用户空间Init进程。
 由于手中的rest_init进程和网上描述的都是不一致的,所以这里也进行了简要的列举,代码如下:
----------------------------------------------------------------
static int __init kernel_init(void * unused)
{
 lock_kernel();
 /*
  * init can run on any cpu.
  */
 set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR);
 /*
  * Tell the world that we're going to be the grim
  * reaper of innocent orphaned children.
  *
  * We don't want people to have to make incorrect
  * assumptions about where in the task array this
  * can be found.
  */
 init_pid_ns.child_reaper = current;

 cad_pid = task_pid(current);

 smp_prepare_cpus(setup_max_cpus);

 do_pre_smp_initcalls();
 start_boot_trace();

 smp_init();
 sched_init_smp();

 cpuset_init_smp();

 do_basic_setup();

 /*
  * check if there is an early userspace init.  If yes, let it do all
  * the work
  */

 if (!ramdisk_execute_command)
  ramdisk_execute_command = "/init";

 if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
  ramdisk_execute_command = NULL;
  prepare_namespace();
 }

 /*
  * Ok, we have completed the initial bootup, and
  * we're essentially up and running. Get rid of the
  * initmem segments and start the user-mode stuff..
  */

 init_post();
 return 0;
}

static noinline int init_post(void)
{
 /* need to finish all async __init code before freeing the memory */
 async_synchronize_full();
 free_initmem();
 unlock_kernel();
 mark_rodata_ro();
 system_state = SYSTEM_RUNNING;
 numa_default_policy();

 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
  printk(KERN_WARNING "Warning: unable to open an initial console./n");

 (void) sys_dup(0);
 (void) sys_dup(0);

 current->signal->flags |= SIGNAL_UNKILLABLE;

 if (ramdisk_execute_command) {
  run_init_process(ramdisk_execute_command);
  printk(KERN_WARNING "Failed to execute %s/n",
    ramdisk_execute_command);
 }

 /*
  * We try each of these until one succeeds.
  *
  * The Bourne shell can be used instead of init if we are
  * trying to recover a really broken machine.
  */
 if (execute_command) {
  run_init_process(execute_command);
  printk(KERN_WARNING "Failed to execute %s.  Attempting "
     "defaults.../n", execute_command);
 }
 run_init_process("/sbin/init");
 run_init_process("/etc/init");
 run_init_process("/bin/init");
 run_init_process("/bin/sh");

 panic("No init found.  Try passing init= option to kernel.");
}
----------------------------------------------------------------
 可以看到,和网络上相关的描述不一样的是,这里首先会去初始化设备驱动,而不是像网上或者数据上所描述的一样,首先去加载跟文件系

统,难道不存在初始化的时候需要访问文件的驱动了?或者以前的做法纯属一种安全的考虑?
 这些问题就留到以后对Linux&Android有深入地了解之后再去考虑吧!
 好了,搞定!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/243138.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何正确入门Windows系统下驱动开发领域?

[作者]猪头三作者网站: http://www.x86asm.com原文链接: http://blog.csdn.net/Code_GodFather/...0/5975901.aspx[贡献者]1> defddr 看雪学院2> StudyRush 看雪学院[序言]很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大…

OVS VxLAN Flow 分析 - 每天5分钟玩转 OpenStack(149)

OVS 的数据流向都是由 Flow 规则控制的&#xff0c;今天我们就来分析 VxLAN 的 Flow 规则。 提个醒&#xff1a;这可能是本教程最烧脑的一节&#xff0c;lets rock it ! 下面分析控制节点上的 flow rule&#xff0c;计算节点类似。 br-int 的 flow rule br-int 的 rule 看上去虽…

制作 OpenStack Linux 镜像 - 每天5分钟玩转 OpenStack(151)

这是 OpenStack 实施经验分享系列的第 1 篇。 OpenStack 的 instance 是通过 Glance 镜像部署的&#xff0c;所以准备镜像是必须要做的工作。本节介绍 Linux 镜像的制作方法&#xff0c;后面还会讨论 Windows 镜像。 下载clould 镜像 最简单的方法是使用标准镜像。主流的Linux发…

NDIS与WinSock关系之自我扫盲

起来真是雷人&#xff0c;最近几天纠结与一个最基本的概念&#xff0c;就是NDIS与WinSock关系&#xff0c;想来想去都没有想明白&#xff0c;真实汗Ing&#xff0c;赶紧找了篇精美的文章来扫盲一下。 原文如下&#xff1a; 文章转自http://www.cnblogs.com/sankye/articles/16…

WDF驱动中KMDF与UMDF区别

众所周知&#xff0c; 早期的Windows 95/98的设备驱动是VxD(Virtual Device Driver)&#xff0c;其中x表示某一类设备。从Windows 2000开始&#xff0c;开发驱动程序必以WDM&#xff08;Windows Driver Model&#xff09;为基础的&#xff0c;但是&#xff0c;如果使用DDK来开发…

根据状态转移表实现时序电路

根据状态转移表实现时序电路 某同步时序电路转换表如下&#xff0c;请使用D触发器和必要的逻辑门实现此同步时序电路 电路的接口如下图所示 A表示输入&#xff0c;Y 表示输出 理解状态转移表&#xff0c;画成状态转移图 timescale 1ns/1nsmodule seq_circuit(input …

Facebook、LinkedIn、Airbnb、Google专家聚首QCon,他们会带来什么?

4月16日~18日&#xff0c;QCon北京2017将在北京国家会议中心举行。现已确认来自海外的Google、Facebook、Airbnb、LinkedIn、Confluent、AppDynamics等公司&#xff0c;国内的百度、阿里巴巴、腾讯、京东、滴滴出行、奇虎360、爱奇艺、微博、bilibili等公司的100余位技术专家担…

Devoxx 2017美国大会首日重要演讲一览

Devoxx美国大会今天开幕。\u0026#xD;\n\u0026#xD;\nDevoxx是北美版的欧洲软件大会&#xff08;European Software Conference&#xff09;。广受追捧的欧洲软件大会由Stephan Janssen在2001年创立&#xff0c;组织方是比利时Java用户组&#xff08;BeJUG&#xff0c;Belgian Ja…

Linux-Android启动之Machine-Init函数

Linux/Android启动之Machine-Init函数 前言&#xff1a; 前面写过两篇Linux/Android的启动过程分析&#xff0c;这篇接着前两篇的知识点进行分析。 Linux/Android的启动过程包括了很多内容&#xff0c;其中有些需要了解&#xff0c;有些则需要在系统移植的时候进行修改。本篇文…

cache 是什么意思 它包括的L1,L2,L3分别是什么东西?

CPU缓存缓存大小也是CPU的重要指标之一&#xff0c;而且缓存的结构和大小对CPU速度的影响非常大&#xff0c;CPU内缓存的运行频率极高&#xff0c;一般是和处理器同频运作&#xff0c;工作效率远远大于系统内存和硬盘。实际工作时&#xff0c;CPU往往需要重复读取同样的数据块&…

边沿检测—以脉冲形式给出信号

边沿检测—以脉冲形式给出信号 题目描述&#xff1a; 有一个缓慢变化的1bit信号a&#xff0c;编写一个程序检测a信号的上升沿给出指示信号rise&#xff0c;当a信号出现下降沿时给出指示信号down。 注&#xff1a;rise,down应为单脉冲信号&#xff0c;在相应边沿出现时的下一个…

贷款秒拒?你可能进了“灰名单”!

灰名单/GRAY LIST今天有客户向我反映&#xff0c;他的征信良好没有半点问题&#xff0c;只有一张信用卡备用&#xff0c;没有一次逾期&#xff0c;但是最近想申请贷款都是被秒拒。他觉得很郁闷&#xff0c;是不是办了假的贷款&#xff1f;他完全不知道被拒的理由&#xff0c;只…

Linux--根文件系统的挂载过程分析

前言&#xff1a; 本篇文章以S3C6410公版的Linux BSP和U-Boot来进行分析&#xff0c;文中所有提及的名词和数据都是以该环境为例&#xff0c;所有的代码流程也是以该环境为例来进行分析。哈哈。如果有不正确或者不完善的地方&#xff0c;欢迎前来拍砖留言或者发邮件到guopeixi…

Troubleshooting OpenStack Bug- 每天5分钟玩转 OpenStack(162)

这是 OpenStack 实施经验分享系列的第 12 篇。 问题描述 客户报告了一个问题&#xff1a;对 instance 执行 migrate 操作&#xff0c;几个小时了一直无法完成&#xff0c;不太正常。 问题分析 遇到这种情况&#xff0c;第一个要检查的就是 instance 所在计算节点的 nova-comput…

ROM简单实现

ROM简单实现 题目描述 实现一个深度为8&#xff0c;位宽为4bit的ROM&#xff0c;数据初始化为0&#xff0c;2&#xff0c;4&#xff0c;6&#xff0c;8&#xff0c;10&#xff0c;12&#xff0c;14。可以通过输入地址addr&#xff0c;输出相应的数据data 接口信号图如下&…

Fedora gedit 打开txt文件乱码的解决

编码格式不同&#xff0c;fedora打开 windows下的文本文件经常出现乱码。解决办法终端输入命令&#xff1a;$ gsettings set org.gnome.gedit.preferences.encodings auto-detected "[UTF-8, GB18030, GB2312, GBK, BIG5, CURRENT, UTF-16]"$ gsettings set org.gn…

Linux--Sys_Read系统调用过程分析

注&#xff1a; 本片文章以Read函数的调用为例来讲述一下系统对块驱动层的一些处理, 哈哈。如果有不正确或者不完善的地方&#xff0c;欢迎前来拍砖留言或者发邮件到guopeixin126.com进行讨论&#xff0c;先行谢过。 一&#xff0e;Read函数经由的层次模型 首先来了解一下Re…

负债的阶梯,你在第几层?

现在的年轻人成为了消费市场上的中流砥柱&#xff0c;他们为我国的GDP贡献了70%的数据。如今年轻人更愿意刷信用卡、网贷平台借钱消费。尽管在外打拼不容易&#xff0c;但是工作稳定&#xff0c;收入稳定&#xff0c;为了犒劳自己及时享乐&#xff0c;他们选择了背负一点债务。…

OOP术语

OOP术语 类&#xff08;class&#xff09;&#xff1a;包含变量和子程序的基本构建块。Verilog中与之对应的是模块&#xff08;module&#xff09;。对象&#xff08;object&#xff09;:类的一个实例。Verilog中你需要实例化一个模块才能使用它。句柄&#xff08;handle&…

minincom cannot open /dev/modem

昨天用pacman -S minicom命令在archlinux上下载并安装了minicom&#xff0c;但是调用minicom命令时出现如下错误&#xff1a;minicom: cannot open /dev/modem: No such file or directory今天终于找到解决方案&#xff0c;做一个软链接到/dev/ttyS0就可以解决问题了。命令如下…