当前位置:网站首页>[u-boot] main line analysis of u-boot operation [03] - board_ init_ r
[u-boot] main line analysis of u-boot operation [03] - board_ init_ r
2022-07-21 06:04:00 【iriczhao】
《u-boot Operation mainline analysis 【03】—board_init_r》
List of articles
- One 、 The opening
- Two 、board_init_r Function pointer array analysis
- (2.1)initr_trace
- (2.2)initr_reloc
- (2.3)event_init
- (2.4)initr_caches
- (2.5)initr_reloc_global_data
- (2.6)initr_malloc
- (2.7)log_init
- (2.8)initr_bootstage
- (2.9)console_record_init
- (2.10)initr_of_live
- (2.11)initr_dm
- (2.12)board_init
- (2.13)initr_binman
- (2.14)initr_dm_devices
- (2.15)stdio_init_tables
- (2.16)serial_initialize
- (2.17)initr_announce
- (2.18)dm_announce
- (2.19)initr_watchdog
- (2.20) Peripheral initialization operation
- (2.20.1)pci_init
- (2.20.2)power_init_board
- (2.20.3)initr_flash
- (2.20.4)initr_nand
- (2.20.5)initr_onenand
- (2.20.6)initr_mmc
- (2.20.7)initr_env
- (2.20.8)stdio_add_devices
- (2.20.9)jumptable_init
- (2.20.10)api_init
- (2.20.11)console_init_r
- (2.20.12)console_announce_r
- (2.20.13)show_board_info
- (2.20.14)interrupt_init
- (2.20.15)initr_scsi
- (2.20.16)initr_net
- (2.20.17)initr_post
- (2.20.18)initr_ide
- (2.20.19)initr_mem
- (2.21)run_main_loop
- 3、 ... and 、 ending
One 、 The opening
We from 《u-boot Operation mainline analysis 【01】—_main》 In the article , Already know , stay u-boot Start the main line , Will call two very important functions :board_init_f()
and board_init_r()
. Here we use ARM32 Architecture, for example , These two functions will be in /arch/arm/lib/crt0.S In the path file _main
Called under the assembly tag ,board_init_f()
Already in 《u-boot Operation mainline analysis 【02】—board_init_f》 In this paper , This article will analyze board_init_f()
function . stay _main Jump to board_init_r As shown in the following code :
/* Omit a lot of assembly code */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
ldr lr, =board_init_r /* this is auto-relocated! */
bx lr
#else
ldr pc, =board_init_r /* this is auto-relocated! */
#endif
And board_init_f The same function , The use of initcall_run_list()
Traversal cycle init_sequence_r This function pointer array , Then execute the functions in the array in turn . The function is defined as follows (/common/board_r.c):
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
/* Set new global data pointer , up to now , Only x86 To do so */
if (CONFIG_IS_ENABLED(X86_64) && !IS_ENABLED(CONFIG_EFI_APP))
arch_setup_gd(new_gd);
#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
gd = new_gd;
#endif
/* Set up u-boot sign */
gd->flags &= ~GD_FLG_LOG_READY;
/* In almost all cases , Here are all empty operations */
if (IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC)) {
for (int i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
MANUAL_RELOC(init_sequence_r[i]);
}
/* Array of execution function pointers (init_sequence_r call ) */
if (initcall_run_list(init_sequence_r))
hang();
/* Will not run here - run_main_loop() Will not return !!! */
hang();
}
Two 、board_init_r Function pointer array analysis
(2.1)initr_trace
This function call trace_init()
, For initialization trace System and turn it on . The definition is as follows :
static int initr_trace(void)
{
#ifdef CONFIG_TRACE
trace_init(gd->trace_buff, CONFIG_TRACE_BUFFER_SIZE);
#endif
return 0;
}
(2.2)initr_reloc
This function is used to set gd->flags The logo of , Used to mark u-boot Relocation complete , The definition is as follows :
static int initr_reloc(void)
{
/* tell others: relocation done */
gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;
return 0;
}
(2.3)event_init
This function is used to start dynamic events , initialization state->spy_head Linked list , The function is defined in /common/event.S in :
int event_init(void)
{
/* Get gd The event state in state */
struct event_state *state = gd_event_state();
/* initialization state->spy_head Linked list */
INIT_LIST_HEAD(&state->spy_head);
return 0;
}
(2.4)initr_caches
This article takes ARM Architecture, for example , Therefore, this function will be executed . This function is used after the system starts , Turn on cache(I-cache/D-chache). The definition is as follows :
#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
static int initr_caches(void)
{
/* Enable caches */
enable_caches();
return 0;
}
#endif
(2.5)initr_reloc_global_data
This function is used to initialize the relocated gd, The definition is as follows :
static int initr_reloc_global_data(void)
{
/* Set up u-boot Occupied by flash The length of */
#ifdef __ARM__
monitor_flash_len = _end - __image_copy_start;
#elif defined(CONFIG_RISCV)
monitor_flash_len = (ulong)&_end - (ulong)&_start;
#elif !defined(CONFIG_SANDBOX) && !defined(CONFIG_NIOS2)
monitor_flash_len = (ulong)&__init_end - gd->relocaddr;
#endif
#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx)
/* * The gd->cpu pointer is set to an address in flash before relocation. * We need to update it to point to the same CPU entry in RAM. * TODO: why not just add gd->reloc_ofs? */
gd->arch.cpu += gd->relocaddr - CONFIG_SYS_MONITOR_BASE;
/* * If we didn't know the cpu mask & # cores, we can save them of * now rather than 'computing' them constantly */
fixup_cpu();
#endif
#ifdef CONFIG_SYS_RELOC_GD_ENV_ADDR
/* * Relocate the early env_addr pointer unless we know it is not inside * the binary. Some systems need this and for the rest, it doesn't hurt. */
gd->env_addr += gd->reloc_off;
#endif
#ifdef CONFIG_OF_EMBED
/* * The fdt_blob needs to be moved to new relocation address * incase of FDT blob is embedded with in image */
gd->fdt_blob += gd->reloc_off;
#endif
#ifdef CONFIG_EFI_LOADER
/* * On the ARM architecture gd is mapped to a fixed register (r9 or x18). * As this register may be overwritten by an EFI payload we save it here * and restore it on every callback entered. */
efi_save_gd();
efi_runtime_relocate(gd->relocaddr, NULL);
#endif
return 0;
}
(2.6)initr_malloc
This function is used to initialize malloc Memory area , The function is defined as follows :
static int initr_malloc(void)
{
ulong malloc_start;
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
debug("Pre-reloc malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
gd->malloc_ptr / 1024);
#endif
/* malloc The area is DRAM Medium u-boot Below the copy */
/* * The value must be the same as board_f.c in gd->start_addr_sp The value of matches : * reserve_noncached(). */
malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN;
mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN),
TOTAL_MALLOC_LEN);
return 0;
}
(2.7)log_init
This function is used to start log System , For later use . The function is defined in /common/log.c In file :
int log_init(void)
{
struct log_driver *drv = ll_entry_start(struct log_driver, log_driver);
const int count = ll_entry_count(struct log_driver, log_driver);
struct log_driver *end = drv + count;
/* * We cannot add runtime data to the driver since it is likely stored * in rodata. Instead, set up a 'device' corresponding to each driver. * We only support having a single device. */
INIT_LIST_HEAD((struct list_head *)&gd->log_head);
while (drv < end) {
struct log_device *ldev;
ldev = calloc(1, sizeof(*ldev));
if (!ldev) {
debug("%s: Cannot allocate memory\n", __func__);
return -ENOMEM;
}
INIT_LIST_HEAD(&ldev->filter_head);
ldev->drv = drv;
ldev->flags = drv->flags;
list_add_tail(&ldev->sibling_node,
(struct list_head *)&gd->log_head);
drv++;
}
gd->flags |= GD_FLG_LOG_READY;
if (!gd->default_log_level)
gd->default_log_level = CONFIG_LOG_DEFAULT_LEVEL;
gd->log_fmt = log_get_default_format();
gd->logc_prev = LOGC_NONE;
gd->logl_prev = LOGL_INFO;
return 0;
}
(2.8)initr_bootstage
This function is used to mark the startup phase as :board_init_r
.
(2.9)console_record_init
This function is used to set console The record of buffer buffer . This function uses malloc Memory area under .
(2.10)initr_of_live
【 Device tree correlation function 】 This function calls of_live_build()
function , Build an activation from a device tree ( layered ) The tree of .of_live_build()
The output parameter of is the device tree to be converted , Return after creation 、 Active tree .
static int initr_of_live(void)
{
if (CONFIG_IS_ENABLED(OF_LIVE)) {
int ret;
bootstage_start(BOOTSTAGE_ID_ACCUM_OF_LIVE, "of_live");
ret = of_live_build(gd->fdt_blob,
(struct device_node **)gd_of_root_ptr());
bootstage_accum(BOOTSTAGE_ID_ACCUM_OF_LIVE);
if (ret)
return ret;
}
return 0;
}
(2.11)initr_dm
This function is used to initialize the device model :
(1) Save the previous assigned driver model and start a new one
(2) Initialize the device model and scan the device nodes
static int initr_dm(void)
{
int ret;
/* Save the previous assigned driver model and start a new one */
gd->dm_root_f = gd->dm_root;
gd->dm_root = NULL;
#ifdef CONFIG_TIMER
gd->timer = NULL;
#endif
bootstage_start(BOOTSTAGE_ID_ACCUM_DM_R, "dm_r");
/* Initialize the device model and scan the device nodes */
ret = dm_init_and_scan(false);
bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_R);
if (ret)
return ret;
return 0;
}
This function calls dm_init_and_scan()
. This function is used to initialize the structure of the driving model and scan the device . This function initializes driver Trees and uclass The root of the tree , Then from the platform data and FDT Scan and bind available devices . call dm_init()
To build the driving model structure . The parameters passed into this function are false, Indicates that all drivers are bound .
(2.12)board_init
stay ARM,RISCV and SANDBOX Under the circumstances , This function will turn on . This function is a function related to the specific architecture , Used to complete some hardware board level initialization operations .
(2.13)initr_binman
This function is used to set binman Symbol information , In this function call chain struct binman_info
Structured memory , And set relevant information parameters . The function is defined as follows :
static int initr_binman(void)
{
int ret;
if (!CONFIG_IS_ENABLED(BINMAN_FDT))
return 0;
ret = binman_init();
if (ret)
printf("binman_init failed:%d\n", ret);
return ret;
}
(2.14)initr_dm_devices
This function call chain is used to initialize the timer early in the system startup , And initializing multiplexing control to the default state .
The function is defined as follows :
static int initr_dm_devices(void)
{
int ret;
if (IS_ENABLED(CONFIG_TIMER_EARLY)) {
ret = dm_timer_init();
if (ret)
return ret;
}
if (IS_ENABLED(CONFIG_MULTIPLEXER)) {
/* * Initialize the multiplexer controls to their default state. * This must be done early as other drivers may unknowingly * rely on it. */
ret = dm_mux_init();
if (ret)
return ret;
}
return 0;
}
(2.15)stdio_init_tables
This function is used to initialize the standard output device linked list (struct stdio_dev
), The function is defined as follows (/common/stdio.c):
int stdio_init_tables(void)
{
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
/* already relocated for current ARM implementation */
ulong relocation_offset = gd->reloc_off;
int i;
/* relocate device name pointers */
for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {
stdio_names[i] = (char *) (((ulong) stdio_names[i]) +
relocation_offset);
}
#endif /* CONFIG_NEEDS_MANUAL_RELOC */
/* Initialize the linked list of output devices struct stdio_dev */
INIT_LIST_HEAD(&devs.list);
return 0;
}
(2.16)serial_initialize
This function is used to register serial port devices .
(2.17)initr_announce
Print out the operation and debugging information :
static int initr_announce(void)
{
debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr);
return 0;
}
(2.18)dm_announce
Print out the information related to the device model :
static int dm_announce(void)
{
int device_count;
int uclass_count;
if (IS_ENABLED(CONFIG_DM)) {
dm_get_stats(&device_count, &uclass_count);
printf("Core: %d devices, %d uclasses", device_count,
uclass_count);
if (CONFIG_IS_ENABLED(OF_REAL))
printf(", devicetree: %s", fdtdec_get_srcname());
printf("\n");
if (IS_ENABLED(CONFIG_OF_HAS_PRIOR_STAGE) &&
(gd->fdt_src == FDTSRC_SEPARATE ||
gd->fdt_src == FDTSRC_EMBED)) {
printf("Warning: Unexpected devicetree source (not from a prior stage)");
printf("Warning: U-Boot may not function properly\n");
}
}
return 0;
}
(2.19)initr_watchdog
This function is used to initialize the watchdog .
(2.20) Peripheral initialization operation
(2.20.1)pci_init
This function is used to initialize pci, It's defined in /drivers/pci/pci-uclass.c In file :
int pci_init(void)
{
struct udevice *bus;
/* * List all known controller devices ,PCIe Devices will also be enumerated . */
for (uclass_first_device_check(UCLASS_PCI, &bus);
bus;
uclass_next_device_check(&bus)) {
;
}
return 0;
}
(2.20.2)power_init_board
This function is related to the specific architecture and board , Used to initialize the power management framework . This function is a weak function definition , The specific architecture and hardware board need to implement specific corresponding functions .
(2.20.3)initr_flash
The function call chain will call flash_init()
Conduct flash initialization , And print out with flash Relevant information .initr_flash()
The function is defined as follows :
static int initr_flash(void)
{
ulong flash_size = 0;
struct bd_info *bd = gd->bd;
if (!is_flash_available())
return 0;
puts("Flash: ");
if (board_flash_wp_on())
printf("Uninitialized - Write Protect On\n");
else
flash_size = flash_init();
print_size(flash_size, "");
#ifdef CONFIG_SYS_FLASH_CHECKSUM
/* * Compute and print flash CRC if flashchecksum is set to 'y' * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */
if (env_get_yesno("flashchecksum") == 1) {
const uchar *flash_base = (const uchar *)CONFIG_SYS_FLASH_BASE;
printf(" CRC: %08X", crc32(0,
flash_base,
flash_size));
}
#endif /* CONFIG_SYS_FLASH_CHECKSUM */
putc('\n');
/* update start of FLASH memory */
#ifdef CONFIG_SYS_FLASH_BASE
bd->bi_flashstart = CONFIG_SYS_FLASH_BASE;
#endif
/* size of FLASH memory (final value) */
bd->bi_flashsize = flash_size;
#if defined(CONFIG_SYS_UPDATE_FLASH_SIZE)
/* Make a update of the Memctrl. */
update_flash_size(flash_size);
#endif
#if defined(CONFIG_OXC) || defined(CONFIG_RMU)
/* flash mapped at end of memory map */
bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size;
#elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE
bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */
#endif
return 0;
}
(2.20.4)initr_nand
This function call nand_init()
initialization nand, And print out relevant information .
static int initr_nand(void)
{
puts("NAND: ");
nand_init();
printf("%lu MiB\n", nand_size() / 1024);
return 0;
}
(2.20.5)initr_onenand
This function call onenand_init()
Function initialization onenand, The definition is as follows :
static int initr_onenand(void)
{
puts("NAND: ");
onenand_init();
return 0;
}
(2.20.6)initr_mmc
This function call mmc_initialize()
Function initialization mmc, And print out relevant information , The definition is as follows :
static int initr_mmc(void)
{
puts("MMC: ");
mmc_initialize(gd->bd);
return 0;
}
mmc_init()
It's defined in /drivers/mmc/mmc.c In file :
int mmc_initialize(struct bd_info *bis)
{
static int initialized = 0;
int ret;
if (initialized) /* Avoid initializing mmc multiple times */
return 0;
initialized = 1;
#if !CONFIG_IS_ENABLED(BLK)
#if !CONFIG_IS_ENABLED(MMC_TINY)
mmc_list_init();
#endif
#endif
ret = mmc_probe(bis);
if (ret)
return ret;
#ifndef CONFIG_SPL_BUILD
print_mmc_devices(',');
#endif
mmc_do_preinit();
return 0;
}
(2.20.7)initr_env
initr_env Function call chain is used to initialize environment variables , The definition is as follows :
static int initr_env(void)
{
/* Initialize environment variables */
if (should_load_env())
env_relocate();
else
env_set_default(NULL, 0);
env_import_fdt();
if (IS_ENABLED(CONFIG_OF_CONTROL))
env_set_hex("fdtcontroladdr",
(unsigned long)map_to_sysmem(gd->fdt_blob));
#if (CONFIG_IS_ENABLED(SAVE_PREV_BL_INITRAMFS_START_ADDR) || \ CONFIG_IS_ENABLED(SAVE_PREV_BL_FDT_ADDR))
save_prev_bl_data();
#endif
/* Find... From the environment variable loadaddr, And back to 16 Base value */
image_load_addr = env_get_ulong("loadaddr", 16, image_load_addr);
return 0;
}
(2.20.8)stdio_add_devices
This function is used to add stdio Equipment to equipment table , Function defined in (/common/stdio.c) In file .
(2.20.9)jumptable_init
This function uses malloc Allocate memory for jump table , It's defined in /common/exports.c In file .
(2.20.10)api_init
This function initializes for external applications api. It's defined in /api/api.c In file :
int api_init(void)
{
struct api_signature *sig;
/* TODO put this into linker set one day... */
calls_table[API_RSVD] = NULL;
calls_table[API_GETC] = &API_getc;
calls_table[API_PUTC] = &API_putc;
calls_table[API_TSTC] = &API_tstc;
calls_table[API_PUTS] = &API_puts;
calls_table[API_RESET] = &API_reset;
calls_table[API_GET_SYS_INFO] = &API_get_sys_info;
calls_table[API_UDELAY] = &API_udelay;
calls_table[API_GET_TIMER] = &API_get_timer;
calls_table[API_DEV_ENUM] = &API_dev_enum;
calls_table[API_DEV_OPEN] = &API_dev_open;
calls_table[API_DEV_CLOSE] = &API_dev_close;
calls_table[API_DEV_READ] = &API_dev_read;
calls_table[API_DEV_WRITE] = &API_dev_write;
calls_table[API_ENV_GET] = &API_env_get;
calls_table[API_ENV_SET] = &API_env_set;
calls_table[API_ENV_ENUM] = &API_env_enum;
calls_table[API_DISPLAY_GET_INFO] = &API_display_get_info;
calls_table[API_DISPLAY_DRAW_BITMAP] = &API_display_draw_bitmap;
calls_table[API_DISPLAY_CLEAR] = &API_display_clear;
calls_no = API_MAXCALL;
debugf("API initialized with %d calls\n", calls_no);
dev_stor_init();
/* * Produce the signature so the API consumers can find it */
sig = malloc(sizeof(struct api_signature));
if (sig == NULL) {
printf("API: could not allocate memory for the signature!\n");
return -ENOMEM;
}
env_set_hex("api_address", (unsigned long)sig);
debugf("API sig @ 0x%lX\n", (unsigned long)sig);
memcpy(sig->magic, API_SIG_MAGIC, 8);
sig->version = API_SIG_VERSION;
sig->syscall = &syscall;
sig->checksum = 0;
sig->checksum = crc32(0, (unsigned char *)sig,
sizeof(struct api_signature));
debugf("syscall entry: 0x%lX\n", (unsigned long)sig->syscall);
return 0;
}
In the above code calls_table
In fact, it is an array of function pointers :
typedef int (*cfp_t)(va_list argp);
static cfp_t calls_table[API_MAXCALL] = {
NULL, };
(2.20.11)console_init_r
This function is used to console The console is fully initialized to the device
(2.20.12)console_announce_r
console_announce_r
Used for printing on the non serial port console U-Boot Console .
(2.20.13)show_board_info
This function is used to display board level information .
(2.20.14)interrupt_init
This function is an architecture related function , Used to interrupt related initialization operations . It is assumed here that ARM32 For example , The function is defined in /arch/arm/lib/interrupts.c In file :
int interrupt_init(void)
{
/* * setup up stacks if necessary */
IRQ_STACK_START_IN = gd->irq_sp + 8;
enable_interrupts();
return 0;
}
(2.20.15)initr_scsi
This function call scsi_init()
initialization scsi, And print out relevant information , The function is defined as follows :
static int initr_scsi(void)
{
puts("SCSI: ");
scsi_init();
puts("\n");
return 0;
}
(2.20.16)initr_net
This function calls eth_initialize()
Initialize the network , The function is defined as follows :
static int initr_net(void)
{
puts("Net: ");
eth_initialize();
#if defined(CONFIG_RESET_PHY_R)
debug("Reset Ethernet PHY\n");
reset_phy();
#endif
return 0;
}
(2.20.17)initr_post
The function is defined as follows :
static int initr_post(void)
{
post_run(NULL, POST_RAM | post_bootmode_get(0));
return 0;
}
(2.20.18)initr_ide
This function call ide_init()
initialization ide, And print out ide Related information , The function is defined as follows :
static int initr_ide(void)
{
puts("IDE: ");
#if defined(CONFIG_START_IDE)
if (board_start_ide())
ide_init();
#else
ide_init();
#endif
return 0;
}
ide_init Function defined in /drivers/block/ide.c In file .
(2.20.19)initr_mem
This function exports Linux The available memory size of , Considering the protected RAM At the top of memory . The function is defined as follows :
int initr_mem(void)
{
ulong pram = 0;
char memsz[32];
pram = env_get_ulong("pram", 10, CONFIG_PRAM);
sprintf(memsz, "%ldk", (long int)((gd->ram_size / 1024) - pram));
env_set("mem", memsz);
return 0;
}
(2.21)run_main_loop
run_main_loop
by board_int_r The operation executed by the last call of the function , The function is defined as follows :
static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
sandbox_main_loop_init();
#endif
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop();
return 0;
}
main_loop()
Function defined in /common/main.c In file :
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
if (IS_ENABLED(CONFIG_VERSION_VARIABLE))
env_set("ver", version_string); /* set version variable */
cli_init();
if (IS_ENABLED(CONFIG_USE_PREBOOT))
run_preboot_environment_command();
if (IS_ENABLED(CONFIG_UPDATE_TFTP))
update_tftp(0UL, NULL, NULL);
if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) {
/* efi_init_early() already called */
if (efi_init_obj_list() == EFI_SUCCESS)
efi_launch_capsules();
}
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s);
cli_loop();
panic("No CLI available");
}
3、 ... and 、 ending
This paper roughly combs the structure board_init_r()
function . Because Xiaosheng's energy and knowledge are limited , If there is something wrong in the text , Please also comment and make corrections !
边栏推荐
- 【HDLBits 刷题】Verilog Language -- Basics 部分
- 安科瑞马达监控方案低压电动机回路及馈线配电回路电力能源监测
- [postgraduate entrance examination vocabulary training camp] day 9 - vital, dynamity, previous, pray, transit, virile, invent
- 项目启动端口被占用的解决办法
- Redis未授权访问漏洞复现(www.hetianlab.com)
- 提取excel表头
- Maybe it's the next generation data specification of WebGIS - OGC API series
- Redis-String类型
- NepCTF
- MySQL 5.7windows environment installation
猜你喜欢
随机推荐
探索者TSSD 2019软件安装包下载及安装教程
运算表达式的抽象
C DataGridView data export excel file
暑假打工 2 个 月,让我明白了 Keepalived 高可用的三种路由方案
暑假打工 2 个 月,让我明白了 Keepalived 高可用的三种路由方案
arthas 案例: 动态更新应用Logger Level
经过6年沉淀,新消费找到了数字化转型范式
STM32+DHT11读取温湿度数据显示
C语言文件操作函数讲解
你是为了什么而努力?不放弃才是我们唯一的选择,加油,每一个有缘人
look out! A "pit" matched by regular test()
Introduction to message queuing
煤炭行业供应链集采系统:数字化推进煤炭产业转型升级
如何确定自己的研究方向 (读后感)
提取excel表头
子网掩码的作用
MATLAB画雷达图(四行代码)
消息队列简介
Reading Ming Dynasty 1566
File文件改名字