linux中initrd的含义,Linux2.6 内核的 Initrd 机制解析

1.什么是 Initrdinitrd 的英文含义是 boot loaderinitialized RAM disk,就是由 boot loader 初始化的内存盘。在 linux内核启动前, boot loader 会将存储介质中的 initrd 文件加载到内存,内核启动时会在访问真正的根文件系统前先访问该内存中的 initrd 文件系统。在 boot loader 配置了 initrd 的情况下,内核启动被分成了两个阶段,第一阶段先执行 initrd 文件系统中的"某个文件",完成加载驱动模块等任务,第二阶段才会执行真正的根文件系统中的 /sbin/init 进程。这里提到的"某个文件",Linux2.6 内核会同以前版本内核的不同,所以这里暂时使用了"某个文件"这个称呼,后面会详细讲到。第一阶段启动的目的是为第二阶段的启动扫清一切障爱,最主要的是加载根文件系统存储介质的驱动模块。我们知道根文件系统可以存储在包括IDE、SCSI、USB在内的多种介质上,如果将这些设备的驱动都编译进内核,可以想象内核会多么庞大、臃肿。Initrd 的用途主要有以下四种:1. linux 发行版的必备部件linux 发行版必须适应各种不同的硬件架构,将所有的驱动编译进内核是不现实的,initrd 技术是解决该问题的关键技术。Linux 发行版在内核中只编译了基本的硬件驱动,在安装过程中通过检测系统硬件,生成包含安装系统硬件驱动的 initrd,无非是一种即可行又灵活的解决方案。2. livecd 的必备部件同 linux 发行版相比,livecd 可能会面对更加复杂的硬件环境,所以也必须使用 initrd。3. 制作 Linux usb 启动盘必须使用 initrdusb 设备是启动比较慢的设备,从驱动加载到设备真正可用大概需要几秒钟时间。如果将 usb 驱动编译进内核,内核通常不能成功访问 usb 设备中的文件系统。因为在内核访问 usb 设备时, usb 设备通常没有初始化完毕。所以常规的做法是,在 initrd 中加载 usb 驱动,然后休眠几秒中,等待 usb设备初始化完毕后再挂载 usb 设备中的文件系统。4. 在 linuxrc 脚本中可以很方便地启用个性化 bootsplash。2.Linux2.4内核对 Initrd 的处理流程为了使读者清晰的了解Linux2.6内核initrd机制的变化,在重点介绍Linux2.6内核initrd之前,先对linux2.4内核的initrd进行一个简单的介绍。Linux2.4内核的initrd的格式是文件系统镜像文件,本文将其称为image-initrd,以区别后面介绍的linux2.6内核的cpio格式的initrd。 linux2.4内核对initrd的处理流程如下:1. boot loader把内核以及/dev/initrd的内容加载到内存,/dev/initrd是由boot loader初始化的设备,存储着initrd。2. 在内核初始化过程中,内核把 /dev/initrd 设备的内容解压缩并拷贝到 /dev/ram0 设备上。3. 内核以可读写的方式把 /dev/ram0 设备挂载为原始的根文件系统。4. 如果 /dev/ram0 被指定为真正的根文件系统,那么内核跳至最后一步正常启动。5. 执行 initrd 上的 /linuxrc 文件,linuxrc 通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。6. /linuxrc 执行完毕,真正的根文件系统被挂载。7. 如果真正的根文件系统存在 /initrd 目录,那么 /dev/ram0 将从 / 移动到 /initrd。否则如果 /initrd 目录不存在, /dev/ram0 将被卸载。8. 在真正的根文件系统上进行正常启动过程 ,执行 /sbin/init。 linux2.4 内核的 initrd 的执行是作为内核启动的一个中间阶段,也就是说 initrd 的 /linuxrc 执行以后,内核会继续执行初始化代码,我们后面会看到这是 linux2.4 内核同 2.6 内核的 initrd 处理流程的一个显著区别。3.Linux2.6 内核对 Initrd 的处理流程linux2.6 内核支持两种格式的 initrd,一种是前面第 3 部分介绍的 linux2.4 内核那种传统格式的文件系统镜像-image-initrd,它的制作方法同 Linux2.4 内核的 initrd 一样,其核心文件就是 /linuxrc。另外一种格式的 initrd 是 cpio 格式的,这种格式的 initrd 从 linux2.5 起开始引入,使用 cpio 工具生成,其核心文件不再是 /linuxrc,而是 /init,本文将这种 initrd 称为 cpio-initrd。尽管 linux2.6 内核对 cpio-initrd和 image-initrd 这两种格式的 initrd 均支持,但对其处理流程有着显著的区别,下面分别介绍 linux2.6 内核对这两种 initrd 的处理流程。cpio-initrd 的处理流程1. boot loader 把内核以及 initrd 文件加载到内存的特定位置。2. 内核判断initrd的文件格式,如果是cpio格式。3. 将initrd的内容释放到rootfs中。4. 执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给/init文件处理。image-initrd的处理流程1. boot loader把内核以及initrd文件加载到内存的特定位置。2. 内核判断initrd的文件格式,如果不是cpio格式,将其作为image-initrd处理。3. 内核将initrd的内容保存在rootfs下的/initrd.image文件中。4. 内核将/initrd.image的内容读入/dev/ram0设备中,也就是读入了一个内存盘中。5. 接着内核以可读写的方式把/dev/ram0设备挂载为原始的根文件系统。6. .如果/dev/ram0被指定为真正的根文件系统,那么内核跳至最后一步正常启动。7. 执行initrd上的/linuxrc文件,linuxrc通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。8. /linuxrc执行完毕,常规根文件系统被挂载9. 如果常规根文件系统存在/initrd目录,那么/dev/ram0将从/移动到/initrd。否则如果/initrd目录不存在, /dev/ram0将被卸载。10. 在常规根文件系统上进行正常启动过程 ,执行/sbin/init。通过上面的流程介绍可知,Linux2.6内核对image-initrd的处理流程同linux2.4内核相比并没有显著的变化, cpio-initrd的处理流程相比于image-initrd的处理流程却有很大的区别,流程非常简单,在后面的源代码分析中,读者更能体会到处理的简捷。4.cpio-initrd同image-initrd的区别与优势没有找到正式的关于cpio-initrd同image-initrd对比的文献,根据笔者的使用体验以及内核代码的分析,总结出如下三方面的区别,这些区别也正是cpio-initrd的优势所在:cpio-initrd的制作方法更加简单cpio-initrd的制作非常简单,通过两个命令就可以完成整个制作过程#假设当前目录位于准备好的initrd文件系统的根目录下bash# find . | cpio -c -o > ../initrd.imgbash# gzip ../initrd.img而传统initrd的制作过程比较繁琐,需要如下六个步骤#假设当前目录位于准备好的initrd文件系统的根目录下bash# dd if=/dev/zero of=../initrd.img bs=512k count=5bash# mkfs.ext2 -F -m0 ../initrd.imgbash# mount -t ext2 -o loop ../initrd.img /mntbash# cp -r * /mntbash# umount /mntbash# gzip -9 ../initrd.img本文不对上面命令的含义作细节的解释,因为本文主要介绍的是linux内核对initrd的处理,对上面命令不理解的读者可以参考相关文档。cpio-initrd的内核处理流程更加简化通过上面initrd处理流程的介绍,cpio-initrd的处理流程显得格外简单,通过对比可知cpio-initrd的处理流程在如下两个方面得到了简化:1. cpio-initrd并没有使用额外的ramdisk,而是将其内容输入到rootfs中,其实rootfs本身也是一个基于内存的文件系统。这样就省掉了ramdisk的挂载、卸载等步骤。2. cpio-initrd启动完/init进程,内核的任务就结束了,剩下的工作完全交给/init处理;而对于image-initrd,内核在执行完/linuxrc进程后,还要进行一些收尾工作,并且要负责执行真正的根文件系统的/sbin/init。通过图1可以更加清晰的看出处理流程的区别:

