Linux 内核启动分析
1. 内核启动地址
1.1. 名词解释
ZTEXTADDR
解压代码运行的开始地址。没有物理地址和虚拟地址之分,因为此时MMU处于关闭状态。这个地址不一定时RAM的地址,可以是支持读写寻址的flash等存储中介。
Start address of decompressor. here's no point in talking about virtual or physical addresses here, since the MMU will be off at the time when you call the decompressor code. You normally call the kernel at this address to start it booting. This doesn't have to be located in RAM, it can be in flash or other read-only or read-write addressable medium.
ZRELADDR
内核启动在RAM中的地址。压缩的内核映像被解压到这个地址,然后执行。
This is the address where the decompressed kernel will be written, and eventually executed. The following constraint must be valid:
__virt_to_phys(TEXTADDR) == ZRELADDR
The initial part of the kernel is carefully coded to be position independent.
TEXTADDR
内核启动的虚拟地址,与ZRELADDR相对应。一般内核启动的虚拟地址为RAM的第一个bank地址加上0x8000。
TEXTADDR = PAGE_OFFSET + TEXTOFFST
Virtual start address of kernel, normally PAGE_OFFSET + 0x8000.This is where the kernel image ends up. With the latest kernels, it must be located at 32768 bytes into a 128MB region. Previous kernels placed a restriction of 256MB here.
TEXTOFFSET
内核偏移地址。在arch/arm/makefile中设定。
PHYS_OFFSET
RAM第一个bank的物理起始地址。
Physical start address of the first bank of RAM.
PAGE_OFFSET
RAM第一个bank的虚拟起始地址。
Virtual start address of the first bank of RAM. During the kernel
boot phase, virtual address PAGE_OFFSET will be mapped to physical
address PHYS_OFFSET, along with any other mappings you supply.
This should be the same value as TASK_SIZE.
1.2. 内核启动地址确定
内核启动引导地址由bootp.lds决定。 Bootp.lds : arch/arm/bootp
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0;
.text : {
_stext = .;
*(.start)
*(.text)
initrd_size = initrd_end - initrd_start;
_etext = .;
}
}
由上 .= 0可以确定解压代码运行的开始地址在0x0的位置。ZTEXTADDR的值决定了这个值得选取。
Makefile : arch/arm/boot/compressed
如果设定内核从ROM中启动的话,可以在make menuconfig 的配置界面中设置解压代码的起始地址,否则解压代码的起始地址为0x0。实际上,默认从ROM启动时,解压代码的起始地址也是0x0。
feq ($(CONFIG_ZBOOT_ROM),y)
ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT)
ZBSSADDR := $(CONFIG_ZBOOT_ROM_BSS)
else
ZTEXTADDR :=0 ZBSSADDR := ALIGN(4)
endif
SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/
……
$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/mach-s3c2410/Makefile .config
@sed "$(SEDFLAGS)" < $< > $@
@sed "$(SEDFLAGS)" < $< > $@ 规则将TEXT_START设定为ZTEXTADDR。TEXT_START在arch/arm/boot/compressed/vmlinux.lds.in 中被用来设定解压代码的起始地址。
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = TEXT_START;
_text = .;
.text : {
_start = .;
*(.start)
*(.text)
*(.text.*)
……
}
}
内核的编译依靠vmlinux.lds,vmlinux.lds由vmlinux.lds.s 生成。从下面代码可以看出内核启动的虚拟地址被设置为PAGE_OFFSET + TEXT_OFFSET,而内核启动的物理地址ZRELADDR在arch/arm/boot/Makefile中设定。
OUTPUT_ARCH(arm)
ENTRY(stext)
SECTIONS
{
#ifdef CONFIG_XIP_KERNEL
. = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
#else
. = PAGE_OFFSET + TEXT_OFFSET;
#endif
.init : { /* Init code and data */
_stext = .;
_sinittext = .;
*(.init.text)
_einittext = .;
……
}
}
# arch/arm/boot/Makefile
# Note: the following conditions must always be true:
# ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
# PARAMS_PHYS must be within 4MB of ZRELADDR
# INITRD_PHYS must be in RAM
ZRELADDR := $(zreladdr-y)
#---> zrealaddr-y is specified with 0x30008000 in arch/arm/boot/makefile.boot
PARAMS_PHYS := $(params_phys-y)
INITRD_PHYS := $(initrd_phys-y)
export ZRELADDR INITRD_PHYS PARAMS_PHYS
通过下面的命令编译内核映像,由参数-a, -e设置其入口地址为ZRELADDR,此值在上面ZRELADDR := $(zreladdr-y)指定。
quiet_cmd_uimage= UIMAGE $@
cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \
-C none -a $(ZRELADDR) -e $(ZRELADDR) \
-n 'Linux-$(KERNELRELEASE)' -d $< $@
1.3. 小结
从上面分析可知道,linux内核被bootloader拷贝到RAM后,解压代码从ZTEXTADDR开始运行(这段代码是与位置无关的PIC)。内核被解压缩到ZREALADDR处,也就是内核启动的物理地址处。相应地,内核启动的虚拟地址被设定为TEXTADDR,满足如下条件:
TEXTADDR = PAGE_OFFSET + TEXT_OFFSET
内核启动的物理地址和虚拟地址满足入下条件:
ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)= virt_to_phys(TEXTADDR)
假定开发板为smdk2410,则有:
内核启动的虚拟地址
TEXTADDR = 0xC0008000
内核启动的物理地址
ZRELADDR = 0x30008000
如果直接从flash中启动还需要设置ZTEXTADDR地址。
2. 内核启动过程分析
内核启动过程经过大体可以分为两个阶段:内核映像的自引导;linux内核子模块的初始化。
2.1. 内核映像的自引导
这阶段的主要工作是实现压缩内核的解压和进入内核代码的入口。
Bootloader完成系统引导后,内核映像被调入内存指定的物理地址ZTEXTADDR。典型的内核映像由自引导程序和压缩的VMlinux组成。因此在启动内核之前需要先把内核解压缩。内核映像的入口的第一条代码就是自引导程序。它在arch/arm/boot/compressed/head.S文件中。
Head.S文件主要功能是实现压缩内核的解压和跳转到内核vmlinux内核的入口。Decompress_kernel(): arch/arm/boot/compressed/misc.c 和call_kernel这两个函数实现了上述功能。在调用decompress_kernel()解压内核之前,需要确保解压后的内核代码不会覆盖掉原来的内核映像。以及设定内核代码的入口地址ZREALADDR。
.text
adr r0, LC0
ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}
.type LC0, #object
LC0: .word LC0 @ r1
.word __bss_start @ r2
.word _end @ r3
.word zreladdr @ r4
.word _start @ r5
.word _got_start @ r6
.word _got_end @ ip
.word user_stack+4096 @ sp
上面这段代码得到内核代码的入口地址,保存在r4中。
/*
* Check to see if we will overwrite ourselves.
* r4 = final kernel address
* r5 = start of this image
* r2 = end of malloc space (and therefore this image)
* We basically want:
* r4 >= r2 -> OK
* r4 + image length <= r5 -> OK
*/
cmp r4, r2
bhs wont_overwrite
add r0, r4, #4096*1024 @ 4MB largest kernel size
cmp r0, r5
bls wont_overwrite
mov r5, r2 @ decompress after malloc space
mov r0, r5
mov r3, r7
bl decompress_kernel
b call_kernel
上面代码判断解压后的内核代码会不会覆盖原来的内核映像,然后调用内核解压缩函数decompress_kernel()。
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; /* 指定内核执行地址,保存在r4中*/
free_mem_ptr = free_mem_ptr_p;
free_mem_ptr_end = free_mem_ptr_end_p;
__machine_arch_type = arch_id;
arch_decomp_setup(); /*解压缩前的初始化和设置,包括串口波特率设置等*/
makecrc(); /*CRC校验*/
putstr("Uncompressing Linux...");
gunzip(); /*调用解压缩函数*/
putstr(" done, booting the kernel.\n");
return output_ptr;
}
把内核映像解压到ZERALADDR地址后,调用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
我们知道r4寄存器内保存的是内核的执行地址,mov pc, r4使得程序指针指向了内核的执行地址,所以下面将进入内核代码执行阶段。
2.2. linux内核子模块的初始化
2.2.1. 预备工作
进入真正的内核代码,首先执行的也是一个叫做head.S(arch/arm/kernel/)的文件。同时head.S也包含了同目录下head-common.S(arch/arm/kernel/)。这两个文件联合起来主要负责下面几项工作:
判断CPU类型,查找运行的CPU ID值与Linux编译支持的ID值是否支持
判断体系类型,查看R1寄存器的Architecture Type值是否支持
创建页表
开启MMU
跳转到start_kernel()(内核子模块初始化程序)
注: 暂时不对各个子程序实现作细节性的分析。
2.2.2. 内核各子模块初始化
Start_kernel函数是Linux内核通用的初始化函数。无论对于什么体系结构的Linux,都要执行这个函数。Start_kernel()函数是内核初始化的基本过程。下面按照函数对内核模块初始化的先后顺序进行分析。
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
smp_setup_processor_id(); /*指定当前的cpu的逻辑号,这个函数对应于对称多处理器的设置,当系统中只有一个cpu的情况,此函数为空,什么也不做*/
lockdep_init(); /* 初始化lockdep hash 表 */
/* 初始化irq */
local_irq_disable();
early_boot_irqs_off();
early_init_irq_lock_class();
/* 锁定内核、设置cpu的状态为’present’,’online’等状态、初始化页表、打印内核版本号等信息、设置体系结构、为cpu分配启动内存空间 ,在此期间中断仍然处于关闭状态*/
lock_kernel();
boot_cpu_init();
page_address_init();
printk(KERN_NOTICE);
printk(linux_banner);
setup_arch(&command_line);
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
/*在打开任何中断之前打开调度器 */
sched_init();
/*关闭任务抢占功能,因为早期的调度器功能比较脆弱,直到第一次调用cpu_idle()*/
preempt_disable();
/* 建立内存区域链表节点,对于单cpu节点数为1 */
build_all_zonelists();
/* 发通知给每个CPU,处理每个CPU的内存状态*/
page_alloc_init();
/* 分析早期没命令参数*/
printk(KERN_NOTICE "Kernel command line: %s\n", saved_command_line);
parse_early_param();
/* 分析命令参数 */
parse_args("Booting kernel", command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
/* 排序内核创建的异常表 */
sort_main_extable();
unwind_init();
/*设置陷阱门和中断门 */
trap_init();
/*初始化内核中的读-拷贝-更新(Read-Copy-Update RCU)子系统 */
rcu_init();
/*初始化IRQ */
init_IRQ();
/* 按照开发办上的物理内存初始化pid hash表 */
pidhash_init();
/*初始化计时器 */
init_timers();
/* 高解析度&高精度的计时器 (high resolution)初始化 */
hrtimers_init();
/*初始化软中断 */
softirq_init();
/* 初始化时钟资源和普通计时器的值 */
timekeeping_init();
/* 初始化系统时间*/
time_init();
/*为内核分配内存以存储收集的数据*/
profile_init();
/* 开中断 */
if (!irqs_disabled())
printk("start_kernel(): bug: interrupts were enabled early\n");
early_boot_irqs_on();
local_irq_enable();
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
/* 初始化控制台,为了能够尽早地帮助调试,显示系统引导的信息*/
console_init();
if (panic_later)
panic(panic_later, panic_param);
/*如果定义了CONFIG_LOCKDEP宏,则打印锁依赖信息,否则什么也不做 */
lockdep_info();
/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
/* 如果定义CONFIG_DEBUG_LOCKING_API_SELFTESTS宏,则locking_selftest()是一个空函数,否则执行锁自测*/
locking_selftest();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
initrd_start < min_low_pfn << PAGE_SHIFT) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
"disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
#endif
/*
(1)dcache_init()创建SLAB缓存,该缓存保存目录项描述符。传存本身被称作dentry_cache。当进程访问文件或目录时所涉及的目录名有多个目录分量组成,目录项描述符就是针对每个分量而创建的。目录项各结构把文件或目录分量与其索引结点结合起来,因而可以通过该目录项可以更快地找到与其对应的索引结点
(2)inode_init()初始化哈希表索引结点和等待队列对头,该队头存放内核要锁存的哈希索引结点。
(3)file_init()确定给每个进程呢个zhogn的文件所分配的最大内存量
(4)mnt_init()创建了保存vfsmount对象且名为mnt_cache的缓存,VFS利用这协对吸纳给来挂载文件系统。该例程也创建mount_hashtable队列,该队列存放mnt_cache中引用的快速访问对象。然后该例程发出调用来初始化sysfs文件系统并挂载root文件系统。
*/
vfs_caches_init_early();
cpuset_init_early();
/* Mem_init()函数为mem_map中的自由区作标记并且打印出自由内存的大小。这个函数在系统的各个部分申请过内存后执行 */
mem_init();
/* 初始化cache相关的链表,函数在初始化页表分配器后smp_init()之前执行 */
kmem_cache_init();
/* 为逻辑号为0的cpu初始化页面。如果是smp情况下,只要cpu表现为online态,此函数就会执行 */
setup_per_cpu_pageset();
/* numa内存策略器初始化 */
numa_policy_init();
/* 内存初始化后调用 */
if (late_time_init)
late_time_init();
/*计算并打印许多著名的"BogoMips"的值,该值度量处理器在一个时钟节拍内可以反复执行多少个delay().对不同速度的处理器,cali_brate_delay()允许的延迟大约相同*/
calibrate_delay();
/* 初始化pidmap_array,分配pid=0给当前进程 */
pidmap_init();
/* 初始化页表高速缓存 */
pgtable_cache_init();
/*初始化优先级树index_bits_to_maxindex数组*/
prio_tree_init();
/*创建anon_vma结构对象slab缓存*/
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
/*根据可用内存大小来建立用户缓冲区uid_cache,初始化最大线程数max_threads,为init_task配置RLIMIT_NPROC的值为max_threads/2 */
fork_init(num_physpages);
/*建立各种块缓冲区,比如VFS, VM等*/
proc_caches_init();
buffer_init();
unnamed_dev_init();
key_init();
security_init();
vfs_caches_init(num_physpages);
radix_tree_init();
signals_init();
/* rootfs populating might need page-writeback */
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
cpuset_init();
taskstats_init_early();
delayacct_init();
/*检查错误,其实是调用check_writebuffer_bugs()函数检查是否有物理地址混淆的现象 */
check_bugs();
/* advanced configuration and power management interface */
acpi_early_init(); /* before LAPIC and SMP init */
/* Do the rest non-__init'ed, we're now alive */
/* 创建init进程,删除内核锁,启动idle线程 */
rest_init();
}
进入init进程后,将执行init()函数负责完成挂接根文件系统、初始化设备驱动和启动用户空间的init进程。(sunny 负责研究这部分)
分享到:
相关推荐
在按照YOLOv10官网上的步骤进行时,运行app.py文件时,如果没有预训练模型的话会报错。解压压缩包里的内容到同级目录下(在requirements.txt文档下面),这样运行后就不会报错。
在IT领域,Linux系统是广泛应用于服务器和嵌入式设备的操作系统。对于系统开发者和管理员,深入理解Linux的Socket编程、IO模型以及进程间通信(IPC)是至关重要的。本资料包提供了三个主要部分的学习资源:`process_comm`涉及进程间通信,`linux_socket`涵盖Socket编程,而`io_mode`则讨论Linux的IO模型。接下来,我们将详细探讨这些关键知识点。 让我们来看看**Linux Socket编程**。Socket是网络通信的基本接口,它允许两个或多个进程通过网络进行数据交换。在Linux中,Socket编程通常涉及到以下步骤:创建Socket,绑定IP地址和端口号,监听连接请求,接受连接,发送和接收数据,最后关闭Socket。`linux_socket`目录可能包含了示例代码,演示如何创建TCP或UDP Socket,处理并发连接,以及实现基本的错误处理机制。理解Socket编程有助于开发网络服务,如Web服务器、FTP服务器等。 我们来讨论**Linux IO模型**。在Linux中,有五种主要的IO模型:阻塞IO、非阻塞IO、IO复用(
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
本压缩包资源说明,你现在往下拉可以看到压缩包内容目录 我是批量上传的基于SpringBoot+Vue的项目,所以描述都一样;有源码有数据库脚本,系统都是测试过可运行的,看文件名即可区分项目~ |Java|SpringBoot|Vue|前后端分离| 开发语言:Java 框架:SpringBoot,Vue JDK版本:JDK1.8 数据库:MySQL 5.7+(推荐5.7,8.0也可以) 数据库工具:Navicat 开发软件: idea/eclipse(推荐idea) Maven包:Maven3.3.9+ 系统环境:Windows/Mac
湖大计算机体系结构实验
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
Toad Data Modeler:Toad中数据字典的创建与管理.docx
前台: 用户读书笔记,心得查看,并且可以支持调用第三方组建,进行实时聊天 在线图书阅读,一本书里,有多个章节,阅读到某个章节,可以保存阅读进度,比如阅读第一章了,点击提交,可以存储阅读进度,在个人后台查看阅读进度 用户注册 后台: 管理员: 管理员用户管理 注册用户管理 书籍类别管理 书籍信息管理 阅读进度管理 分享阅读心得,笔记管理 系统管理 注册用户: 个人资料修改 分享阅读心得,笔记(下拉表,选择类别) 个人阅读进度查看
SolarWinds数据库性能分析器:高级功能:SQL分析与优化.docx
电子商务之价格优化算法:动态定价:价格战与市场动态.docx
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
基于VB的程序实例,可供参考学习使用
基于java的旅游推荐系统设计与实现答辩PPT.pptx
安装前的准备 1、安装Python:确保你的计算机上已经安装了Python。你可以在命令行中输入python --version或python3 --version来检查是否已安装以及安装的版本。 个人建议:在anaconda中自建不同python版本的环境,方法如下(其他版本照葫芦画瓢): 比如创建python3.8环境,anaconda命令终端输入:conda create -n py38 python==3.8 2、安装pip:pip是Python的包管理工具,用于安装和管理Python包。你可以通过输入pip --version或pip3 --version来检查pip是否已安装。 安装WHL安装包 1、打开命令行(或打开anaconda命令行终端): 在Windows上,你可以搜索“cmd”或“命令提示符”并打开它。 在macOS或Linux上,你可以打开“终端”。 2、cd到whl文件所在目录安装: 使用cd命令导航到你下载的whl文件所在的文件夹。 终端输入:pip install xxx.whl安装即可(xxx.whl指的是csdn下载解压出来的whl) 3、等待安装完成: 命令行会显示安装进度,并在安装完成后返回提示符。 以上是简单安装介绍,小白也能会,简单好用,从此再也不怕下载安装超时问题。 使用过程遇到问题可以私信,我可以帮你解决! 收起
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
基于VB的程序实例,可供参考学习使用
《基于Java Swing实现的飞机大战游戏》是一份旨在帮助学习者掌握Java编程和游戏开发的资源。该资源通过一个完整的飞机大战游戏实例,展示了如何使用Java语言结合Swing库进行桌面应用程序开发。本资源不仅包含详细的源码,还提供了相关文档、课程设计报告及视频教程,适合作为计算机科学与技术、软件工程等专业的课程设计或毕业设计项目。游戏功能包括玩家控制飞机移动、自动发射子弹、敌机随机出现与移动、碰撞检测以及游戏得分计算等。此外,还加入了背景音乐和音效,增强了游戏体验。通过多线程技术确保游戏流畅运行,同时采用面向对象编程思想构建了清晰的项目结构。总之,《基于Java Swing实现的飞机大战游戏》是一个理想的学习工具,它能够帮助初学者深入理解Java GUI编程和事件处理机制,同时也为有经验的开发者提供了实战演练的机会。
**正文** 在现代Web开发中,实时通信是一个重要的需求,SpringBoot框架提供了与WebSocket的集成,使得开发者可以方便地在应用程序中实现双向通信。本文将深入探讨如何在SpringBoot项目中整合WebSocket,以及相关的概念和技术。 WebSocket协议是HTML5引入的一种在单个TCP连接上进行全双工通信的协议,它允许服务器主动向客户端推送数据,极大地提高了实时性。而SpringBoot作为Spring框架的轻量级版本,简化了配置和启动流程,使得开发WebSocket应用变得更加便捷。 我们需要在`pom.xml`中添加SpringBoot WebSocket的相关依赖。SpringBoot默认集成了Spring Websocket,我们只需要引入`spring-boot-starter-websocket`模块即可: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId