uboot学习之BL3的流程
BL2的最后通过汇编调用了board_init_r函数,此时进入BL3的阶段,此时的主要工作:
这一阶段涉及的文件及任务如下
arch/arm/lib/board.c
1. board_init_r()是进入定制板目录的入口
common/main.c
2. main_loop()中关闭中断,执行命令以及加载引导内核
下面分析一下board_init_r函数:
/*
************************************************************************
*
* This is the next part if the initialization sequence: we are now
* running from RAM and have a "normal" C environment, i. e. global
* data can be written, BSS has been cleared, the stack size in not
* that critical any more, etc.
*
************************************************************************
*/
void board_init_r(gd_t *id, ulong dest_addr)
{
...
bd_t *bd;
...
gd = id;
bd = gd->bd; gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ monitor_flash_len = _end_ofs; ...
debug("monitor flash len: %08lX\n", monitor_flash_len);
board_init(); /* Setup chipselects */
上述代码的作用是对gd和bd进行赋值,其中monitor_flash_len为整个U-Boot的长度。
malloc_start = dest_addr - TOTAL_MALLOC_LEN - sizeof(struct spare_boot_head_t);
...
/* The Malloc area is immediately below the monitor copy in DRAM */
mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);
对SDRAM中的malloc空间进行清零初始化。
#if !defined(CONFIG_SYS_NO_FLASH)
puts("Flash: "); flash_size = flash_init();
if (flash_size > ) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
print_size(flash_size, "");
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
s = getenv("flashchecksum");
if (s && (*s == 'y')) {
printf(" CRC: %08X", crc32(,
(const unsigned char *) CONFIG_SYS_FLASH_BASE,
flash_size));
}
putc('\n');
# else /* !CONFIG_SYS_FLASH_CHECKSUM */
print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
} else {
puts(failed);
hang();
}
#endif
上述代码的作用是计算FLASH的大小,并把它通过串口显示在控制台上。由于没有定义CONFIG_SYS_FLASH_CHECKSUM,所以没有执行CRC的校验和。其中flash_init函数是在drivers/mtd目录下的cfi_flash.c文件内(因为include/configs/smdk2410.h中定义了CONFIG_FLASH_CFI_DRIVER)。
/* set up exceptions */
interrupt_init();
/* enable exceptions */
enable_interrupts();
interrupt_init函数是建立IRQ中断堆栈,enable_interrupts函数是使能IRQ中断,它们都是在arch/arm/lib目录下的interrupts.c文件中定义的。
...
/* initialize environment */
env_relocate();
初始化环境变量,由于gd->env_valid等于0,所以在这里设置的是缺省环境变量。env_relocate函数是在common目录下的env_common.c文件中定义的。
#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
arm_pci_init();
#endif
初始化PCI。
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr("ipaddr");
stdio_init(); /* get the devices list going. */
jumptable_init();
#if defined(CONFIG_API)
/* Initialize API */
api_init();
#endif console_init_r(); /* fully init console as a device */
上面代码的作用分别对应:
设置IP地址。
初始化各类外设,如IIC、LCD、键盘、USB等,当然只有在定义了这些外设的前提下,才对这些外设进行初始化。该函数是在common目录下的stdio.c文件中定义的。
初始化跳转表gd->jt,该跳转表是一个函数指针数组,它定义了U-Boot中基本的常用函数库。该函数是在common目录下的exports.c文件中定义的。
初始化API。
初始化控制台,即标准输入、标准输出和标准错误,在这里都是串口。该函数是在common目录下的console.c文件中定义的。
/* Initialize from environment */
s = getenv("loadaddr");
if (s != NULL)
load_addr = simple_strtoul(s, NULL, );
从环境变量中获取loadaddr参数,得到需要加载的地址。
#if defined(CONFIG_CMD_NET)
s = getenv("bootfile");
if (s != NULL)
copy_filename(BootFile, s, sizeof(BootFile));
#endif
从环境变量中获取bootfile参数,得到通过TFTP加载的镜像文件名。
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts("Net: ");
#endif
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif
初始化以太网,其中eth_initialize函数是在net目录下的eth.c文件中定义的。
...
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;)
{
main_loop();
}
hang();
/* NOTREACHED - no way out of command loop except booting */
}
board_init_r函数的最后就是执行一个死循环,调用main_loop函数。该函数是在common目录下的main.c文件内定义的。
总结:
3.接下来分析main_loop函数:
void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
static char lastcommand[CONFIG_SYS_CBSIZE] = { , };
int len;
int rc = ;
int flag;
#endif
声明一些hush参数。关于hush后面会讲到。
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif
声明启动延时需要的参数。
#ifdef CONFIG_SYS_HUSH_PARSER
u_boot_hush_start ();
#endif
初始化hush功能。稍后再说。
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, ) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); # ifdef CONFIG_BOOT_RETRY_TIME
init_cmd_timeout ();
# endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_POST
if (gd->flags & GD_FLG_POSTFAIL) {
s = getenv("failbootcmd");
}
else
#endif /* CONFIG_POST */
#ifdef CONFIG_BOOTCOUNT_LIMIT
if (bootlimit && (bootcount > bootlimit)) {
printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
(unsigned)bootlimit);
s = getenv ("altbootcmd");
}
else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
s = getenv ("bootcmd"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, );
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
} # ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s) {
# ifndef CONFIG_SYS_HUSH_PARSER
run_command(s, );
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
}
}
#endif /* CONFIG_MENUKEY */
#endif /* CONFIG_BOOTDELAY */
第2行和第3行的含义是从环境变量中获取bootdelay参数,得到自动启动缺省镜像文件的延时(单位是秒)。
其中bootdelay的作用是:uboot正常启动后,会调用main_loop(void)函数,进入main_loop()之后,如果在规定的时间(CONFIG_BOOTDELAY)内,没有检查到任何按键事件的发生,就会去加载OS,并启动系统。
第8行的含义是初始化命令行超时机制。
第25行的含义是从环境变量中获取bootcmd参数,得到在启动延时过程中自动执行的命令。当我们得到了bootcmd参数,bootdelay参数也是大于等于0,并且在启动延时过程中没有按下任意键时,执行第37行的parse_string_outer函数,该函数的作用是解释bootcmd参数
并执行,它是在common目录下的hush.c文件内定义的。
/*
2 * Main Loop for Monitor Command Processing
3 */
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= ) {
/* Saw enough of a valid command to
13 * restart the timeout.
14 */
reset_cmd_timeout();
}
#endif
len = readline (CONFIG_SYS_PROMPT); flag = ; /* assume no special flags for now */
if (len > )
strcpy (lastcommand, console_buffer);
else if (len == )
flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -) {
/* -2 means timed out, retry autoboot
28 */
puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
/* Reinit board to run initialization code again */
do_reset (NULL, , , NULL);
# else
return; /* retry autoboot */
# endif
}
#endif if (len == -)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag); if (rc <= ) {
/* invalid command or not repeatable, forget it */
lastcommand[] = ;
}
}
#endif /*CONFIG_SYS_HUSH_PARSER*/
}
由于在include/configs/smdk2410.h文件中定义了CONFIG_SYS_HUSH_PARSER,所以上面的代码仅仅执行的是第5行至第7行的内容。第5行的parse_file_outer函数是在common目录下的hush.c文件中定义的,它的含义是依次读取命令序列中的命令并执行之,其中在该
函数还调用了parse_stream_outer函数,这个函数体内有一个do-while循环,只有发生语法错误的时候才会跳出该循环,因此一般情况下永远也不会执行上面代码中的第7行内容,而是始终在那个do-while循环体内。
上面说到过如果在CONFIG_BOOTDELAY时间内,用户按下键盘上的任意一个按键,uboot就会进入与用户交互的状态。如果用户在配置文件中定义了CONFIG_SYS_HUSH_PARSER,就会通过parse_file_outer(),去接收并解析用户命令,否则进入一个for(;;)循环,
通过 readline (CONFIG_SYS_PROMPT)接收用户命令,然后调用run_command(cmd,flag)去解析并执行命令。
当在配置文件中定义了CONFIG_SYS_HUSH_PARSER,main_loop会调用parse_file_outer(),进入hush中。其中parse_file_outer()在u-boot\common\hush.c下。
#ifndef __U_BOOT__
static int parse_file_outer(FILE *f)
#else
int parse_file_outer(void)
#endif
{
int rcode;
struct in_str input;
#ifndef __U_BOOT__
setup_file_in_str(&input, f);
#else
setup_file_in_str(&input);
#endif
rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
return rcode;
}
/* most recursion does not come through here, the exeception is
* from builtin_source() */
int parse_stream_outer(struct in_str *inp, int flag)
{ struct p_context ctx;
o_string temp=NULL_O_STRING;
int rcode;
#ifdef __U_BOOT__
int code = ;
#endif
do {
ctx.type = flag;
initialize_context(&ctx);
update_ifs_map();
if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", );
inp->promptmode=;
rcode = parse_stream(&temp, &ctx, inp, '\n');
#ifdef __U_BOOT__
if (rcode == ) flag_repeat = ;
#endif
if (rcode != && ctx.old_flag != ) {
syntax();
#ifdef __U_BOOT__
flag_repeat = ;
#endif
}
if (rcode != && ctx.old_flag == ) {
done_word(&temp, &ctx);
done_pipe(&ctx,PIPE_SEQ);
#ifndef __U_BOOT__
run_list(ctx.list_head);
#else
code = run_list(ctx.list_head);
if (code == -) { /* exit */
b_free(&temp);
code = ;
/* XXX hackish way to not allow exit from main loop */
if (inp->peek == file_peek) {
printf("exit not allowed from main input shell.\n");
continue;
}
break;
}
if (code == -)
flag_repeat = ;
#endif
} else {
if (ctx.old_flag != ) {
free(ctx.stack);
b_reset(&temp);
}
#ifdef __U_BOOT__
if (inp->__promptme == ) printf("<INTERRUPT>\n");
inp->__promptme = ;
#endif
temp.nonnull = ;
temp.quote = ;
inp->p = NULL;
free_pipe_list(ctx.list_head,);
}
b_free(&temp);
} while (rcode != - && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */
#ifndef __U_BOOT__
return ;
#else
return (code != ) ? : ;
#endif /* __U_BOOT__ */
}
hush是uboot中命令接收和解析的工具,与uboot原始的命令解析方法相比,该工具更加智能。
这里会在run_list中调用到hush中的run_pipe_real(struct pipe *pi),在该函数中经过一些列解析,最终会调用到对应的命令执行函数。
/* run_pipe_real() starts all the jobs, but doesn't wait for anything
* to finish. See checkjobs().
*
* return code is normally -1, when the caller has to wait for children
* to finish to determine the exit status of the pipe. If the pipe
* is a simple builtin command, however, the action is done by the
* time run_pipe_real returns, and the exit code is provided as the
* return value.
*
* The input of the pipe is always stdin, the output is always
* stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
* because it tries to avoid running the command substitution in
* subshell, when that is in fact necessary. The subshell process
* now has its stdout directed to the input of the appropriate pipe,
* so this routine is noticeably simpler.
*/
static int run_pipe_real(struct pipe *pi)
{
int i;
#ifndef __U_BOOT__
int nextin, nextout;
int pipefds[]; /* pipefds[0] is for reading */
struct child_prog *child;
struct built_in_command *x;
char *p;
# if __GNUC__
/* Avoid longjmp clobbering */
(void) &i;
(void) &nextin;
(void) &nextout;
(void) &child;
# endif
#else
int nextin;
int flag = do_repeat ? CMD_FLAG_REPEAT : ;
struct child_prog *child;
cmd_tbl_t *cmdtp;
char *p;
# if __GNUC__
/* Avoid longjmp clobbering */
(void) &i;
(void) &nextin;
(void) &child;
# endif
#endif /* __U_BOOT__ */ nextin = ;
#ifndef __U_BOOT__
pi->pgrp = -;
#endif /* Check if this is a simple builtin (not part of a pipe).
* Builtins within pipes have to fork anyway, and are handled in
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
*/
if (pi->num_progs == ) child = & (pi->progs[]);
#ifndef __U_BOOT__
if (pi->num_progs == && child->group && child->subshell == ) {
int squirrel[] = {-, -, -};
int rcode;
debug_printf("non-subshell grouping\n");
setup_redirects(child, squirrel);
/* XXX could we merge code with following builtin case,
* by creating a pseudo builtin that calls run_list_real? */
rcode = run_list_real(child->group);
restore_redirects(squirrel);
#else
if (pi->num_progs == && child->group) {
int rcode;
debug_printf("non-subshell grouping\n");
rcode = run_list_real(child->group);
#endif
return rcode;
} else if (pi->num_progs == && pi->progs[].argv != NULL) {
for (i=; is_assignment(child->argv[i]); i++) { /* nothing */ }
if (i!= && child->argv[i]==NULL) {
/* assignments, but no command: set the local environment */
for (i=; child->argv[i]!=NULL; i++) { /* Ok, this case is tricky. We have to decide if this is a
* local variable, or an already exported variable. If it is
* already exported, we have to export the new value. If it is
* not exported, we need only set this as a local variable.
* This junk is all to decide whether or not to export this
* variable. */
int export_me=;
char *name, *value;
name = xstrdup(child->argv[i]);
debug_printf("Local environment set: %s\n", name);
value = strchr(name, '=');
if (value)
*value=;
#ifndef __U_BOOT__
if ( get_local_var(name)) {
export_me=;
}
#endif
free(name);
p = insert_var_value(child->argv[i]);
set_local_var(p, export_me);
if (p != child->argv[i]) free(p);
}
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
}
for (i = ; is_assignment(child->argv[i]); i++) {
p = insert_var_value(child->argv[i]);
#ifndef __U_BOOT__
putenv(strdup(p));
#else
set_local_var(p, );
#endif
if (p != child->argv[i]) {
child->sp--;
free(p);
}
}
if (child->sp) {
char * str = NULL; str = make_string((child->argv + i));
parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
free(str);
return last_return_code;
}
#ifndef __U_BOOT__
for (x = bltins; x->cmd; x++) {
if (strcmp(child->argv[i], x->cmd) == ) {
int squirrel[] = {-, -, -};
int rcode;
if (x->function == builtin_exec && child->argv[i+]==NULL) {
debug_printf("magic exec\n");
setup_redirects(child,NULL);
return EXIT_SUCCESS;
}
debug_printf("builtin inline %s\n", child->argv[]);
/* XXX setup_redirects acts on file descriptors, not FILEs.
* This is perfect for work that comes after exec().
* Is it really safe for inline use? Experimentally,
* things seem to work with glibc. */
setup_redirects(child, squirrel);
#else
/* check ";", because ,example , argv consist from
* "help;flinfo" must not execute
*/
if (strchr(child->argv[i], ';')) {
printf ("Unknown command '%s' - try 'help' or use 'run' command\n",
child->argv[i]);
return -;
}
/* Look up command in command table */ if ((cmdtp = find_cmd(child->argv[i])) == NULL) {
printf ("Unknown command '%s' - try 'help'\n", child->argv[i]);
return -; /* give up after bad command */
} else {
int rcode;
#if defined(CONFIG_CMD_BOOTD)
/* avoid "bootd" recursion */
if (cmdtp->cmd == do_bootd) {
if (flag & CMD_FLAG_BOOTD) {
printf ("'bootd' recursion detected\n");
return -;
}
else
flag |= CMD_FLAG_BOOTD;
}
#endif
/* found - check max args */
if ((child->argc - i) > cmdtp->maxargs)
return cmd_usage(cmdtp);
#endif
child->argv+=i; /* XXX horrible hack */
#ifndef __U_BOOT__
rcode = x->function(child);
#else
/* OK - call function to do the command */ rcode = (cmdtp->cmd)
(cmdtp, flag,child->argc-i,&child->argv[i]);
if ( !cmdtp->repeatable )
flag_repeat = ; #endif
child->argv-=i; /* XXX restore hack so free() can work right */
#ifndef __U_BOOT__ restore_redirects(squirrel);
#endif return rcode;
}
}
#ifndef __U_BOOT__
} for (i = ; i < pi->num_progs; i++) {
child = & (pi->progs[i]); /* pipes are inserted between pairs of commands */
if ((i + ) < pi->num_progs) {
if (pipe(pipefds)<) perror_msg_and_die("pipe");
nextout = pipefds[];
} else {
nextout=;
pipefds[] = -;
} /* XXX test for failed fork()? */
if (!(child->pid = fork())) {
/* Set the handling for job control signals back to the default. */
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGTTIN, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
signal(SIGCHLD, SIG_DFL); close_all(); if (nextin != ) {
dup2(nextin, );
close(nextin);
}
if (nextout != ) {
dup2(nextout, );
close(nextout);
}
if (pipefds[]!=-) {
close(pipefds[]); /* opposite end of our output pipe */
} /* Like bash, explicit redirects override pipes,
* and the pipe fd is available for dup'ing. */
setup_redirects(child,NULL); if (interactive && pi->followup!=PIPE_BG) {
/* If we (the child) win the race, put ourselves in the process
* group whose leader is the first process in this pipe. */
if (pi->pgrp < ) {
pi->pgrp = getpid();
}
if (setpgid(, pi->pgrp) == ) {
tcsetpgrp(, pi->pgrp);
}
} pseudo_exec(child);
} /* put our child in the process group whose leader is the
first process in this pipe */
if (pi->pgrp < ) {
pi->pgrp = child->pid;
}
/* Don't check for errors. The child may be dead already,
* in which case setpgid returns error code EACCES. */
setpgid(child->pid, pi->pgrp); if (nextin != )
close(nextin);
if (nextout != )
close(nextout); /* If there isn't another process, nextin is garbage
but it doesn't matter */
nextin = pipefds[];
}
#endif
return -;
}
上面关键是这段:
/* OK - call function to do the command */ rcode = (cmdtp->cmd)
(cmdtp, flag,child->argc-i,&child->argv[i]);
if ( !cmdtp->repeatable )
flag_repeat = ;
通过这段代码调用对应的命令执行函数。cmd_tbl_t的结构如下:
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
char *usage; /* Usage message (short) */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
}; typedef struct cmd_tbl_s cmd_tbl_t;
对于uboot支持的每一个命令,是通过U_BOOT_CMD宏定义的,他定义了该命令对应的名称name,支持的最大参数rep,重复次数,实现函数cmd,以及输入help命令时,显示的帮助信息usage。
在执行函数cmd中,第一个参数对应该命令结构本身的指针,第二个参数对应flag标记,第三个参数对应参数数目,第四个参数是指针数组,里面存储的是对应参数的指针。
uboot学习之BL3的流程的更多相关文章
- u-boot bootz 加载kernel 流程分析
u-boot 加载 kernel 的流程分析. image重要结构体头文件 // include/image.h * * Legacy and FIT format headers used by d ...
- app开发学习需要经历哪些流程
app开发学习需要经历哪些流程?如何零基础入门app开发?以下是知乎热心开发者的经验总结,对学习app开发有很好的参考意义 1.如果没有编程基础的,学习基础知识的过程肯定是必须的.2.有了一些基础 ...
- [笔记] 基于nvidia/cuda的深度学习基础镜像构建流程 V0.2
之前的[笔记] 基于nvidia/cuda的深度学习基础镜像构建流程已经Out了,以这篇为准. 基于NVidia官方的nvidia/cuda image,构建适用于Deep Learning的基础im ...
- uboot学习之uboot.bin的运行流程
上篇博客:http://www.cnblogs.com/yeqluofwupheng/p/7347925.html 讲到uboot-spl的工作流程,接下来简述一下uboot.bin的工作流程,这对应 ...
- uboot学习之uboot-spl的程序流程分析
uboot-spl的程序流程主要包含下面的几个函数: _start->reset->save_boot_params->cpu_init_crit->lowlevel_init ...
- uboot学习之uboot启动流程简述
一.uboot启动分为了三个阶段BL0.BL1.BL2:BL0表示上电后运行ROM中固化的一段程序,其中ROM中的程序是厂家写进去的,所以具体功能可能根据厂家芯片而有所不同.功能如下: 初始化系统时钟 ...
- 第二天-uboot学习
源码阅读方法1.源码目录结构2.配置(支持当前使用的硬件)3.编译(Makefile)4.启动流程 工具使用1.在同一文件查找 shitf+8 N n进行上下查找 2.在工程目录中 ctags ubo ...
- uboot学习第一天
Windows操作系统BIOS(设置) Windows系统 文件系统 驱动程序 应用程序 linux操作系统bootloader(引导系统) kernel(内核) 文件系统 驱动程序 应用程序 交叉编 ...
- 1.ok6410移植bootloader,移植u-boot,学习u-boot命令
ok6410移植u-boot 既然是移植u-boot当然首先需要u-boot源码,这里的u-boot代码是由国嵌提供的. 一.配置编译u-boot A. 解压 u-boot 压缩文件 B. 进入解压生 ...
随机推荐
- NN入门,手把手教你用Numpy手撕NN(一)
前言 这是一篇包含极少数学推导的NN入门文章 大概从今年4月份起就想着学一学NN,但是无奈平时时间不多,而且空闲时间都拿去做比赛或是看动漫去了,所以一拖再拖,直到这8月份才正式开始NN的学习. 这篇文 ...
- aabccd统计每个字符出现的次数,结果显示{ a: 2, b: 1, c: 2, d: 1 };去掉重复的字符,使结果显示abcd
遍历字符串的方式和遍历数组的方式有点相似,或者说就是相同的.在学习数组的遍历方法之前,可以通过for循环去遍历数组,同样,字符串也可以:字符串跟数组都有一个length的属性.下面代码奉上,个人思路! ...
- Java中访问修饰符public、private、protecte、default
Java中访问修饰符public.private.protecte.default的意义讲解:public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”.被其修饰的类.属性以及方法不 仅 ...
- java IO流 之 FIle类基础
package IO; import java.io.File;import java.io.IOException; public class FileIO { /** * 构建及获取文件名信息 * ...
- emlog博客的安装教程
简介 emlog 是一款基于PHP和MySQL的功能强大的博客及CMS建站系统.致力于为您提供快速.稳定,且在使用上又极其简单.舒适的内容创作及站点搭建服务. 安装步骤 1.将src文件夹下的所有文件 ...
- .NET CORE下最快比较两个文件内容是否相同的方法 - 续
.NET CORE下最快比较两个文件内容是否相同的方法 - 续 在上一篇博文中, 我使用了几种方法试图找到哪个是.NET CORE下最快比较两个文件的方法.文章发布后,引起了很多博友的讨论, 在此我对 ...
- Oracle误操作--被提交后的数据回退(闪回)
由于一时的粗心,在做update操作时,忘记了加where条件,导致全表数据被修改.此类错误实属不该!!特此记录一下!! 网上搜索Oracle数据回退操作,介绍如下: 闪回级别 闪回场景 闪回技术 对 ...
- 从Linux服务器下载上传文件
首先要确定好哪两种的连接:Linux常用的有centors和unbantu两种版本,PC端Mac和Windows 如果在两个Linux之间传输,或Linux和Mac之间传输可以使用scp命令,类似于s ...
- lightoj 1173 - The Vindictive Coach(dp)
题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1173 题解:像这种题目显然可以想到n为几时一共有几种排列可以递推出来.然后就是 ...
- Mysql InnoDB引擎下 事务的隔离级别
mysql InnoDB 引擎下事物学习 建表user CREATE TABLE `user` ( `uid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, ...