图1内核对cpio-initrd和image-initrd处理流程示意图

461e14ec3061621dff9a35508c6a5768.gifcpio-initrd的职责更加重要如图1所示,cpio-initrd不再象image-initrd那样作为linux内核启动的一个中间步骤,而是作为内核启动的终点,内核将控制权交给cpio-initrd的/init文件后,内核的任务就结束了,所以在/init文件中,我们可以做更多的工作,而不比担心同内核后续处理的衔接问题。当然目前linux发行版的cpio-initrd的/init文件的内容还没有本质的改变,但是相信initrd职责的增加一定是一个趋势。5.linux2.6内核initrd处理的源代码分析上面简要介绍了Linux2.4内核和2.6内核的initrd的处理流程,为了使读者对于Linux2.6内核的initrd的处理有一个更加深入的认识,下面将对Linuxe2.6内核初始化部分同initrd密切相关的代码给予一个比较细致的分析,为了讲述方便,进一步明确几个代码分析中使用的概念:rootfs: 一个基于内存的文件系统,是linux在初始化时加载的第一个文件系统,关于它的进一步介绍可以参考文献[4]。initramfs: initramfs同本文的主题关系不是很大,但是代码中涉及到了initramfs,为了更好的理解代码,这里对其进行简单的介绍。Initramfs是在 kernel 2.5中引入的技术,实际上它的含义就是:在内核镜像中附加一个cpio包,这个cpio包中包含了一个小型的文件系统,当内核启动时,内核将这个cpio包解开,并且将其中包含的文件系统释放到rootfs中,内核中的一部分初始化代码会放到这个文件系统中,作为用户层进程来执行。这样带来的明显的好处是精简了内核的初始化代码,而且使得内核的初始化过程更容易定制。Linux 2.6.12内核的 initramfs还没有什么实质性的东西,一个包含完整功能的initramfs的实现可能还需要一个缓慢的过程。对于initramfs的进一步了解可以参考文献[1][2][3]。cpio-initrd: 前面已经定义过,指linux2.6内核使用的cpio格式的initrd。image-initrd: 前面已经定义过,专指传统的文件镜像格式的initrd。realfs: 用户最终使用的真正的文件系统。内核的初始化代码位于 init/main.c 中的 static int init(void * unused)函数中。同initrd的处理相关部分函数调用层次如下图,笔者按照这个层次对每一个函数都给予了比较详细的分析,为了更好的说明,下面列出的代码中删除了同本文主题不相关的部分:

