uboot
详细注释讲解

声明:该贴是通过参考其他人的帖子整理出来,从中我加深了对uboot的理解,我知道对其他人一定也是有很大的帮助,不敢私藏,如果里面的注释有什么错误请给我回复,我再加以修改。有些部分可能还没解释清楚,如果您觉得有必要注释,希望指出。再次强调该贴的大部分功劳应该归功于那些原创者,由于粗心,我没有留意参考的出处。我的目的是想让大家共同进步。希望大家念在我微不足道的心意,能够积极回馈,以便使帖子更加完善。以后还会把整理的东西陆续公布出来,谢谢光临!!


大多数
bootloader都分为stage1stage2两部分,u-boot也不例外。依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。
1Stage1 start.S代码结构

u-bootstage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下:
1)定义入口。由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROMFlash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
2)设置异常向量(Exception Vector)。
3)设置CPU的速度、时钟频率及终端控制寄存器。
4)初始化内存控制器。
5)将ROM中的程序复制到RAM中。
6)初始化堆栈。
7)转到RAM中执行,该工作可使用指令ldr pc来完成。
2Stage2 C语言代码部分
lib_arm/board.c中的start arm bootC语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个u-bootarmboot)的主函数,该函数只要完成如下操作:
1)调用一系列的初始化函数。
2)初始化Flash设备。
3)初始化系统内存分配函数。
4)如果目标系统拥有NAND设备,则初始化NAND设备。
5)如果目标系统有显示设备,则初始化该类设备。
6)初始化相关网络设备,填写IPMAC地址等。
7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。
3U-Boot的启动顺序(示例,其他u-boot版本类似)
cpu/arm920t/start.S

@文件包含处理

#include
@由顶层的mkconfig生成,其中只包含了一个文件:configs/<</SPAN>顶层makefile中6个参数的第1个参数>.h

#include
#include

注:ARM微处理器支持字节(8位)、半字(16位)、字(32位)3种数据类型
@向量跳转表,每条占四个字节(一个字),地址范围为0x0000
0000~@0x0000 0020
@ARM体系结构规定在上电复位后的起始位置,必须有8条连续的跳

@转指令,通过硬件实现。他们就是异常向量表。ARM在上电复位后,@是从0x00000000开始启动的,其实如果bootloader存在,在执行

@下面第一条指令后,就无条件跳转到start_code,下面一部分并没@执行。设置异常向量表的作用是识别bootloader。以后系统每当有@异常出现,则CPU会根据异常号,从内存的0x00000000处开始查表@做相应的处理

.globl _start
@_start是GNU汇编器的默认入口标签,.globl将_start声明为外部程序可访问的标签,.globl是GNU汇编的保留关键字,前面加点是GNU汇编的语法
_start: b start_code @0x00
@ARM上电后执行的第一条指令,也即复位向量,跳转到start_code

@reset用b,就是因为reset在MMU建立前后都有可能发生
@其他的异常只有在MMU建立之后才会发生

ldr pc,
_undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq

@对于ARM数据从内存到CPU之间的移动只能通过L/S指令,如:ldr
r0,0x12345678为把0x12345678内存中的数据写到r0中,还有一个就是ldr伪指令,如:ldr
r0,=0x12345678为把0x12345678地址写到r0中,mov只能完成寄存器间数据的移动,而且立即数长度限制在8位

_undefined_instruction: .word
undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
@.word为GNU
ARM汇编特有的伪操作,为分配一段字内存单元(分配的单元为字对齐的),可以使用.word把标志符作为常量使用。如_fiq:.word
fiq即把fiq存入内存变量_fiq中,也即是把fiq放到地址_fiq中。

.balignl 16,0xdeadbeef
@.balignl
是.balign的变体

@ .align伪操作用于表示对齐方式:通过添加填充字节使当前位置

@满足一定的对齐方式。.balign的作用同.align
@ .align {alignment} {,fill}
{,max}
@
其中:
alignment用于指定对齐方式,可能的取值为2的次

@幂,缺省为4fill是填充内容,缺省用0填充。max是填充字节@数最大值,如果填充字节数超过max,
就不进行对齐
,例如:
@ .align 4


【参考好野人的窝,于关u-boot中的.balignl 16,0xdeadbeef的理解
http://haoyeren.blog.sohu.com/84511571.html


