热门标签 | 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提供了一种更具替代性的稳定和兼容方案。



推荐阅读
  • 这篇文章将揭示 Vue 和 React 组件库中五个鲜为人知的强大工具。这些工具均以纯 JavaScript 实现,功能卓越。其中,async-validator 是一个数据验证插件,不仅预置了 URL 和电子邮件的验证规则,还支持异步验证功能。 ... [详细]
  • 表面缺陷检测数据集综述及GitHub开源项目推荐
    本文综述了表面缺陷检测领域的数据集,并推荐了多个GitHub上的开源项目。通过对现有文献和数据集的系统整理,为研究人员提供了全面的资源参考,有助于推动该领域的发展和技术进步。 ... [详细]
  • Docker网络基础探讨了如何通过高效的技术手段实现跨主机容器间的顺畅通信与访问。本文深入分析了Docker网络架构,特别是其在多主机环境下的应用,为Go语言开发者提供了宝贵的实践指导和理论支持。 ... [详细]
  • 本文介绍了在Windows 10系统下使用VirtualBox虚拟机环境部署CentOS 7.2,并在其上安装Docker的具体步骤。针对宝塔面板在Docker容器中磁盘空间限制为8GB的问题,提供了详细的解决方案和优化建议,确保用户能够高效利用有限的存储资源。 ... [详细]
  • 如何在DataGridView中实现带有图标的单元格显示
    本文详细探讨了在C# WinForms应用程序中,如何通过DataGridView控件实现带有图标的单元格显示。文章不仅提供了具体的实现方法,还深入解析了相关技术细节,对于希望提升用户界面交互体验的开发者而言,具有很高的参考价值。 ... [详细]
  • 前言: 网上搭建k8s的文章很多,但很多都无法按其说明在阿里云ecs服务器成功搭建,所以我就花了些时间基于自己成功搭建k8s的步骤写了个操作手册,希望对想搭建k8s环境的盆友有所帮 ... [详细]
  • 利用PaddleSharp模块在C#中实现图像文字识别功能测试
    PaddleSharp 是 PaddleInferenceCAPI 的 C# 封装库,适用于 Windows (x64)、NVIDIA GPU 和 Linux (Ubuntu 20.04) 等平台。本文详细介绍了如何使用 PaddleSharp 在 C# 环境中实现图像文字识别功能,并进行了全面的功能测试,验证了其在多种硬件配置下的稳定性和准确性。 ... [详细]
  • 使用 XlsxWriter 模块在 Python 中实现 Excel 单元格内多种格式文本的高效写入
    XlsxWriter 是一个强大的 Python 库,专门用于生成 `.xlsx` 格式的 Excel 文件。该模块不仅支持基本的数据写入,还提供了丰富的格式化选项,能够实现单元格内多种文本样式的高效处理。无论是字体、颜色、对齐方式还是边框,XlsxWriter 都能轻松应对,满足用户在 Excel 视图中的各种需求。 ... [详细]
  • RancherOS 是由 Rancher Labs 开发的一款专为 Docker 设计的轻量级 Linux 发行版,提供了一个全面的 Docker 运行环境。其引导镜像仅 20MB,非常适合在资源受限的环境中部署。本文将详细介绍如何在 ESXi 虚拟化平台上安装和配置 RancherOS,帮助用户快速搭建高效、稳定的容器化应用环境。 ... [详细]
  • 为了深入了解某些测试框架的工作原理,并在培训中构建一个简单的测试框架,我系统地研究了 should.js 的源代码。本文将分享我的学习过程和分析结果,帮助读者更好地掌握 should.js 的核心机制。 ... [详细]
  • 深入解析JavaScript对象属性中的访问器方法:get/set与getter/setter详解 ... [详细]
  • 在Linux环境中,通过编写Shell脚本来实现自定义命令的创建与激活,能够极大地简化服务器上多个子系统的管理操作。例如,通过简单的命令如“tt”,即可快速查看各个应用程序的名称及其运行状态,从而提高系统维护的效率和便捷性。 ... [详细]
  • 我正致力于利用Azure Functions和System.IO.Compression库,将大量文件高效地压缩并存储到Azure Blob容器中。这种方法不仅提高了存储效率,还优化了数据管理流程。通过这种方式,可以显著减少存储成本,并提升数据访问速度。 ... [详细]
  • Spring Boot与Redis的高效集成方案
    本文探讨了Spring Boot与Redis的高效集成方法,详细介绍了如何在Spring Boot项目中配置和使用Redis,以提升应用性能和数据处理能力。同时,文章还涉及了Go语言社区的相关资源,为Golang开发者提供了宝贵的技术交流平台。 ... [详细]
  • 算术表达式分析与解析技术初探
    本文初步探讨了算术表达式的分析与解析技术,针对作者在职业转型过程中发现自身算法基础薄弱的问题,决定在接下来的三个月内,系统地学习和掌握常用数据结构与算法,以提升个人技术能力。研究内容不仅涵盖了基本的算术表达式解析方法,还深入讨论了其在实际应用中的优化策略,为相关领域的进一步研究奠定了基础。 ... [详细]
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社区 版权所有