技术标签: ARM Linux of Kernel u-boot kernel ARM Linux of U-boot Uboot 移植 linux kernel Android Code Transplant
U-BOOT全线移植分析系列之四
――U-boot如何引导Linux内核启动?
Sailor_forever [email protected] 转载请注明
http://blog.csdn.net/sailor_8318/archive/2008/08/05/2773412.aspx
【摘要】本节介绍了U-boot使用Go或bootm启动linux内核的方法。首先介绍了mkimage的参数意义和bootm的详细执行流程。然后分析了如何利用mkimage生成内核映象的方法。对于bootm方式的内核是否压缩、-a、-e、运行地址等16种组合情况,给出了详细的测试过程,提出了6种可用方法种的三种最优解。
【关键字】:U-boot;AT91RM9200;bootm;mkimage;-a;-e;-c
1) 运行地址!=链接地址0x20008000,不能启动
Uboot> tftp 21000000 Image;tftp 21100000 ramdisk;go 21000000
。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Error: a 在哪提示的?
2) 运行地址=链接地址0x20008000,不能启动,难道是ramdisk的问题
Uboot> tftp 20008000 Image;tftp 21100000 ramdisk;go 20008000
。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Error: a
1) 运行地址!=链接地址0x20008000,能启动,内核自解压成功,但是解压后的内核运行错误
Uboot> tftp 21000000 zImage;tftp 21100000 ramdisk;go 21000000
。。。。。。。。。。。
done
Bytes transferred = 6993691 (6ab71b hex)
## Starting application at 0x21000000 ...
Uncompressing Linux............................................................. done, booting the kernel.
€?~??鄜屈
2) 运行地址==链接地址0x20008000,能启动,内核自解压成功,但是解压后的内核运行错误
Uboot> tftp 20008000 zImage;tftp 21100000 ramdisk; go 20008000
## Starting application at 0x20008000 ...
Uncompressing Linux............................................................. done, booting the kernel.
€?~??鄜屈
上面的ramdisk都是添加了uboot的头的,去掉头部再试试。去掉了还是不行,go方法的ramdisk的地址是怎么设置的??要详细看下uboot在ramdisk这块是如何跟内核交互的?
通过mkimage这个tool可以给zImage添加一个header:
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
此header是如何生成的?利用u-boot里面的mkimage工具来生成uImage (u-boot源码包/tools/mkimage.c )
这里解释一下参数的意义:
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type' “kernel或是ramdisk”
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)(内核启动时在此位置查询完整的内核印象)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place,即不进行文件的拷贝,在当前位置执行)
对于ARM linux内核映象用法:
-A arm -------- 架构是arm
-O linux -------- 操作系统是linux
-T kernel -------- 类型是kernel
-C none/bzip/gzip -------- 压缩类型
-a 20008000 ---- image的载入地址(hex),通常为0xX00008000
-e 200080XX---- 内核的入口地址(hex),XX为0x40或者0x00
-n linux-XXX --- image的名字,任意
-d nameXXX ---- 无头信息的image文件名,你的源内核文件
uImageXXX ---- 加了头信息之后的image文件名,任意取
Bootm命令在/common/cmd_bootm.c中do_bootm函数
》》》》》》》》》》》获取当前内核的地址,默认地址或者bootm的第一个参数
默认的加载地址或传递给bootm命令(优先)与实际的内核存放地址需要一致
if (argc < 2) {
addr = load_addr; // load_addr = CFG_LOAD_ADDR;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
printf ("## Booting image at %08lx .../n", addr);
》》》》》》》》》》》》获得image头,没有mkimage的就返回了
memmove (&header, (char *)addr, sizeof(image_header_t));
》》》》》》》》》》》》打印头部信息
print_image_hdr ((image_header_t *)addr);
实例:
Image Name: dd-kernel-2.4.19
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869574 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008000
》》》》》》》》》》》》校验image头部
printf (" Verifying Checksum ... "); printf ("OK/n");
》》》》》》》》》》》》检查image支持的体系结构即—A 选项是否为arm或者ppc等
》》》》》》》》》》》》检查image的类型
TYPE_MULTI 是否指内核与文件系统一起,内核后面有个分界线
switch (hdr->ih_type)
case IH_TYPE_KERNEL:
name = "Kernel Image";
break;
case IH_TYPE_MULTI:
》》》》》》》》》》判断内核的压缩类型
此处的内核是否压缩非zImage和Image的概念,而是指内核在被mkimage处理前是否用gunzip等压缩过
switch (hdr->ih_comp) {
case IH_COMP_NONE: // 非压缩内核
if(ntohl(hdr->ih_load) == addr) {
// 当前内核存放的地址与-a指定的一致,则不搬动,-e必须必-a大0x40
printf (" XIP %s ... ", name);
} else {
//当前内核存放的地址与-a指定的不一致,则将内核搬到-a地址,此时-a与-e必相同
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
。。。。
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
//压缩内核,将除去头部的内核解压到-a 指定的地址了,要求-a与-e相同
// 为防止解压缩时覆盖,对于压缩内核,内核存放地址最好在—a后面
(uchar *)data, (int *)&len) != 0) {
do_reset (cmdtp, flag, argc, argv);
}
break;
》》》》》》》》》》》》》》》》判断操作系统类型
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify);
//前四个为传给bootm的,addr为内核最初的存放地址,没有用处
break;
#ifdef CONFIG_PPC
static boot_os_Fcn do_bootm_linux;
#else
extern boot_os_Fcn do_bootm_linux;
由上可知,对于ppc和其他体系结构的do_bootm_linux函数实现是不一样的
》》》》》》》》》》》》》》启动Linux内核
do_bootm_linux (cmd_tbl_t *cmdtp, int flag,
int argc, char *argv[],
ulong addr,
ulong *len_ptr,
int verify)
》》》》》》》》》》》》获取命令行参数
if ((s = getenv("bootargs")) == NULL)
s = "";
strcpy (cmdline, s);
》》》》》》》》》》》》赋内核启动地址
kernel = (void (*)(bd_t *, ulong, ulong, ulong, ulong))hdr->ih_ep;
注意,对于压缩过的内核,会将内核解压到-a指定的地址,此时-a 与-e 地址必须相同
》》》》》》》》》》》判断bootm的命令参数中是否有initrd
if (argc >= 3) {
addr = simple_strtoul(argv[2], NULL, 16);
printf ("## Loading RAMDisk Image at %08lx .../n", addr);
若有initrd则赋值,否则为0
》》》》》》》》》》》》》》》启动Linux内核
/*
* Linux Kernel Parameters:
* r3: ptr to board info data
* r4: initrd_start or 0 if no initrd
* r5: initrd_end - unused if r4 is 0
* r6: Start of command line string
* r7: End of command line string
*/
//*kbd = *(gd->bd); 在上面赋值的
(*kernel) (kbd, initrd _start, initrd_end, cmd_start, cmd_end);
启动流程的总结:
对于非gzip压缩的内核,bootm命令会首先判断bootm xxxx 这个指定的地址xxxx是否与-a指定的加载地址相同。
(1)如果不同的话会从这个地址开始提取出这个64byte的头部,对其进行分析,然后把去掉头部的内核复制到-a指定的load地址中去运行之(此时-e选型必须同-a)
(2)如果相同的话那就让其原封不动的放在那,但-e指定的入口地址会推后64byte,以跳过这64byte的头部。
对于gzip压缩过的内核,因为u-boot要对其解压,因此运行地址是不能等于-a指定的地址的,且必须有一定的间隔,否则解压到-a的内核会覆盖当前运行的程序。此时要求-a等于-e指定的地址。
1> mkimage 如何指定入口参数 ( -e 0xxxxxx)
2> mkimage 指定了入口参数后, 你用tftpboot 下载kernel到哪个地址?
3> -c 如何指定?
u-boot里面的解压和内核自解压的区别: u-boot 里面的解压实际上是bootm 实现的 , 把 mkimage -C bzip2或者gzip 生成的 uImage进行解压 ; 而kernel的自解压是对zImage进行解压,发生在bootm解压之后。
U-boot 对内核添加头部时,前面已经用gzip压缩过一次内核了,而不是指原有的内核印象是否是压缩内核。指uImage 本身被压缩了,即对原来的zImage/Image添加了U-boot的压缩方式,使得生成的uImage变小了。此时-c gzip
若没有对zImage/Image用gzip命令压缩过,则-c none。
综合上面分析,mkimage的影响因子为:
-e,内核的入口地址是否与-a相同
Tftpaddr,即将内核加载到RAM中运行的地址,决定是否搬运或解压内核
-c,内核是否经过gzip压缩过,决定了是搬运还是解压
另外内核本身为非压缩的Image或zImage也是一个影响因子。组合情况共2^4 =16种
(1)Mkimage 之前用gzip对Image进行压缩
<1> -a=-e = 0x20008000,tftpaddr= 0x21000000
解压到-a指定的地址,成功启动
Uboot> tftp 21000000 uImage-zip-8000;tftp 21100000 ramdisk;bootm 21000000
## Booting image at 21000000 ...
Image Name: dd-kernel-2.4.19-zip-8000
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869629 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
Starting kernel ...
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #42 四 10月 11 14:15:35 CST 2007
AT91RM9200DK login: root
<2> -a=-e = 0x20008000, tftpaddr= 0x20008000
解压失败,启动失败
Uboot> tftp 20008000 uImage-zip-zImage-8000;tftp 21100000 ramdisk;bootm 20008000
Uboot> tftp 20008000 uImage-zip-8000;tftp 21100000 ramdisk;bootm 20008000
## Booting image at 20008000 ...
Image Name: dd-kernel-2.4.19-zip-8000
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869629 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
Uncompressing Kernel Image ... Error: inflate() returned -3
GUNZIP ERROR - must RESET board to recover
由于当前运行地址tftpaddr与解压缩后的地址-a重合了,导致解压缩失败,因此二者必须相隔一定的距离
<3> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x21000000
能够解压到-a地址,但-e指定的入口不对,启动失败
Uboot> tftp 21000000 uImage-zip-8040;tftp 21100000 ramdisk;bootm 21000000
TFTP from server 192.168.0.12; our IP address is 192.168.0.15
Filename 'uImage-zip-8040'.
Load address: 0x21000000
。。。。。。。。。
## Booting image at 21000000 ...
Image Name: dd-kernel-2.4.19-zip-8040
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 869629 Bytes = 849.2 kB
Load Address: 20008000
Entry Point: 20008040
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
Starting kernel ... 死了
<4> -a=-e = 0x20008000, tftpaddr= 0x20008000
解压失败,入口也不对,启动失败
(2)Mkimage 之前未对Image进行压缩
<1> -a=-e = 0x20008000 tftpaddr= 0x21000000
搬动到-a指定的地址,成功启动
Uboot> tftp 21000000 uImage-nzip-8000;tftp 21100000 ramdisk;bootm 21000000
## Booting image at 21000000 ...
Image Name: dd-kernel-2.4.19-nzip-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1873059 Bytes = 1.8 MB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... Bad Data CRC
为什么总是校验失败呢?当前的内核印象为1.8M,难道太大了,后面的ramdisk将其覆盖??
下面未拷贝ramdisk,校验成功,成功启动,无法安装跟文件系统,是因为无ramdisk。说明上面确实是覆盖了,因此要对于大的内核印象要合理设置tftpaddr的地址和ramdisk的地址
Addr(ramdisk)= 0x2110 0000
Addr(tftpaddr)= 0x2100 0000
Addr(ramdisk)-Addr(tftpaddr)= 0x10 0000 = 1M < 1.8M
Uboot> tftp 21000000 uImage-nzip-8000;bootm 21000000
## Booting image at 21000000 ...
Image Name: dd-kernel-2.4.19-nzip-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1873059 Bytes = 1.8 MB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
OK
Starting kernel ...
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #44 四 10月 11 17:27:24 CST 2007
。。。。。。。
Kernel panic: VFS: Unable to mount root fs on 01:00
Addr(ramdisk)- 2M = 0x20f0 0000 = Addr(tftpaddr)成功启动
Uboot> tftp 20f00000 uImage-nzip-8000;tftp 21100000 ramdisk;bootm 20f00000
## Booting image at 20f00000 ...
Image Name: dd-kernel-2.4.19-nzip-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1873059 Bytes = 1.8 MB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
OK
Starting kernel ...
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #44 四 10月 11 17:27:24 CST 2007
AT91RM9200DK login: root
<2> -a=-e = 0x20008000, tftpaddr= 0x20008000
不搬动,但-e地址不对,失败
<3> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x20008000
不搬动,但未成功启动,入口地址对的啊?????
Uboot> tftp 20008000 uImage-nzip-8040;tftp 21100000 ramdisk;bootm 20008000
## Booting image at 20008000 ...
Image Name: dd-kernel-2.4.19-nzip-8040
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1873059 Bytes = 1.8 MB
Load Address: 20008000
Entry Point: 20008040
Verifying Checksum ... OK
XIP Kernel Image ... OK
Starting kernel ...死了????
<4> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x21000000
搬动,但-e地址不对,失败
(1)Mkimage 之前用gzip对zImage进行压缩,即-c gzip
<1> -a=-e = 0x20008000,tftpaddr= 0x21000000
解压到-a指定的地址,成功启动
Uboot> tftp 21000000 uImage-zip-zImage-8000;tftp 21100000 ramdisk;bootm 21000000
TFTP from server 192.168.0.12; our IP address is 192.168.0.15
Filename 'uImage-zip-zImage-8000'.
Load address: 0x21000000
## Booting image at 21000000 ...
Image Name: dd-zip-zImage-8000
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 876753 Bytes = 856.2 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK // U-boot对内核解压
Starting kernel ...
Uncompressing Linux..............压缩内核zImage自解压......................... done, booting the kernel.
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #43 四 10月
AT91RM9200DK login: root
[root@AT91RM9200DK /root]$ls
<2> -a=-e = 0x20008000, tftpaddr= 0x20008000
解压失败,启动失败
Uboot> tftp 20008000 uImage-zip-zImage-8000;tftp 21100000 ramdisk;bootm 20008000
TFTP from server 192.168.0.12; our IP address is 192.168.0.15
Filename 'uImage-zip-zImage-8000'.
Load address: 0x20008000
## Booting image at 20008000 ...
Image Name: dd-zip-zImage-8000
Image Type: ARM Linux Kernel Image (gzip compressed)
Data Size: 876753 Bytes = 856.2 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
Uncompressing Kernel Image ... Error: inflate() returned -3
GUNZIP ERROR - must RESET board to recover
由于当前运行地址tftpaddr与解压缩后的地址-a重合了,导致解压缩失败,因此二者必须相隔一定的距离
<3> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x21000000,失败
Uboot> tftp 21000000 uImage-zip-zImage-8040;tftp 21000000 ramdisk;bootm 21000000
TFTP from server 192.168.0.12; our IP address is 192.168.0.15
Filename 'uImage-zip-zImage-8040'.
Load address: 0x21000000
。。。。。。。。。
## Booting image at 21000000 ...
Bad Magic Number 难道对于压缩内核,幻数对的条件是-a==-e地址??即压缩内核默认-a==-e??
此法肯定失败,但问题出在这,还真不对啊,不试了,感兴趣的朋友可以玩下
(2)Mkimage 之前未对zImage进行压缩,即-c none
<1> -a=-e = 0x20008000 tftpaddr= 0x21000000
搬动到-a指定的地址,成功启动
Uboot> tftp 21000000 uImage-nzip-zImage-8000;tftp 21100000 ramdisk;bootm 21000000
## Booting image at 21000000 ...
Image Name: dd-nzip-zImage-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 881748 Bytes = 861.1 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
OK
Starting kernel ...
Uncompressing Linux............................................................. done, booting the kernel.
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #43 四 10月 11 14:25:14 CST 2007
AT91RM9200DK login:
<2> -a=-e = 0x20008000, tftpaddr= 0x20008000
不搬动,但-e地址不对,失败
Uboot> tftp 20008000 uImage-nzip-zImage-8000;tftp 21100000 ramdisk;bootm 20008000
## Booting image at 20008000 ...
Image Name: dd-nzip-zImage-8000
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 881748 Bytes = 861.1 kB
Load Address: 20008000
Entry Point: 20008000
Verifying Checksum ... OK
XIP Kernel Image ... OK
Starting kernel ... 死了。。。
<3> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x20008000
不搬动,成功启动
Uboot> tftp 20008000 uImage-nzip-zImage-8040;tftp 21100000 ramdisk;bootm 20008000
## Booting image at 20008000 ...
Image Name: dd-nzip-zImage-8040
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 881748 Bytes = 861.1 kB
Load Address: 20008000
Entry Point: 20008040
Verifying Checksum ... OK
XIP Kernel Image ... OK
Starting kernel ...
Uncompressing Linux............................................................. done, booting the kernel.
Linux version 2.4.19-rmk7 (root@dding) (gcc version 2.95.3 20010315 (release)) #43 四 10月 11 14:25:14 CST 2007
AT91RM9200DK login:
<4> -a=0x20008000,-e = 0x20008040 ,tftpaddr= 0x21000000
搬动,但-e地址不对,失败
由上面的16个例子,我们可以看出,能够启动内核的由以下几种情况:
各种情况对应的统一ramdiskaddr= 0x21100000
<1>非压缩的Image内核:
-a=-e = 0x20008000 ,–c=none,tftpaddr= 0x20f00000
此法主要由于内核太大,导致tftpaddr做了一定的修正
-a= 0x20008000 ,-e = 0x20008040,–c=none,tftpaddr=0x20008000
此法理论上可行,但我未试验成功,有兴趣的朋友可以探究下
对于非压缩的Image内核,mkimage之前不压缩的话,内核印象较大,此法不常用
-a=-e = 0x20008000 ,–c=gzip,tftpaddr= 0x21000000
–c=gzip压缩内核必须解压,只有这种情况成功;其他解压覆盖或者-e入口不对
<2>压缩的zImage内核:
-a=-e = 0x20008000 ,–c=none,tftpaddr= 0x21000000
-a= 0x20008000 ,-e = 0x20008040,–c=none,tftpaddr=0x20008000
-a=-e = 0x20008000 ,–c=gzip,tftpaddr= 0x21000000
–c=gzip压缩内核必须解压,只有这种情况成功;其他解压覆盖或者-e入口不对
zImage已经压缩过一次了,一般无需再压缩,此法不常用
常见方法:
<1>非压缩的Image内核:
-a=-e = 0x20008000 ,–c=gzip,tftpaddr= 0x21000000
<2>压缩的zImage内核:
-a=-e = 0x20008000 ,–c=none,tftpaddr= 0x21000000
-a= 0x20008000 ,-e = 0x20008040,–c=none,tftpaddr=0x20008000
待续:
U-boot如何向Linux内核传递命令行参数?
Go引导内核的详细方法?
Ramdisk与initrd怎么传给内核?
文章浏览阅读612次,点赞17次,收藏8次。你知道在Selenium中如何模拟用户行为来规避网站检测吗?本文将揭示一些不为人知的技巧和策略,帮助新手使用Selenium自动化工具时避免被网站检测到,提高自动化测试的稳定性和可靠性。
文章浏览阅读39次。功能最强在的编辑器——vi vi是所有UNIX系统都会提供的屏幕编辑器,它提供了一个视窗设备,通过它可以编辑文件。当然,对UNIX系统略有所知的人, 或多或少都觉得vi超级难用,但vi是最基本的编辑器,所以希望读者能好好把它学起来,以后在UNIX世界里必将畅行无阻、 游刃有余,因为其他几种文本处理器并非UNIX标准配备。说不定别人的Linux机器没安装joe或pico,..._linux vi时候 written
文章浏览阅读2.3k次。火山图是散点图的一种,它将统计测试中的统计显著性量度(如p value)和变化幅度相结合,从而能够帮助快速直观地识别那些变化幅度较大且具有统计学意义的数据点(基因等)。常应用于转录组研究,也能应用于基因组,蛋白质组,代谢组等统计数据。所以关注火山图(其它类型图也是),先理解每个点是什么(点代表基因、样品、通路或其它的,这个认识可以来自于常识,更准确的是看作者的描述),然后看横轴代表什么、纵轴代表..._volcano plot
文章浏览阅读615次。区块链作为一种崭新的、颠覆性的技术,是国内外活跃的研究领域和毕业设计选题方向。 本文列出最新的一组区块链方面的论文,希望可以对选择区块链毕业设计的同学们有所帮助, 这是汇智网编辑整理的区块链毕业设计论文系列中的第11篇。区块链相关链接: - 以太坊智能合约与Dapp入门教程 - 以太坊去中心化电商Dapp实战教程 - 以太坊ERC721数字资产实战教程 - 比特币数据分析ETL工具 - Fabri..._区块链大作业及源代码
文章浏览阅读3.4k次,点赞3次,收藏27次。HTML、CSS精美按钮输入框图形样式_好看的输入框样式
文章浏览阅读2k次,点赞2次,收藏4次。回答问题可以的。变量修改的本质是存储空间中的数据被改变,我们通过变量名、指针等方式改变数据都是找到数据的存储位置,对存储空间进行操作。换句话说,空间的存储位置决定了一个变量能不能被修改。局部变量存放在栈区,程序运行完毕释放内存。而加了const修饰之后,,我们不可以使用变量名对这块内存进行修改,却可以通过其他方式对修改内存从而达到对变量的修改。全局变量存放在数据段,加const修饰之后,存放在只读数据段,是无法对内存进行操作的。所以一个数据能不能被修改,还是看它存放的位置。变量名和变量我们首_c++ static const mutable
文章浏览阅读713次,点赞17次,收藏18次。这篇文章的目的是了解嵌入式Linux的四个组成部分 —— 工具链、引导加载程序、内核、根文件系统 —— 通过使用最少的代码从头开始启动 Raspberry Pi 4 的命令。
文章浏览阅读1k次。一.测试自动化的内涵(1)手工测试的局限性通过手工测试无法做到覆盖所有代码路径; 许多与时序、死锁、资源冲突、多线程等有关的错误通过手工测试很难捕捉到 在系统负载、性能测试时,需要模拟大量数据、或大量并发用户等各种应用场合时,也很难通过手工测试来进行 在进行系统可靠性时,需要模拟系统运行十年、几十年,以验证系统能否稳定运行,也是手工测试无法模拟的。 如果有大量(几千)的测试用例,需要在短时间内完成,手工测试无法做到 测试可以发现错误,并不能表明程序的正确性。 代码全部Code Path测_什么是使用自动化测试的主要特征
文章浏览阅读1.8k次,点赞53次,收藏41次。总之,智慧灯杆通过传感器感知环境信息,采集和处理数据,并根据结果控制灯杆的开关、亮度调节等操作,同时能够与其他设备或网络进行通信和数据交互,实现远程管理和监控。它能够通过传感器感知城市的环境数据,如空气质量、温度、湿度、噪音等,实时获取和监测城市的状态,并传输给城市管理者进行分析和决策。智慧灯杆是一种集成了多种智能功能的新型灯杆,它通过将传感器、通信设备、LED灯源、信息显示屏等技术融入到灯杆中,实现了智能化的城市管理和服务。充电功能:智慧灯杆可以配备充电插座,供人们充电使用,如手机充电、电动车充电等。_智慧灯杆
文章浏览阅读1.5k次。Start Emacs是一个功能相对较多而且很复杂的texteditor,所以想要熟练使用必须要对emacs的一些基本概念进行了解,了解这些概念最好的教程就是emacs自带的 “Emacs _emacs c++代码浏览插件
文章浏览阅读845次,点赞16次,收藏18次。登录注册功能基于Shiro实现RBAC模型的权限控制系统, 分为普通用户, 社团管理员, 系统管理员三个角色实现会员的增加, 删除, 修改, 搜索查询功能实现物品的信息管理功能支持对活动信息进行维护, 实现增删改查的功能能够对社团信息进行维护管理。
文章浏览阅读55次。在主循环中,通过调用 sm.tx_fifo(data) 方法将单个字节的数据写入状态机的发送 FIFO 中。这是一个利用状态机的 rp2.StateMachine.put() 方法来向 TX FIFO 中写入数据,以及 rp2.StateMachine.get() 方法来从 RX FIFO 中读取数据的例子。当需要在程序中控制状态机的数据写入速度或频率时,例如在使用状态机模拟高速或高频的 I/O 行为时,根据 TX FIFO 中的字数来决定何时调用 StateMachine.put() 方法。