@TEXT_BASE在开发板相关的目录中的config.mk文档中定义,
他定

@义了代码在运行时所在的地址,
那么_TEXT_BASE中保存了这个地

@址(这个TEXT_BASE怎么来的还不清楚)

.globl _armboot_start
_armboot_start:
.word _start
@用_start来初始化_armboot_start。(为什么要这么定义一下还不明白)


@下面这些是定义在开发板目录链接脚本中的

.globl _bss_start
_bss_start:
.word __bss_start
@__bss_start
定义在和开发板相关的u-boot.lds中,_bss_start保存的是__bss_start标号所在的地址。

.globl _bss_end
_bss_end:
.word _end
@同上,这样赋值是因为代码所在地址非编译时的地址,直接取得该标号对应地址。

@中断的堆栈设置

#ifdef CONFIG_USE_IRQ

.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de


.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif

@复位后执行程序

@真正的初始化从这里开始了。其实在CPU一上电以后就是跳到这里执行的
reset:

@更改处理器模式为管理模式

@对状态寄存器的修改要按照:读出-修改-写回的顺序来执行
@
31 30 29 28 --- 7 6 - 4 3 2 1 0
N Z C V I F M4 M3 M2 M1 M0
0 0 0 0 0 User26 模式
0 0 0 0 1 FIQ26 模式
0 0 0 1 0 IRQ26 模式
0 0 0 1 1 SVC26 模式
1 0 0 0 0 User 模式
1 0 0 0 1 FIQ 模式
1 0 0 1 0 IRQ 模式
1 0 0 1 1 SVC 模式
1 0 1 1 1 ABT 模式
1 1 0 1 1 UND 模式
1 1 1 1 1 SYS 模式

mrs r0,cpsr
@将cpsr的值读到r0中
bic r0,r0,#0x1f
@清除M0~M4
orr r0,r0,#0xd3
@禁止IRQ,FIQ中断,并将处理器置于管理模式
msr cpsr,r0

@以下是点灯了,这里应该会牵涉到硬件设置,移植的时候应该可以不要
bl coloured_LED_init
bl red_LED_on

@针对AT91RM9200进行特殊处理
#if defined(CONFIG_AT91RM9200DK) ||
defined(CONFIG_AT91RM9200EK)

ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
@sub带上了s用来更改进位标志,对于sub来说,若发生借位则C标志置0,没有则为1,这跟adds指令相反!要注意。
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif

@针对S3C2400和S3C2410进行特殊处理

@CONFIG_S3C2400、CONFIG_S3C2410等定义在include/configs/下不同开发板的头文件中
#if defined(CONFIG_S3C2400) ||
defined(CONFIG_S3C2410)

@关闭看门狗定时器的自动复位功能并屏蔽所有中断,上电后看门狗为开,中断为关
# if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008
# define CLKDIVN 0x14800014
#else
@s3c2410的配置
# define pWTCON 0x53000000
@pWTCON定义为看门狗控制寄存器的地址(s3c2410和s3c2440相同)
# define INTMSK 0x4A000008
@INTMSK定义为主中断屏蔽寄存器的地址(s3c2410和s3c2440相同)
# define INTSUBMSK 0x4A00001C
@INTSUBMSK定义为副中断屏蔽寄存器的地址(s3c2410和s3c2440相同)
# define CLKDIVN 0x4C000014
@CLKDIVN定义为时钟分频控制寄存器的地址(s3c2410和s3c2440相同)
# endif
@至此寄存器地址设置完毕

ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
@对于S3C2440和S3C2410的WTCON寄存器的[0]控制允许或禁止看门狗定时器的复位输出功能,设置为“0”禁止复位功能。


mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
@2410好像应该为7ff才对(不理解uboot为何是这个数字)
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
@对于S3C2410的INTMSK寄存器的32位和INTSUBMSK寄存器的低11位每一位对应一个中断,相应位置“1”为不响应相应的中断。对于S3C2440的INTSUBMSK有15位可用,所以应该为0x7fff了。

ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
@时钟分频设置,FCLK为核心提供时钟,HCLK为AHB(ARM920T,内存@控制器,中断控制器,LCD控制器,DMA和主USB模块)提供时钟,@PCLK为APB(看门狗、IIS、I2C、PWM、MMC、ADC、UART、GPIO、@RTC、SPI)提供时钟。分频数一般选择1:4:8,所以HDIVN=2,PDIVN=1,@CLKDIVN=5,这里仅仅是配置了分频寄存器,关于MPLLCON的配置肯@定写在lowlevel_init.S中了

