热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

将PEBuilder转换为DIBooter.sh,集成DI工具至启动层(5):实现离线镜像引导安装

本文探讨了将PEBuilder转换为DIBooter.sh的方法,重点介绍了如何将DI工具集成到启动层,实现离线镜像引导安装。通过使用DD命令替代传统的grub-install工具,实现了GRUB的离线安装。此外,还详细解析了bootice工具的工作原理及其在该过程中的应用,确保系统在无网络环境下也能顺利引导和安装。

本文关键字:通过DD安装grub代替grub-install,grub离线安装技术,restore grub2 installation using dd,bootice工具原理

在《pebuilder变成dibuilder.sh,将di tools集入boot层4》的中间,我们提到过暂代方案的思路,这种思路的第一步,就是用grub作通用booter代替难实现的虚拟booter,为此我们在上文提出了初步的在dibuilder.sh中为离线镜像实现离线安装grub2的代码,----- 在整个《pebuilder变成dibuilder.sh,将di tools集入boot层》系列中,如果说(4)是(2)的增强继续,那么本文(5)就是对(3)的增强继续。前面准备好了所有debian dists(di,pve),我们最终的目的,是要在这些dists的基础上组装minstackos,并使dibuilder.sh中的生成raw镜像/localinstall生效,实现最终能安装在mbp实机上测试,这是后话


(为什么是di,pve这二者,因为我感觉debian installer的装机方案和pve的bearmetal hypervisor+lxc as app container越来越顺眼了,毕竟,用grub+dipe+pve作为多区段镜像第一个分区完全可以替换实现虚拟boot+payload的伪不死boot效果(这也可以理解为"在一块flash上装一个类似di的pe":类似白群将synope写在nand rom中),而debian pve也可以代替vm hypervisor inside boot to replace libos这些复杂目前还没用起来的东西),况且,我们也有dibuilder.sh对接),最后,过度虚拟化也真的不好用,这也是用grub代替虚拟boot作通用boot,及用lxc代替docker这种不好instant commit的原生云部署容器的又一原因。这些暂代方案的思路,贯穿了整个《minstackos的实践部分》


剩下的问题是考虑更好的dibuilder.sh对接,比如对接上文,在dibuilder.sh中继续实现为离线镜像生成grub2引导的问题提供方案。

好了,动工


测试研究mbr脚本定制,得到core_img

为什么要得到一个core_img,这个问题从何而来?先来看下grub2这个东西,它就像deb的动态control文件,我们要把离线镜像集成grub,必须要得到一个为此镜像预安装了的grub2,这绝非拷贝/boot/grub下的文件那么简单,


关于grub2 mbr安装

grub-install只是一个脚本,内部真正执行工作的是grub-mkimage和grub-setup,grub2-install 脚本的工作包括调用grub2-probe扫描计算机并收集磁盘和分区信息,调用grub2-mkimage构建一个基于/boot/grub下脚手架文件之上的新的new.img,即core.img,grub-mkimage的过程形如grub-mkimage -d x86_64-efi -p /boot/grub -o grubx64.efi -O x86_64-efi acpi all_video bitmap bitmap_scale blocklist exfat expr ....一般地,仅 fshelp ext2 part_msdos biosdisk就够了,最后grub2-setup把grub的boot.img写入MBR中,把core.img写进设备的第一个扇区

在BIOS平台下,boot.img是grub启动的第一个img文件,它被写入到MBR中或分区的boot sector中,因为boot sector的大小是512字节,所以该img文件的大小也是512字节。boot.img唯一的作用是读取属于core.img的第一个扇区并跳转到它身上,将控制权交给该扇区的img。由于体积大小的限制,boot.img无法理解文件系统的结构,因此grub2-install将会把core.img的位置硬编码到boot.img中(这并不一定就是物理扇区号,可能是虚拟offset),这样就一定能找到core.img的位置。

关于bios下写入booter的复杂性,其实我们在装黑苹果(1)处理clover的boot的biosdisk时候也遇到了。还有上次《普化群晖将其改造成正常磁盘布局及编译源码打开kernel message》说到grub-install在/dev/loop上失效,可能是在host上dev map string没有绑定https://itectec.com/superuser/how-to-install-grub-into-an-img-file/。可能需要mkdir -p /mnt/boot/grub,cat 进 /mnt/boot/grub/device.map:(hd0) /dev/loop0,(hd0,1) /dev/loop1等正确内容