图2 initrd相关代码的调用层次关系图

6740a44bc5bdf869e247cf0ed7412df7.gifinit函数是内核所有初始化代码的入口,代码如下,其中只保留了同initrd相关部分的代码。static int init(void * unused){[1]populate_rootfs();[2]if (sys_access((const char __user *) "/init", 0) == 0)execute_command = "/init";elseprepare_namespace();[3]if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)printk(KERN_WARNING "Warning: unable to open an initial console.\n");(void) sys_dup(0);(void) sys_dup(0);[4]if (execute_command)run_init_process(execute_command);run_init_process("/sbin/init");run_init_process("/etc/init");run_init_process("/bin/init");run_init_process("/bin/sh");panic("No init found. Try passing init= option to kernel.");}代码[1]:populate_rootfs函数负责加载initramfs和cpio-initrd,对于populate_rootfs函数的细节后面会讲到。代码[2]:如果rootfs的根目录下中包含/init进程,则赋予execute_command,在init函数的末尾会被执行。否则执行prepare_namespace函数,initrd是在该函数中被加载的。代码[3]:将控制台设置为标准输入,后续的两个sys_dup(0),则复制标准输入为标准输出和标准错误输出。代码[4]:如果rootfs中存在init进程,就将后续的处理工作交给该init进程。其实这段代码的含义是如果加载了cpio-initrd则交给cpio-initrd中的/init处理,否则会执行realfs中的init。读者可能会问:如果加载了cpio-initrd, 那么realfs中的init进程不是没有机会运行了吗?确实,如果加载了cpio-initrd,那么内核就不负责执行realfs的init进程了,而是将这个执行任务交给了cpio-initrd的init进程。解开fedora core4的initrd文件,会发现根目录的下的init文件是一个脚本,在该脚本的最后一行有这样一段代码:………..switchroot --movedev /sysroot就是switchroot语句负责加载realfs,以及执行realfs的init进程。对cpio-initrd的处理对cpio-initrd的处理位于populate_rootfs函数中。void __init populate_rootfs(void){[1] char *err = unpack_to_rootfs(__initramfs_start,__initramfs_end - __initramfs_start, 0);[2]if (initrd_start) {[3]err = unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start, 1);[4]if (!err) {printk(" it is\n");unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start, 0);free_initrd_mem(initrd_start, initrd_end);return;}[5]fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 700);if (fd >= 0) {sys_write(fd, (char *)initrd_start,initrd_end - initrd_start);sys_close(fd);free_initrd_mem(initrd_start, initrd_end);}}代码[1]:加载initramfs, initramfs位于地址__initramfs_start处,是内核在编译过程中生成的,initramfs的是作为内核的一部分而存在的,不是 boot loader加载的。前面提到了现在initramfs没有任何实质内容。代码[2]:判断是否加载了initrd。无论哪种格式的initrd,都会被boot loader加载到地址initrd_start处。代码[3]:判断加载的是不是cpio-initrd。实际上 unpack_to_rootfs有两个功能一个是释放cpio包,另一个就是判断是不是cpio包, 这是通过最后一个参数来区分的, 0:释放 1:查看。代码[4]:如果是cpio-initrd则将其内容释放出来到rootfs中。代码[5]:如果不是cpio-initrd,则认为是一个image-initrd,将其内容保存到/initrd.image中。在后面的image-initrd的处理代码中会读取/initrd.image。对image-initrd的处理 在prepare_namespace函数里,包含了对image-initrd进行处理的代码,相关代码如下:void __init prepare_namespace(void){[1]if (initrd_load())goto out;out:umount_devfs("/dev");[2]sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");security_sb_post_mountroot();mount_devfs_fs ();}代码[1]:执行initrd_load函数,将initrd载入,如果载入成功的话initrd_load函数会将realfs的根设置为当前目录。代码[2]:将当前目录即realfs的根mount为Linux VFS的根。initrd_load函数执行完后,将真正的文件系统的根设置为当前目录。initrd_load函数负责载入image-initrd,代码如下:int __init initrd_load(void){[1]if (mount_initrd) {create_dev("/dev/ram", Root_RAM0, NULL);[2]if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {sys_unlink("/initrd.image");handle_initrd();return 1;}}sys_unlink("/initrd.image");return 0;}代码[1]:如果加载initrd则建立一个ram0设备 /dev/ram。代码[2]:/initrd.image文件保存的就是image-initrd,rd_load_image函数执行具体的加载操作,将image-nitrd的文件内容释放到ram0里。判断ROOT_DEV!=Root_RAM0的含义是,如果你在grub或者lilo里配置了 root=/dev/ram0 ,则实际上真正的根设备就是initrd了,所以就不把它作为initrd处理 ,而是作为realfs处理。handle_initrd()函数负责对initrd进行具体的处理,代码如下:static void __init handle_initrd(void){[1]real_root_dev = new_encode_dev(ROOT_DEV);[2]create_dev("/dev/root.old", Root_RAM0, NULL);mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);[3]sys_mkdir("/old", 0700);root_fd = sys_open("/", 0, 0);old_fd = sys_open("/old", 0, 0);/* move initrd over / and chdir/chroot in initrd root */[4]sys_chdir("/root");sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");mount_devfs_fs ();[5]pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);if (pid > 0) {while (pid != sys_wait4(-1, &i, 0, NULL))yield();}/* move initrd to rootfs' /old */sys_fchdir(old_fd);sys_mount("/", ".", NULL, MS_MOVE, NULL);/* switch root and cwd back to / of rootfs */[6]sys_fchdir(root_fd);sys_chroot(".");sys_close(old_fd);sys_close(root_fd);umount_devfs("/old/dev");[7]if (new_decode_dev(real_root_dev) == Root_RAM0) {sys_chdir("/old");return;}[8]ROOT_DEV = new_decode_dev(real_root_dev);mount_root();[9]printk(KERN_NOTICE "Trying to move old root to /initrd ... ");error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);if (!error)printk("okay\n");else {int fd = sys_open("/dev/root.old", O_RDWR, 0);printk("failed\n");printk(KERN_NOTICE "Unmounting old root\n");sys_umount("/old", MNT_DETACH);printk(KERN_NOTICE "Trying to free ramdisk memory ... ");if (fd < 0) {error = fd;} else {error = sys_ioctl(fd, BLKFLSBUF, 0);sys_close(fd);}printk(!error ? "okay\n" : "failed\n");}handle_initrd函数的主要功能是执行initrd的linuxrc文件,并且将realfs的根目录设置为当前目录。代码[1]:real_root_dev,是一个全局变量保存的是realfs的设备号。代码[2]:调用mount_block_root函数将initrd文件系统挂载到了VFS的/root下。代码[3]:提取rootfs的根的文件描述符并将其保存到root_fd。它的作用就是为了在chroot到initrd的文件系统,处理完initrd之后要,还能够返回rootfs。返回的代码参考代码[7]。代码[4]:chroot进入initrd的文件系统。前面initrd已挂载到了rootfs的/root目录。代码[5]:执行initrd的linuxrc文件,等待其结束。代码[6]:initrd处理完之后,重新chroot进入rootfs。代码[7]:如果real_root_dev在 linuxrc中重新设成Root_RAM0,则initrd就是最终的realfs了,改变当前目录到initrd中,不作后续处理直接返回。代码[8]:在linuxrc执行完后,realfs设备已经确定,调用mount_root函数将realfs挂载到root_fs的 /root目录下,并将当前目录设置为/root。代码[9]:后面的代码主要是做一些收尾的工作,将initrd的内存盘释放。到此代码分析完毕。6.结束语通过本文前半部分对cpio-initrd和imag-initrd的阐述与对比以及后半部分的代码分析,我相信读者对Linux 2.6内核的initrd技术有了一个较为全面的了解。在本文的最后,给出两点最重要的结论:1. 尽管Linux2.6既支持cpio-initrd,也支持image-initrd,但是cpio-initrd有着更大的优势,在使用中我们应该优先考虑使用cpio格式的initrd。2. cpio-initrd相对于image-initrd承担了更多的初始化责任,这种变化也可以看作是内核代码的用户层化的一种体现,我们在其它的诸如FUSE等项目中也看到了将内核功能扩展到用户层实现的尝试。精简内核代码,将部分功能移植到用户层必然是linux内核发展的一个趋势。参考资料从下面三篇文章中,可以获得更多的关于initramfs的知识:[1][2][3]从下面这篇文章中读者可以了解到关于linux VSF、rootfs的相关知识:[4] http://www.ibm.com/developerworks/cn/linux/l-vfs/下面是一些initrd的参考资料:[5]关于作者李大治,软件工程师,目前从事Linux平台下网络安全产品的开发工作,您可以通过dazhi.li@gmail.com同他取得联系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/395461.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VBS基础篇 - 常量