@归纳出CLKDIVN的值跟分频的关系:
@0x0 = 1:1:1 , 0x1 = 1:1:2 , 0x2 = 1:2:2 , 0x3 = 1:2:4, 0x4 =
1:4:4, 0x5 = 1:4:8, 0x6 = 1:3:3,
0x7 = 1:3:6
@S3C2440的输出时钟计算式为:Mpll=(2*m*Fin)/(p*2^s)
S3C2410的输出时钟计算式为:Mpll=(m*Fin)/(p*2^s)
m=M(the value for divider M)+8;p=P(the value for divider P)+2
M,P,S的选择根据datasheet中PLL VALUE SELECTION TABLE表格进行,

我的开发板晶振为16.9344M,所以输出频率选为:399.65M的话M=0x6e,P=3,S=1
@s3c2440增加了摄像头,其FCLK、HCLK、PCLK的分频数还受到CAMDIVN[9](默认为0),CAMDIVN[8](默认为0)的影响

#endif


@选择是否初始化CPU
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
@执行CPU初始化,BL完成跳转的同时会把后面紧跟的一条指令地址保存到连接寄存器LR(R14)中。以使子程序执行完后正常返回。
#endif

@调试阶段的代码是直接在RAM中运行的,而最后需要把这些代码
@固化到Flash中,因此U-Boot需要自己从Flash转移到
@RAM中运行,这也是重定向的目的所在。
@通过adr指令得到当前代码的地址信息:如果U-boot是从RAM @开始运行,则从adr,r0,_start得到的地址信息为
@r0=_start=_TEXT_BASE=TEXT_BASE=0x33F80000;
@如果U-bootFlash开始运行,即从处理器对应的地址运行,
@r0=0x0000,这时将会执行copy_loop标识的那段代码了。
@ _TEXT_BASE
定义在
board/smdk2410/config.mk

#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:

adr r0, _start

ldr r1, _TEXT_BASE

cmp r0, r1

beq stack_setup
ldr r2, _armboot_start
@_armboot_start为_start地址
ldr r3, _bss_start
@_bss_start为数据段地址
sub r2, r3, r2

add r2, r0, r2

copy_loop:
ldmia r0!, {r3-r10}

@从源地址[r0]读取8个字节到寄存器,每读一个就更新一次r0地址

@ldmia:r0安字节增长
stmia r1!, {r3-r10}

@LDM(STM)用于在寄存器所指的一片连续存储器和寄存器列表的寄存@器间进行数据移动,或是进行压栈和出栈操作。

@格式为:LDM(STM){条件}{类型}基址寄存器{!},寄存器列表{^}
@对于类型有以下几种情况: IA
每次传送后地址加1,用于移动数

@据块

IB 每次传送前地址加1,用于移动数据块
DA 每次传送后地址减1,用于移动数据块
DB 每次传送前地址减1,用于移动数据块
FD 满递减堆栈,用于操作堆栈(即先移动指针再操作数据,相当于DB)
ED 空递减堆栈,用于操作堆栈(即先操作数据再移动指针,相当于DA)
FA 满递增堆栈,用于操作堆栈(即先移动指针再操作数据,相当于IB)
EA 空递增堆栈,用于操作堆栈(即先操作数据再移动指针,相当于IA)
(这里是不是应该要涉及到NAND或者NOR的读写?没有看出来)

cmp r0, r2
ble copy_loop
#endif /
* CONFIG_SKIP_RELOCATE_UBOOT */


@初始化堆栈
stack_setup:


ldr r0, _TEXT_BASE

@获取分配区域起始指针,

sub r0, r0, #CONFIG_SYS_MALLOC_LEN

@CFG_MALLOC_LEN=128*1024+CFG_ENV_SIZE=128*1024+0x1@0000=192K

sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE

@CFG_GBL_DATA_SIZE 128---size in bytes reserved for
initial data

用来存储开发板信息

#ifdef CONFIG_USE_IRQ