这也就是说,(以上这是一个与具体磁盘绑定的动态配置过程)磁盘上的/boot/grub/i386-pc/*.img的这些文件全是脚手架文件,本质起作用的是具体磁盘环境下已经被通过grub-install嵌写入磁盘结构,我们的工作就是要将这个动态过程,变成静态的通过可恢复文件方式就能完成安装的grub-install-static。却要求能配合我们稍后以自由方式生成的1g的boot区一起工作。(而且这又有限制和条件:在dibuilder.sh中我们基于加入了osx考虑不打算用grub-install)接下来你会看到,正确的boot.img容易生成,而core.img却难于得到。

所幸我们得到了一个脚本:

bootinfoscript,https://github.com/arvidjaar/bootinfoscript,它会显示一个主输出,同时在/tmp下保留大量有价值的临时文件(如解包的core.img),及其它输出(如Trash),它给出了一些bash算法,很为珍贵,以下是二个重要函数:

grub2_read_blocklist ()
这个函数整理hdd上存在于旧core.img->第一个sector中的diskboot.img未端部分->里的fragment信息,收集重组为新core.img上的fragments并写入,未来供grub2_info用
参数中的hdd也可能是硬盘(full core.img from blocklists,因为多个分区上都有可能有引导目录和core.img)也可能是单个普通core.img文件,后者情形我们不考虑,因为我们基本都是在读写磁盘嵌入区的这些文件区段,而不是文件系统中的这些文件。
grub2_info ()
这个函数用上一步得到的新core.img fragment信息,进一步,求得boot.img里core.img里的位置,以及更多信息(core.img中的文件夹信息,模块,配置文件),完成整个grub解析工作
stage1即是boot.img或/dev/sda mbr区,grub2的core.img是stage2,这种说法引用了grub1的stage1,2

但是bootinfoscript的实现比较理想化,依赖linux,比如它用到filefrag(apt-get install binutils for strings command),应该换成用string代替加判断才更为portable。

无论如何,我们从这个脚本和它的输出中希望得到的帮助是:它可以用于为我们的镜像输出core_img。供下一部分直接dd,而实际上,它也的确可以办到:我们可以在生成的虚拟镜像中安装一个linux,使之形成一个能启动的/dev/sda或其它需要的分区结构,在其中测试bootinfoscript,提取/tmp下的core_img。


2,实际修改dibuilder.sh并得到boot.img

得到core_img,我们接下来修改dibuilder.sh的对接部分,首先修改dibuilder.sh local remastering and install支持部分,修改得到一套较为规则的/tmp/tmpdown->/tmp/tmpremastering->/tmp/tmpmnt target or /boot target的文件夹逻辑结构:

首先,原来prepare misc中的net,grub部分中的grub归入接下来的remastering all up部分,整个脚本只有三部了,这是新的整个第三部分,


echo -e "\n \033[36m # Remastering all up... \033[0m \n"
remasteringdir='/tmp/tmpremastering';
[[ -d $remasteringdir ]] && rm -rf $remasteringdir;
sleep 2s && echo -en "\runpacking grub files ..."
export tmpMNT=/tmp/tmpmnt
[[ "$tmpMODE" == '1' && "$tmpTARGET" == 'minstackos' ]] && {

prepareimg() {
tmpDEV=$(mount | grep "$tmpMNT" | awk '{print $1}')
[ -z "$tmpDEV" ] && {
rm -rf /tmp/tmpimage.raw
dd if=/dev/zero of=/tmp/tmpimage.raw bs=1024 count=1048576 >/dev/null 2>&1
[[ "$tmpPLAT" == "0" ]] && tmpDEV=`losetup -fP --show /tmp/tmpimage.raw | awk '{print $1}'` || tmpDEV=`hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount /tmp/tmpimage.raw | awk '{print $1}'`
注意osx与linux在工具能力上的差别和分区控制方面的差别。通常我们必须在mbr和第一个分区开始前预留一定空间放core.img,一般grub-install会把core.img写入到第一个分区,但是我们放在这个预留一些空白区以更方便控制和定制,比如刻意向我们的离线grub2合成方向靠拢
[ -n "$tmpDEV" ] && {
[[ "$tmpPLAT" == "0" ]] && parted -s "$tmpDEV" mktable msdos >/dev/null 2>&1 && parted -s "$tmpDEV" mkpart primary ext3 2048s 100% >/dev/null 2>&1 && parted -s "$tmpDEV" set 1 boot on >/dev/null 2>&1 && mkfs.ext3 "$tmpDEV"p1 >/dev/null 2>&1
[[ "$tmpPLAT" == "1" ]] && diskutil partitionDisk "$tmpDEV" MBR FREE %noformat% 1048576 "MS-DOS FAT16" TMPVOL 0 >/dev/null 2>&1
}
[ ! -d "$tmpMNT" ] && [[ "$tmpPLAT" == "0" ]] && mkdir "$tmpMNT" && mount "$tmpDEV"p1 "$tmpMNT" || tmpMNT=/Volumes/TMPVOL;[[ ! -d /tmp/tmpmnt ]] && ln -s /Volumes/TMPVOL /tmp/tmpmnt
#echo "tmpdev is:""$tmpDEV"
#echo "tmpmnt is:""$tmpMNT"
#echo "- before installing/remastering grub,unzip it"
mkdir -p /tmp/tmpremastering/grub
tar -xf /tmp/tmpdown/debian/dists/jessie/main/binary-amd64/grub.gz -C /tmp/tmpremastering/grub
(这里放接下来得到boot.img和离线镜像写grub的过程,这里的内容)
进生成core_img所在系统,针对/dev/sda得到其mbr(不要用脚本手架文件中的boot.img,开头字节是空的),得到原始字串并处理:sudo hexdump -v -n 446 /dev/sda | (手动处理掉换行,索引,并删掉最后一行) | sed -e "s/ //g" | sed -e "s/\([0-9a-z][0-9a-z]\)/\1 /g" | sed -e "s/ /\\\x/g | sed -E 's/(\\x..)(\\x..)/\2\1/g'"
(You can use xxd to dump binary files just like hexdump and od, but you can also use it to do the reverse: turn a hex dump back into binary. If y)
得到下列待处理字符串命名为grub202:
grub202='\xeb\x63\x90\x10\x8e\xd0\xbc\x00\xb0\xb8\x00\x00\x8e\xd8\x8e\xc0\xfb\xbe\x00\x7c\xbf\x00\x06\xb9\x00\x02\xf3\xa4\xea\x21\x06\x00\x00\xbe\xbe\x07\x38\x04\x75\x0b\x83\xc6\x10\x81\xfe\xfe\x07\x75\xf3\xeb\x16\xb4\x02\xb0\x01\xbb\x00\x7c\xb2\x80\x8a\x74\x01\x8b\x4c\x02\xcd\x13\xea\x00\x7c\x00\x00\xeb\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00\x00\x00\x00\xff\xfa\x90\x90\xf6\xc2\x80\x74\x05\xf6\xc2\x70\x74\x02\xb2\x80\xea\x79\x7c\x00\x00\x31\xc0\x8e\xd8\x8e\xd0\xbc\x00\x20\xfb\xa0\x64\x7c\x3c\xff\x74\x02\x88\xc2\x52\xbb\x17\x04\xf6\x07\x03\x74\x06\xbe\x88\x7d\xe8\x17\x01\xbe\x05\x7c\xb4\x41\xbb\xaa\x55\xcd\x13\x5a\x52\x72\x3d\x81\xfb\x55\xaa\x75\x37\x83\xe1\x01\x74\x32\x31\xc0\x89\x44\x04\x40\x88\x44\xff\x89\x44\x02\xc7\x04\x10\x00\x66\x8b\x1e\x5c\x7c\x66\x89\x5c\x08\x66\x8b\x1e\x60\x7c\x66\x89\x5c\x0c\xc7\x44\x06\x00\x70\xb4\x42\xcd\x13\x72\x05\xbb\x00\x70\xeb\x76\xb4\x08\xcd\x13\x73\x0d\x5a\x84\xd2\x0f\x83\xd0\x00\xbe\x93\x7d\xe9\x82\x00\x66\x0f\xb6\xc6\x88\x64\xff\x40\x66\x89\x44\x04\x0f\xb6\xd1\xc1\xe2\x02\x88\xe8\x88\xf4\x40\x89\x44\x08\x0f\xb6\xc2\xc0\xe8\x02\x66\x89\x04\x66\xa1\x60\x7c\x66\x09\xc0\x75\x4e\x66\xa1\x5c\x7c\x66\x31\xd2\x66\xf7\x34\x88\xd1\x31\xd2\x66\xf7\x74\x04\x3b\x44\x08\x7d\x37\xfe\xc1\x88\xc5\x30\xc0\xc1\xe8\x02\x08\xc1\x88\xd0\x5a\x88\xc6\xbb\x00\x70\x8e\xc3\x31\xdb\xb8\x01\x02\xcd\x13\x72\x1e\x8c\xc3\x60\x1e\xb9\x00\x01\x8e\xdb\x31\xf6\xbf\x00\x80\x8e\xc6\xfc\xf3\xa5\x1f\x61\xff\x26\x5a\x7c\xbe\x8e\x7d\xeb\x03\xbe\x9d\x7d\xe8\x34\x00\xbe\xa2\x7d\xe8\x2e\x00\xcd\x18\xeb\xfe\x47\x52\x55\x42\x20\x00\x47\x65\x6f\x6d\x00\x48\x61\x72\x64\x20\x44\x69\x73\x6b\x00\x52\x65\x61\x64\x00\x20\x45\x72\x72\x6f\x72\x0d\x0a\x00\xbb\x01\x00\xb4\x0e\xcd\x10\xac\x3c\x00\x75\xf4\xc3\xe1\x75\x0e\x09\x00\x00'
#(确认得到的显示正确,grub有特征码字串)
#printf $grub202 | hexdump -C
#虚拟offset号为1,无须处理
#printf $grub202 | hexdump -v -n 1 -s 92
#$grub202=`printf $grub202 | sed -e 's/\(x[0-9a-z][0-9a-z]\)/x01/93'`
#printf $grub202 | hexdump -v -n 1 -s 92
#添加可启动标识
#printf $grub202 | hexdump -v -n 1 -s 447
grub202+='\x80'
#printf $grub202 | hexdump -v -n 1 -s 447
diskutil unmountDisk "$tmpDEV"
printf $grub202 | dd of="$tmpDEV" bs=447 count=1
#确认已写入
hexdump -v -n 512 -C $tmpDEV
#core.img无特征码字串,前面10个字节判断
dd if=/tmp/tmpremastering/grub/i386-pc/core.img of="$tmpDEV" seek=512 bs=25760 count=1 cOnv=notrunc
hexdump -v -n 10 -C /tmp/tmpremastering/grub/i386-pc/core.img
hexdump -v -s 512 -n 10 -C $tmpDEV
diskutil mountDisk "$tmpDEV"
正式脚本中记得把上面调试输出适当注释掉
}
sleep 2s && echo -en "[ \033[32m /tmp/tmpremastering/grub/* \033[0m ]"
# Automatically remove DISK on exit
[[ "$tmpPLAT" == "0" ]] && trap 'echo; echo "- Ejecting tmpdev disk"; umount "$tmpMNT" && losetup -d "$tmpDEV"' EXIT || trap 'echo; echo "- Ejecting tmpdev disk"; diskutil eject "$tmpDEV";rm -rf /tmp/tmpmnt' EXIT
}
prepareimg
}
原grub remastering部分
LoaderMode='0'
setInterfaceName='0'
setIPv6='0'
......
sed -i '$a\\n' /tmp/tmpremastering/grub/grub.new;
fi
sleep 2s && echo -en "[ \033[32m /tmp/tmpremastering/grub/grub.new \033[0m ]"
}
在这里才写入脚手架文件,一切都是为了让down,remastering,copying to target这三部分清淅分开
sleep 2s && echo -en "\nprocessing grub ......"
[[ -d /Volumes/TMPVOL ]] && [[ "$tmpPLAT" == "1" ]] && mkdir -p "$tmpMNT"/boot/grub && cp -a /tmp/tmpremastering/grub/* "$tmpMNT"/boot/grub
[[ "$tmpINSTANTWITHOUTVNC" == '0' ]] && {
GRUBPATCH='0';
if [[ "$LoaderMode" == "0" && "$tmpPLAT" != "1" ]]; then
......
[[ -f $GRUBDIR/grubenv ]] && sed -i 's/saved_entry/#saved_entry/g' $GRUBDIR/grubenv;
fi
开始remaster initrd部分的解压步骤
sleep 2s && echo -en "\nunpacking initrd ......"
mkdir -p /tmp/tmpremastering/initrd/usr/bin;
cd /tmp/tmpremastering/initrd;
.....
[[ "$tmpMODE" != "2" ]] && [[ "$tmpTARGET" == "minstackos" ]] && cp -f "$downdir/dists/jessie/main/binary-amd64/initrd.img" "/tmp/tmpremastering/$NewIMG"
[[ "$tmpPLAT" != "1" ]] && [[ "$tmpMODE" == "2" ]] && cp -f "$downdir/dists/jessie/main/debian-installer/binary-amd64/initrd.img" "/tmp/tmpremastering/$NewIMG"
......
[[ "$tmpPLAT" == '0' ]] && $UNCOMP >/dev/null 2>&1 || $UNCOMP >/dev/null 2>&1
[[ -f '/boot/firmware.cpio.gz' ]] && {
gzip -d >/dev/null 2>&1
}
sleep 2s && echo -en "\nprocessing initrd ...."
find $downdir/dists/jessie -type f \( -name *.deb -o -name *.ldeb \) | while read line; do line2=${line##*/};echo -en "\033[s \033[K [ \033[32m ${line2:0:40} \033[0m ] \033[u";[[ $(ar -t ${line} | grep -E -o data.tar.gz) == 'data.tar.gz' ]] && ar -p ${line} data.tar.gz |zcat|tar -xf - -C /tmp/tmpremastering/initrd || ar -p ${line} data.tar.xz |xzcat|tar -xf - -C /tmp/tmpremastering/initrd; done
......
sleep 2s && echo -en "\nmake a safe wget wrapper to inc --no-check-certificate"
mv /tmp/tmpremastering/initrd/usr/bin/wget /tmp/tmpremastering/initrd/usr/bin/wget2
cat >/tmp/tmpremastering/initrd/usr/bin/wget<#!/bin/sh
rdlkf() { [ -L "\$1" ] && (local lk="\$(readlink "\$1")"; local d="\$(dirname "$1")"; cd "\$d"; local l="\$(rdlkf "\$lk")"; ([[ "\$l" = /* ]] && echo "\$l" || echo "\$d/\$l")) || echo "\$1"; }
DIR="\$(dirname "\$(rdlkf "\$0")")"
exec /usr/bin/env wget2 --no-check-certificate "\$@"
EOF
chmod +x /tmp/tmpremastering/initrd/usr/bin/wget
fi
清楚的remasterin /copying分离
echo -en "\ncopying vmlinuz to the target/mnt ......"
[[ -d /boot ]] && [[ "$tmpMODE" != "2" ]] && [[ "$tmpTARGET" == "minstackos" ]] && cp -f "$downdir/dists/jessie/main/binary-amd64/vmlinuz" /boot/vmlinuz
[[ -d /boot ]] && [[ "$tmpPLAT" != "1" ]] && [[ "$tmpMODE" == "2" ]] && cp -f "$downdir/dists/jessie/main/debian-installer/binary-amd64/vmlinuz" /boot/vmlinuz
[[ -d /Volumes/TMPVOL ]] && [[ "$tmpPLAT" == "1" ]] && cp -f "$downdir/dists/jessie/main/binary-amd64/vmlinuz" /Volumes/TMPVOL/vmlinuz
(全程注意新脚本变动中的文件夹新命名变化)
除此之外,脚本其实在原先第一部分checkmirros部分把主体中检测1,2都注释了,直接返回提供的mirror url,相当于没检测,这是为了适应导致下到/tmp/tmpdown里的新结构debian文件夹的源镜像变化部分。

然后在osx上brew install qemu,cd /tmp/测试。

sudo qemu-system-x86_64 -machine pc-q35-2.4 -smp 4,cores=2 -cpu Penryn,kvm=off,vendor=GenuineIntel -m 4096 -device ide-hd,bus=ide.2,drive=MacHDD -drive id=MacHDD,if=none,format=raw,file=./buildpackage.raw -monitor stdio

成功进入。继续在脚本中处理硬盘镜像。



关于整合或分开使用这几个dists,除了前面“用di代替synope进行通用式dd安装镜像”“三个initramfs合为一个”等。还有一个思路,将di U盘安装代替multiboot unetbootin或ventoy,这样,比如用grub+dibuilder.sh面向的多img为基础打造这样一个类似的本地化多启U盘呢(不考虑它们的运行),我们可以以那个1G的boot区为启动区,mbr始终是现在最得到全面兼容的格式,数据区放各种img.gz。grub中写好各种安装条目。它就是dibuilder.sh的本地版了:在前面《云主机装黑果实践(1)》我们讲到过制造一个本地多启U盘的方案。它是以iso解压出的东西为内容基础的和efi方案为基础的,(后来我们发现了ventoy,它以iso为基础的,比如osx镜像全部弄为cdr/iso了,如经过hdiutil convert -format UDRW -o cdr.dmg common.iso之后,白苹果其实也可以用,不过这二者启动和对各种ISO的支持都不太稳定,继续深入一点,dibuilder.sh提供了一种更具替代性的稳定和兼容方案。



推荐阅读
  • 本文基于刘洪波老师的《英文词根词缀精讲》,深入探讨了多个重要词根词缀的起源及其相关词汇,帮助读者更好地理解和记忆英语单词。 ... [详细]
  • ImmutableX Poised to Pioneer Web3 Gaming Revolution
    ImmutableX is set to spearhead the evolution of Web3 gaming, with its innovative technologies and strategic partnerships driving significant advancements in the industry. ... [详细]
  • 本文介绍了如何在C#中启动一个应用程序,并通过枚举窗口来获取其主窗口句柄。当使用Process类启动程序时,我们通常只能获得进程的句柄,而主窗口句柄可能为0。因此,我们需要使用API函数和回调机制来准确获取主窗口句柄。 ... [详细]
  • 本题涉及一棵由N个节点组成的树(共有N-1条边),初始时所有节点均为白色。题目要求处理两种操作:一是改变某个节点的颜色(从白变黑或从黑变白);二是查询从根节点到指定节点路径上的第一个黑色节点,若无则输出-1。 ... [详细]
  • 深入探讨CPU虚拟化与KVM内存管理
    本文详细介绍了现代服务器架构中的CPU虚拟化技术,包括SMP、NUMA和MPP三种多处理器结构,并深入探讨了KVM的内存虚拟化机制。通过对比不同架构的特点和应用场景,帮助读者理解如何选择最适合的架构以优化性能。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • 本文探讨了在使用Azure Active Directory进行用户身份验证时,结合AddAuthentication和RequireAuthenticatedUser的必要性及其潜在冗余问题。 ... [详细]
  • Ihaveastringwithquotesaroundthepathasfollows:我在路径周围有一个带引号的字符串,如下所示:C:\ProgramFiles(x ... [详细]
  • 本文详细介绍了C语言的起源、发展及其标准化过程,涵盖了从早期的BCPL和B语言到现代C语言的演变,并探讨了其在操作系统和跨平台编程中的重要地位。 ... [详细]
  • 本文详细探讨了KMP算法中next数组的构建及其应用,重点分析了未改良和改良后的next数组在字符串匹配中的作用。通过具体实例和代码实现,帮助读者更好地理解KMP算法的核心原理。 ... [详细]
  • 本文详细介绍如何使用arm-eabi-gdb调试Android平台上的C/C++程序。通过具体步骤和实用技巧,帮助开发者更高效地进行调试工作。 ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 本文详细介绍了macOS系统的核心组件,包括如何管理其安全特性——系统完整性保护(SIP),并探讨了不同版本的更新亮点。对于使用macOS系统的用户来说,了解这些信息有助于更好地管理和优化系统性能。 ... [详细]
author-avatar
刘刘刘存乐_626
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有