VBS基础篇 - 常量 常量&#xff1a;指的是在程序运行过程中其值保持不变的量&#xff0c;它用来保存固定不变的数值&#xff0c;字符串等常数 。 常量的定义&#xff1a;在vbscript中使用使用 Const 指令可以创建名称具有一定含义的字符串型或数值型常量&#xff0c;并给它们赋…

为什么虚拟助手的前途一片光明

by Steve史蒂夫(Steve) 为什么虚拟助手的前途一片光明 (Why the future is bright for Virtual Assistants) I purchased my first mobile phone in the summer of 1999. I was 17 years old, growing up in a nondescript town in the Midlands of the United Kingdom. The p…

利用深度学习来预测股票价格变动

https://www.toutiao.com/i6644852565341110791/ 利用深度学习来预测股票价格变动&#xff08;长文&#xff0c;建议收藏&#xff09; 原创 不靠谱的猫 2019-01-10 21:01:39完整架构概述 在这篇文章中&#xff0c;我将创建一个预测股票价格变动的完整过程。我们将使用生成对抗网…

C语言 用链表对学号进行排序,求解C语言中建立一个对链表按照学号进行排序的问题...

功能&#xff1a;选择排序(由小到大)返回&#xff1a;指向链表表头的指针*//*选择排序的基本思想就是反复从还未排好序的那些节点中&#xff0c;选出键值(就是用它排序的字段&#xff0c;我们取学号num为键值)最小的节点&#xff0c;依次重新组合成一个链表。我认为写链表这类程…

字符集(CHARACTER SET)和校对集(COLLATE)

http://blog.sina.com.cn/s/blog_9707fac301016wxm.html http://www.th7.cn/db/mysql/201412/84636.shtml 从上文中可以看出character_set_connection、character_set_client、 character_set_results三个字符集什么时候用到。从实际上可以看到&#xff0c;当客户端连接服务器的…

shell 本地接口自动化

一.基于http/https的接口 一般情况下&#xff0c;当前大多公司在做接口自动化的时候都会使用一些工具&#xff1b;比如&#xff1a;postman/jmeter/python自研开发接口平台。。。 以上的情况&#xff0c;都是在源码与测试使用分离的情况下实践的。也就是说&#xff1a;目前国内…

hitchhiker部署_《 Hitchhiker的Python机器学习指南》

hitchhiker部署by Conor Dewey由Conor Dewey 《 Hitchhiker的Python机器学习指南》 (The Hitchhiker’s Guide to Machine Learning in Python) 提供实施代码&#xff0c;教学视频等 (Featuring implementation code, instructional videos, and more) 趋势 (The Trend) Machi…

CAD库中列举所有航路点

select distinct f1.airway_point_name,f1.latitude,f1.longitude,upper(f1.airway_point_type_name)type,f2.code_fir from airway_ordered_point f1, v_airway_point f2where f2.significant_point_idf1.airway_point_idorder by code_fir, type,airway_point_name转载于:htt…

第50次二级c语言真题,2006年4月全国计算机等级考试二级C语言笔试试卷含答案