@这里如果需要使用IRQ,
还有给
IRQ保留堆栈空间,
一般不使用
.
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12

@该部分将未初始化数据段_bss_start----_bss_end中的数据
@清零
clear_bss:
ldr r0, _bss_start
ldr r1, _bss_end
mov r2, #0x00000000

clbss_l:str r2, [r0]
add r0, r0, #4
cmp r0, r1
ble clbss_l

@跳到阶段二C语言中去
ldr pc, _start_armboot

_start_armboot: .word
start_armboot
@start_armboot在/lib_arm/中,到这里因该是第一阶段已经完成了吧,下面就要去C语言中执行第二阶段了吧


@CPU初始化

@在“relocate:
”之前被调用

#ifndef
CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:

@初始化CACHES
mov r0, #0
mcr p15, 0, r0, c7, c7, 0

mcr p15, 0, r0, c8, c7, 0


@关闭MMU和CACHES
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
@对协处理器的操作还是看不懂,暂时先不管吧,有时间研究一下ARM技术手册的协处理器部分。


@初始化RAM时钟,因为内存是跟开发板密切相关的,所以这部分在/开发板目录/lowlevel_init.S中实现

mov ip, lr
@
保存LR,以便正常返回,注意前面是通过BL跳到cpu_init_crit来的。

@(ARM9有37个寄存器,ARM7有27个)
37个寄存器=7个未分组寄存器(R0~R7)+
2×(5个分组寄存器R8~R12)+6×2(R13=SP,R14=lr 分组寄存器) + 1(R15=PC)
+1(CPSR) + 5(SPSR)
用途和访问权限:
R0~R7:USR(用户模式)、fiq(快速中断模式)、irq(中断模式)、svc(超级用法模式)、abt、und
R8~R12:R8_usr~R12_usr(usr,irq,svc,abt,und)
R8_fiq~R12_fiq(fiq)
R11=fp
R12=IP(从反汇编上看,fp和ip一般用于存放SP的值)
R13~R14:R13_usr
R14_usr(每种模式都有自己的寄存器)
SP ~lr :R13_fiq
R14_fiq
R13_irq R14_irq
R13_svc R14_svc
R13_abt R14_abt
R13_und R14_und
R15(PC):都可以访问(即PC的值为当前指令的地址值加8个字节)
R16 :((Current Program Status
Register,当前程序状态寄存器))
SPSR _fiq,SPSR_irq,SPSR_abt,SPSR_und(USR模式没有)

#if defined(CONFIG_AT91RM9200EK)

#else
bl lowlevel_init

@在重定向代码之前,必须初始化内存时序,因为重定向时需要将@flash中的代码复制到内存中lowlevel_init在@/board/smdk2410/lowlevel_init.S中。

#endif
mov lr, ip
mov pc, lr
@返回到主程序

#endif


@这段没有看明白,不过好像跟移植关系不是很大,先放一放。
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72

#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52

#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0

#define MODE_SVC 0x13
#define I_BIT 0x80

.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE)
sub r2, r2, #(CONFIG_SYS_MALLOC_LEN)
sub r2, r2, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ set base 2 words into
abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC

add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm

.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r7, sp, #S_PC
stmdb r7, {sp, lr}^ @ Calling SP, LR
str lr, [r7, #0] @ Save calling PC
mrs r6, spsr
str r6, [r7, #4] @ Save CPSR
str r0, [r7, #8] @ Save OLD_R0
mov r0, sp
.endm

.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm

.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE)
sub r13, r13, #(CONFIG_SYS_MALLOC_LEN)
sub r13, r13, #(CONFIG_SYS_GBL_DATA_SIZE+8) @ reserved a couple
spots in abort stack

str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]

mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm

.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm

.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm


@异常向量处理

@每一个异常向量处其实只放了一条跳转指令(因为每个异常向量只 @有4个字节不能放太多的程序),跳到相应的异常处理程序中。
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction

.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt

.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort

.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort

.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used

#ifdef CONFIG_USE_IRQ

.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs

.align 5
fiq:
get_fiq_stack

irq_save_user_regs
bl do_fiq
irq_restore_user_regs

#else

.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq

.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq

#endif
@可知start.S的流程为:异常向量——上电复位后进入复位异常向量——跳到启动代码处——设置处理器进入管理模式——关闭看门狗——关闭中断——设置时钟分频——关闭MMU和CACHE——进入lowlever_init.S——检查当前代码所处的位置,如果在FLASH中就将代码搬移到RAM中


Uboot详细解析1的更多相关文章

  1. Uboot详细解析2

    1.第二阶段的主线函数位于u-boot-2010.06/arch/arm/lib/board.c. 第二阶段的功能: <1> 初始化本阶段要使用到的硬件设备. 设置时钟.初始化串口. bo ...

  2. 【系统移植】uboot详细分析

    uboot使用 uboot控制台,倒计时    命令: 调试,操作一些硬件 setenv printenv saveenv  nand erase  nand write  tftp zImage h ...

  3. java类生命周期详细解析

    (一)详解java类的生命周期 引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前 ...

  4. springmvc 项目完整示例06 日志–log4j 参数详细解析 log4j如何配置

    Log4j由三个重要的组件构成: 日志信息的优先级 日志信息的输出目的地 日志信息的输出格式 日志信息的优先级从高到低有ERROR.WARN. INFO.DEBUG,分别用来指定这条日志信息的重要程度 ...

  5. include_path详细解析

    include_path详细解析     原文地址:http://www.laruence.com/2010/05/04/1450.html 1.php默认的包含路径为 .;C:\php\pear 即 ...

  6. Intent的详细解析以及用法

    Intent的详细解析以及用法      Android的四大组件分别为Activity .Service.BroadcastReceiver(广播接收器).ContentProvider(内容提供者 ...

  7. C++多态的实现及原理详细解析

    C++多态的实现及原理详细解析 作者: 字体:[增加 减小] 类型:转载   C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型 ...

  8. 对MySQL DELETE语法的详细解析

    以下的文章主要描述的是MySQL DELETE语法的详细解析,首先我们是从单表语法与多表语法的示例开始的,假如你对MySQL DELETE语法的相关内容十分感兴趣的话,你就可以浏览以下的文章对其有个更 ...

  9. 转:二十一、详细解析Java中抽象类和接口的区别

    转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...

随机推荐

  1. New Concept English three (30)

    27W/m 32 words the death of ghost For years, villagers believed that Endley Farm was hunted. The far ...

  2. 【SQL查询】合并多个数据集_union

    SQL UNION 操作符 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列.列也必须拥有相似的数据类型.同时,每 ...

  3. pod上传私有spec文件库

    一.验证 function podlint() { pod lib lint $1.podspec --use-libraries --allow-warnings --verbose --sourc ...

  4. [Luogu3769][CH弱省胡策R2]TATT

    luogu 题意 其实就是四维偏序. sol 第一维排序,然后就只需要写个\(3D-tree\)了. 据说\(kD-tree\)的单次查询复杂度是\(O(n^{1-\frac{1}{k}})\).所以 ...

  5. Webpack-dashboard 简单使用

    1. 安装 npm install webpack-dashboard --save-dev 2. 配置说明 webpack config // Import the plugin: var Dash ...

  6. 初识用.NET Remoting来开发分布式应用

    一..NET Remoting简介: .NET Remoting从某种意义上讲是DCOM的替代品.ASP.NET Web服务十分有用,但是这项技术在企业内联网的解决方案中,对于某些业务请求来说并不快, ...

  7. PCBA 的收货要求记录

    PCBA 的收货要求记录 性能 功能性测试 外观 标识 需要可以识别的料号(客户料号或货号) 贴片 元件焊点饱满 元件参数统一 后焊 插件焊盘饱满 插件焊盘不可以有小孔 焊盘不可以有漏铜上锡均匀 体积 ...

  8. bzoj 2119 股市的预测 —— 枚举关键点+后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2119 思路就是对于这个形如 ABA 的串,枚举 A 的长度,并按照长度分出几块,找到一些关键 ...

  9. 使用Inno SetUp脚本打包Winform程序

    在开发桌面程序时,往往需要用到打包工具将程序打包为exe可执行文件. 之前在项目中用了下 InstallShield Limited Edition for Visual Studio  2015,它 ...

  10. further occurrences of HTTP header parsing errors will be logged at DEBUG level.

    1.   获取参数Json的值为null String json=request.getParameter("Json"); 首先检查是否有下面的东东, 信息: Error par ...