一、选择题((1)一(10)每题2分&#xff0c;(11)一(50)每题1分&#xff0c;共60分)下列各题A)、B)、C)、D)四个选项中&#xff0c;只有一个选项是正确的&#xff0c;请将正确选项涂写在答题卡相应位置上&#xff0c;答在试卷上不得分。(1)下列选项中不属于结构化程序设计方法的是…

python hashlib模块

摘要算法简介 Python的hashlib提供了常见的摘要算法&#xff0c;如MD5&#xff0c;SHA1等等。 什么是摘要算法呢&#xff1f;摘要算法又称哈希算法、散列算法。它通过一个函数&#xff0c;把任意长度的数据转换为一个长度固定的数据串&#xff08;通常用16进制的字符串表示&…

TZOJ 5101 A Game(区间DP)

描述 Consider the following two-player game played with a sequence of N positive integers (2 < N < 100) laid onto a 1 x N game board. Player 1 starts the game. The players move alternately by selecting a number from either the left or the right end o…

国家职业标准职业编码查询_为什么我学会编码而不是从事金融职业

国家职业标准职业编码查询by Amir Ghafouri通过阿米尔加富里(Amir Ghafouri) 为什么我学会编码而不是从事金融职业 (Why I learned to code instead of pursuing a career in finance) Last year I faced a major life and career decision: commit to pursuing a Chartered F…

go tool trace goalng调优工具

为什么80%的码农都做不了架构师&#xff1f;>>> 你想知道你的Go程序在做什么吗&#xff1f; go tool trace 可以向你揭示&#xff1a;Go程序运行中的所有的运行时事件。 这种工具是Go生态系统中用于诊断性能问题时&#xff08;如延迟&#xff0c;并行化和竞争异常…

程序员 文本编辑器 c语言,程序员必备的五款文本编辑器

原标题&#xff1a;程序员必备的五款文本编辑器程序员的工作离不开文本编辑器&#xff0c;有人说一个txt就能搞定&#xff0c;但txt面对如今复杂的要求&#xff0c;明显有些捉襟见肘&#xff0c;下面推荐五款超级好用的文本编辑器及搭配软件&#xff0c;绝对是程序员的大爱。程…

PCH文件的创建和配置

1.PCH文件的的创建 (1)CommandN (2)打开新建文件窗口:ios->other->PCH file&#xff0c;创建一个pch文件 2.PCH文件的配置 (1)在工程的TARGETS里边Building Setting中搜索Prefix Header (2)然后在Precompile Prefix Header下边的Prefix Header右边双击&#xff0c;添加刚…

ci 数据库异常捕获_系统地捕获错误:如何通过4个步骤构建GitLab CI测试管道

ci 数据库异常捕获by Joyz通过乔伊斯 系统地捕获错误&#xff1a;如何通过4个步骤构建GitLab CI测试管道 (Catch bugs systematically: how to build a GitLab CI testing pipeline in 4 steps) Your first app is a hit the day it’s launched. But one week later, you rea…

(小白)函数一: 声明函数的方法—语句定义法和表达式定义法的区别

一、函数的定义&#xff1a; 在说明什么是函数前先举一个小例子&#xff1a; 大家都知道印刷术是我国的四大发明&#xff08;科普一下&#xff1a;中国四大发明&#xff1a;造纸术、印刷术、火药、指南针&#xff09;之一&#xff0c;之所以有印刷术&#xff0c;是因为重复的抄…

android限制输入字符的范围,Android EditText 对输入字数和内容范围进行限制

在做定制机时&#xff0c;对光敏值进行范围控制时&#xff0c;以及对区号输入时遇到对输入字数以及输入内容的显示。找了好多方法&#xff0c;终于找到了几种方法其中EditText的addTextChangedListener功不可没。例如对光敏值要在0到61之间。大于61时要在输入框中自动变为61.代…

vue13过滤器 debounce延迟、limitBy、filterBy、orderBy

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>智能社——http://www.zhinengshe.com</title><meta name"viewport" content"widthdevice-width, initial-scale1.0, maximum…

Sass:一种CSS预处理器语言

http://sass-lang.com/ Sass是一种CSS预处理器语言&#xff0c;通过编程方式生成CSS代码。因为可编程&#xff0c;所以操控灵活性自由度高&#xff0c;方便实现一些直接编写CSS代码较困难的代码。 同时&#xff0c;因为Sass是生成CSS的语言&#xff0c;所以写出来的Sass文件是不…