探索 Linux 系统的启动过程
引言##
之所以想到写这些东西,那是因为我确实想让大家也和我一样,把 Linux 桌面系统打造成真真正正日常使用的工具,而不是安装之后试用几把再删掉。我是真的在日常生活和工作中都使用 Linux,比如在 Linux 下编程、写博客、写论文和做幻灯。当然,对于 LibreOffice 这样的软件使用起来都不会有什么困难,所以在我的博客中就基本没有提到,就像 Windows 下的程序员不会去写 MS Office 的使用指南一样。如果有人不能坚持使用 Linux,那一定是 Linux 中的某些困难打败了他。刚使用 Linux 时确实会碰到很多困难,比如界面不够美观啊、字体不够顺眼啊、输入法太难用啊,还有就是想做某个工作找不到工具、想改一个属性找不到入口、系统崩溃了无法恢复等等,甚至是刚安装一个软件包或者下载一个文件,却不知道放到文件系统的什么地方了。所以,我的这一个系列就是在向大家展示我的方法论,用什么软件不重要,怎么用好也不重要,而是碰到问题了,用什么思路去找答案,到哪里去找答案。在折腾 Linux 的旅途中,了解 Linux 系统的启动过程也是很重要的一环。
关于 Linux 系统的启动过程也是几经变革。首先是操作系统从磁盘载入的过程发生了变化,很老旧的操作系统需要自己写启动扇区,现在的操作系统早就将这个过程交给了专门的启动工具。最常用的操作系统加载程序是 Grub,这个名字大家肯定早就如雷灌耳了,而且 Grub 也已经从第 1 版发展到了第 2 版,我们称之为 Grub2。 Grub2 和 Grub1 有很大的差别,网络上有很多讲 Linux 的文章动不动就拿 Grub1 说事儿,比如让大家修改 menu.lst 文件这样的,都是很过时的内容了。关于 Grub2 的详细资料可以阅读文档info grub或者info grub2。Grub 的优点是支持多操作系统的启动,Windows、Linux、BSD 都不在话下,但是它们的启动协议是不一样的,启动 Windows 靠的是 chainloader,启动 Linux 直接使用 linux 命令,而 BSD 系统好像是使用的 multiboot,特别是 Grub 的 multiboot 规范对那些很想自己写操作系统的程序员很有用,因为再也不用从启动扇区的代码开始写了,可以直接进入保护模式用 C 语言写操作系统。Linux 系统在启动的时候还有 initrd 或 initramfs 机制,这个也需要 Linux 用户特别注意。initrd 或 initramfs 也经历过一次技术革新,最开始采用的是 RamDisk 技术,目前采用的 tmpfs 技术。最后,内核加载入内存后,会将系统初始化的控制权交给 init 程序,而 init 程序也不是一成不变的,最开始使用的是 SysVinit,后来又改成 Upstart,现在最流行的又变成了 systemd。现在仍然有很多书和网文讲的还是 SysVinit 时代的做法,当然这些文章只能以 RedHat EL 5 以及之前的发行版作为示例,而对于最新的发行版,这些文章中讲的方法又会给新入门的用户造成困难。
探索 Linux 系统的启动过程是每一个 Linux 用户进步到一定程度后必然要去做的一件事。有的人可能只是好奇,也有的人可能确实需要从启动过程中去解决某一个特定的问题。这种探索我进行过多次,也早就想写一写,但是总是写不好。这一次是借着 systemd 普及的东风,让这个探索过程成为我这一系列的里程碑。为什么说是借着 systemd 普及的东风呢?因为听说本月即将发布的 Ubuntu 15.04 版本将会从 upstart 更换为 systemd,目前正在紧张地测试中。当然,一下子迁移过来对开发人员来讲还是有点难度的,但是只要确定目标,即使 Ubuntu 15.04 不使用 systemd,到 15.10 也肯定是它了。因此,对于我这个喜欢折腾多个 Linux 发行版的人来说,就不用多线作战了,只需要学好 systemd 即可。
Linux 启动过程概述##
这里先简单列一下 Linux 操作系统启动的全过程:
- 按下电脑的电源键后,电脑通电,BIOS 启动;
 - BIOS 读取硬盘的 MBR,运行启动扇区中的代码,旧系统往往需要自己写启动扇区,而新系统基本上由专用的启动软件接管了,在 Linux 世界中,目前都是用的 Grub2。由于启动扇区空间太小,放不下太复杂的代码逻辑,所以 Grub2 也使用了多阶段启动的策略;
 - Grub2 负责将操作系统内核加载到内存,如果有必要,也会把 initramfs 文件加载到内存,然后将控制权交给内核;
 - 内核进行初始化,内核的初始化过程结束后,就会把控制权交给
/init程序,从此进入用户空间; - 因为内核先是将 initramfs 文件挂在为根文件系统,所以刚开始运行的
/init程序其实是 initramfs 文件中的,所以该文件需要的重要的初始化脚本、内核模块、配置文件等,都位于 initramfs 文件中,这也是为什么很多时候我们修改了某些配置文件后,需要先更新 initramfs 文件再重启操作系统才会生效; - initramfs 文件中的
/init程序负责挂载硬盘上的文件系统,然后再把根文件系统切换到硬盘上的根分区,再运行/sbin/init程序,这时所有程序、配置文件、脚本都是使用的硬盘上的了,当然,网络文件系统也是同理。可以看出 init 程序的运行也是一个分阶段的过程; /sbin/init程序负责系统的初始化、各种服务的运行、用户的登陆等等;- 如果需要运行图形界面,则
/sbin/init程序会运行 Display Manager,在 Fedora 中是 gdm,在 Ubuntu 中是 lightdm。然后 Display Manager 负责启动整个图形界面。 
Grub 加载 Linux 内核和 initramfs##
研究 Linux 启动过程的第一站就是要研究 Grub。大部分时候我们不用考虑 Grub 的安装,因为安装 Linux 桌面系统的时候已经帮我们自动安装好了,我们要做的,最多就是改改 Grub 的配置文件。只有在一种情况下需要手动安装 Grub,那就是在系统崩溃后急救的时候,当然,需要先运行一个光盘版或 U 盘版的 Linux 系统,然后运行grub-install命令,这个命令需要指定将 Grub 安装到那个硬盘的 MBR,具体操作看文档即可。
Grub2 和 Grub 相比,有了很多改进,改进到 Grub2 都相当于一个小型的操作系统了,比如 Grub2 能识别各种格式的硬盘分区及文件系统,能识别各种格式的图像,甚至还能动态加载模块。其中最重要的改变是配置文件由 Grub1 时代的/boot/menu.lst文件变成 Grub2 时代的/boot/grub/grub.cfg文件了(注意,有的系统中是/boot/grub2/grub.cfg,比如 Fedora),配置文件的语法也变复杂了很多。虽然 Grub2 配置文件的语法比较复杂,但是我们一般不需要直接修改该文件,而是修改/etc/default/grub文件后,再使用update-grub命令自动生成配置文件。当然,要完成一些简单任务时对/boot/grub/grub.cfg文件进行直接修改也是可以的,只是一旦系统更新,则对该文件的修改有可能被覆盖。
Grub 最重要的任务就是加载 Linux 内核以及 initramfs 文件了,这几个命令是非常简单易懂的,在/boot/grub/grub.cfg文件中也非常容易找到,如下图:

上图主要是展示 Grub 载入 Linux 内核并且给 Linux 内核传递参数的过程,同时展示载入 initramfs 文件的过程。
探索 initramfs 文件的方法##
Grub 将 Linux 内核和 initramfs 文件载入内存后,就把控制权交给内核,内核会将内存中的 initramfs 文件挂载为根文件系统,当内核初始化完成后需要进入用户空间时,会运行/init,其实这个/init是在 initramfs 文件中的,所以如果想知道系统启动后都干了些什么,研究一下 initramfs 文件中的内容是有必要的。
在/boot目录下,一般都有一个/boot/initrd.img文件或一个/boot/initramfs.img文件,但是要看里面的内容还得有点技巧。先得学习一下历史。第一个问题就是为什么要有 initrd 或者 initramfs,答案是这样的,为了减小 Linux 内核的大小,有一些驱动并没有编译进内核,而是以模块的形式存在于文件系统中,但是有些文件系统呢又需要内核加载了相应的驱动模块才能读取,这样就形成了一个是先有鸡还是先有蛋这样的一个矛盾的问题,为了解决这个问题,就是把这些驱动模块放到 initrd 或 initramfs 文件中,由启动器将其载入内存,然后内核加载内存中的驱动模块,再驱动其它的文件系统,然后进行更多的工作。同时,对于很多嵌入式操作系统而言,可能载入内存的 initrd 或 initramfs 就是最终的文件系统。第二个问题就是 initrd 文件和 initramfs 文件是什么格式的,怎么创建和打开。这个问题有点复杂,且看我下面细细道来。
其实在 Linux 的历史中,内存中的文件系统使用的技术还不一样。在很老的系统中,是将某一块内存模拟成磁盘,称之为 RamDisk,这个技术效率不高,为什么不高呢,那是因为将内存模拟成磁盘后,依然需要像对待磁盘一样对待它,需要给它创建特定格式的文件系统,读取或写入文件的时候还是要用到内核的缓存系统,这样同一份数据就在内存中存在了两份,所以很浪费。老系统中使用的 initrd 文件其实就相当于是一个磁盘的镜像,所以要读取或写入它的内容,就需要将它挂在到系统中,然后进行读写。
现在我们用的都是新系统,initrd 已经被 initramfs 取代了,虽然很多系统中仍然用initrd.img作为文件名,其实使用的是 initramfs 技术。initramfs 技术不使用 RamDisk,而是使用 tempfs,也就是复用了内核中的缓存系统,所以 tempfs 中的内容在内存中只存在一份,那就是在内核的缓存中,不仅节约了内存开销,也提高了效率。那么 initramfs 文件是什么格式呢?它就是经过压缩的 CPIO 格式,只要会cpio,打开它就不是问题。
其实也不是完全没有问题,我在研究 Fedora 21 的 initramfs.img 文件时就碰到了困难。如果大家使用cat initramfs.img | cpio -imd将该 CPIO 文件解开,会发现只有少许几个文件,文件数量和文件大小都远远低于我们的预期,如下图:

问题究竟出在哪里呢?这是因为 Fedora 21 采用了最新的 Early User Space 技术。这个问题在网上还暂时搜不到答案,我也是看到解压出的文件中有一个early_cpio,然后以 early 为关键词去看内核的代码,才知道的。但是关于该技术的具体信息,我也没搞懂。不过这不是重点,重点是如何正确地将该文件解包。首先,我找到了 Fedora 21 系统中用来构建 initramfs 文件的工具是 dracut,采用的方法论请看我该系列的第一篇,如下图:

然后,我发现 dracut 软件包中提供的lsinitrd工具可以查看 initramfs 中的内容,而且该工具是一个脚本。所以我就把它打开看了一下,如下图:

在这个脚本中,我发现它需要用到一个skipcpio程序,跳过 initramfs 文件的头部后,再将剩下的部分当成压缩的 CPIO 文件进行解包。看来,会读脚本程序有时也很重要,所以我的《Bash脚本编程语言中的美学与哲学》这一篇没有白写。最后,解包 initramfs 文件的命令是这样的:
sudo /usr/lib/dracut/skipcpio initramfs-$(uname -r).img | zcat | cpio -imd
如下图:

接下来,我们就可以愉快地研究/init程序都干些什么了。
基于 systemd 的 init 系统##
其实 systemd 在开源社区是有争论的,有很多人反对 systemd,反对的理由是 systemd 太复杂了,它干的事太多了,它除了是一个 init 程序外,同时还负责管理硬件、日志、定时任务等,甚至连用户登录都接管了。很多人仍为 Unix 世界的哲学是只做一件事并做好,对于 systemd 这种什么都要管庞大软件包,很多人是不喜欢的。systemd 的另外一个违反 Unix 哲学的地方就是它的日志系统使用了二进制格式,而不是 Unix 中广泛使用的纯文本格式。
但是我们不要被这些反对意见误导。systemd 真的是一个非常优秀的初始化系统,至少我是在使用过后就喜欢上了它。systemd 对传统所做出的每一个改变都是有道理的,即使是二进制格式的日志,也是为了更好地管理和解析日志,同时也提供了非常完善的查看日志的工具。Linux 之父也说他对 systemd 没有看法,他认为所谓的“只做一件事并做好”的哲学早已过时,该哲学只适合于那些可以通过流水线将各程序组合起来的场景,而现实中有很多复杂的应用场景不能通过流水线解决。不管怎么样,systemd 的使用是越来越普遍,Fedora、RedHat EL 7均以采用 systemd,Ubuntu 的下一个版本也将采用 systemd。所以,学习 systemd 是必须的。
学习 systemd 的最好的途径是查看它的文档man systemd,然后根据该文档的提示查看其它相应的文档,比如man systemd.service、man systemd.target等等。要完整地讲述 systemd 估计都可以写一本书,我下面只说说它的一些基本概念。
系统初始化要做很多工作,如挂在文件系统,启动sshd服务,配置交换分区等,systemd 把每一个工作作为一个 unit,每一个 unit 对应一个配置文件。systemd 又将 unit 分成不同的类型,每一种类型的 unit 其配置文件具有不同的扩展名,常见的 unit 类型有:
- service 对应一个后台服务进程,如 httpd、mysqld 等;
 - soket 对应一个套接字,之后对应到一个service,类似于xinetd的功能;
 - device 对应一个用 udev 规则标记的设备;
 - mount 对应系统中的一个挂载点,systemd 据此进行自动挂载,为了与 SysVinit 兼容,目前 systemd 自动处理 /etc/fstab 并转化为 mount;
 - automount 自动挂载点;
 - swap 配置交换分区;
 - target 配置单元的逻辑分组,包含多个相关的配置单元,可以当成是 SysVinit 中的运行级;
 - timer 定时器,用来定时触发用户定义的操作,它可以用来取代传统的 atd,crond 等;
 
systemd 的配置文件存放于/usr/lib/systemd/system目录中,initramfs 中的配置文件自然是位于相对于其解压目录的usr/lib/systemd/system目录中了。initramfs 中的/init程序以及硬盘上的/sbin/init程序都只是/usr/lib/systemd/systemd程序的链接而已。systemd 启动后,先根据default.target配置文件中的规则初始化系统。其实default.target也是一个链接,如果它链接到multi-user.target,则启动字符界面,如果它链接到graphical.target,则启动图形界面。这个和传统的 SysVinit 中的运行级别有一定的相似性。systemd 中的 target 很多,它和传统的 SysVinit 中的运行级别对应关系如下:
| SysVinit中的运行级别 | systemd中的 target | 意义 | 
|---|---|---|
| 0 | runlevel0.target, poweroff.target | 关闭系统。 | 
| 1, s, single | runlevel1.target, rescue.target | 单用户模式。 | 
| 2, 4 | runlevel2.target, runlevel4.target, multi-user.target | 用户定义/域特定运行级别。默认等同于 3。 | 
| 3 | runlevel3.target, multi-user.target | 多用户,非图形化。用户可以通过多个控制台或网络登录。 | 
| 5 | runlevel5.target, graphical.target | 多用户,图形化。通常为所有运行级别 3 的服务外加图形化登录。 | 
| 6 | runlevel6.target, reboot.target | 重启。 | 
| emergency | emergency.target | 紧急 Shell。 | 
一个 target 会依赖其它的 unit,被依赖的 unit 又会依赖更多的 unit,系统启动时这些 unit 都会同时被运行,和拔出萝卜带出泥的原理是一样的,而且 systemd 是并行启动各个 unit,这样可以加快系统启动的速度。每一个 unit 的配置文件都是纯文本的,语法格式极其简单,可以打开直接阅读。这些配置的细节我就不讲了,文档里面都写得很清楚。对于用户来讲,只要会使用 systemd 的管理工具systemctl命令就可以解决大部分的问题。
我们经常和系统启动打交道的地方是设置某些服务在系统启动时自动运行。回忆一下 RedHat EL 5 之前的系统,要让某个服务在系统启动时运行,我们需要把一些脚本文件放到/etc/rc.d/init.d目录中,然后在相应的/etc/rc.d/rcX.d目录中建立符号链接,并且这些脚本的命名还有讲究。同时,RedHat EL 5 之前的系统还提供了几个工具来简化我们的工作,它们是service命令和chkconfig命令。在现在的新系统中,这几个命令就只能成为回忆了,systemctl命令接管了这些工作。如下表:
| SysVinit 命令 | Systemd 命令 | 意义 | 
|---|---|---|
| service foo start | systemctl start foo.service | 用来启动一个服务 (并不会重启现有的)。 | 
| service foo stop | systemctl stop foo.service | 用来停止一个服务 (并不会重启现有的)。 | 
| service foo restart | systemctl restart foo.service | 用来停止并启动一个服务。 | 
| service foo reload | systemctl reload foo.service | 当支持时,重新装载配置文件而不中断等待操作。 | 
| service foo condrestart | systemctl condrestart foo.service | 如果服务正在运行那么重启它。 | 
| service foo status | systemctl status foo.service | 汇报服务是否正在运行。 | 
| ls /etc/rc.d/init.d/ | systemctl list-unit-files --type=service | 用来列出可以启动或停止的服务列表。 | 
| chkconfig foo on | systemctl enable foo.service | 在下次启动时或满足其他触发条件时设置服务为启用。 | 
| chkconfig foo off | systemctl disable foo.service | 在下次启动时或满足其他触发条件时设置服务为禁用。 | 
| chkconfig foo | systemctl is-enabled foo.service | 用来检查一个服务在当前环境下被配置为启用还是禁用。 | 
| chkconfig –list | systemctl list-unit-files --type=service | 输出在各个运行级别下服务的启用和禁用情况。 | 
| chkconfig foo –list | ls /etc/systemd/system/*.wants/foo.service | 用来列出该服务在哪些运行级别下启用和禁用。 | 
| chkconfig foo –add | systemctl daemon-reload | 当您创建新服务文件或者变更设置时使用。 | 
| telinit 3 | systemctl isolate multi-user.target | 改变至多用户运行级别。 | 
其实systemctl命令也只是把相应的*.service文件放到相应的目录中而已,这也说明,要使用 systemd,并不只是将 SysVinit 或者 upstart 替换成 systemd 就可以了的,所有的其它的和服务相关的软件包,都需要针对 systemd 进行修改。所以,Ubuntu 要走的路还有点长。
在使用了 systemd 的系统中,电源管理也是由 systemd 接管的。之所以提到电源管理,是因为我在笔记本上使用 Linux 时碰到的一个非常奇葩的问题,那就是在我的笔记本电脑上,进入桌面后,总是提示“无线网卡已通过硬件禁用”,只有先将系统挂起,然后再唤醒,无线网卡才能启用。systemd 电源管理的命令如下:
| 命令 | 操作 | 
|---|---|
| systemctl reboot | 重启机器 | 
| systemctl poweroff | 关机 | 
| systemctl suspend | 挂起 | 
| systemctl hibernate | 休眠 | 
| systemctl hybrid-sleep | 混合休眠模式(同时休眠到硬盘并挂起) | 
最后补充一下,systemd 自带日志服务 journald,并且用二进制格式保存所有日志信息,用户使用journalctl命令来查看日志信息。
进入图形界面##
如果作为服务器运行,Linux 系统的启动就仅仅只包含以上过程。如果是使用 Linux 桌面系统,最后还需要进入图形界面。进入图形界面的工作是由显示管理器来完成的,在 Fedora 中,显示管理器程序是 gdm,在 Ubuntu中,是 lightdm。显示管理器负责启动 X Server 并显示登录界面,用户登录后,显示管理器启动 gnome-session 从而进入整个桌面环境。关于 X Window 方面的内容,可以看我写的这一篇《X Window的奥秘》。最后要说一下图形界面的新进展,那就是 Wayland 将要取代 Xorg 成为新的图形技术。关于 Wayland 的优点,请看这两篇《揭开Wayland的面纱(一):X Window的前生今世》、《 揭开Wayland的面纱(二):Wayland应运而生》。这两篇文章发布于2010年,现在已经是2015年了,但是 Wayland 还是没有普及。不过在 Fedora 21 中,已经可以试用 Wayland 了,我试用了一下,貌似还不太稳定。
好了,关于 Linux 系统启动过程的东西就写这么多吧。从以上内容可以看到,Linux 中的技术也是一直不断在进步,我们需要拥抱变化,而不能墨守成规。很多时候,图书和网络教程并不能跟上最新的进展,这是就需要我们自己去思考和探索,因此,掌握正确的方法论尤其重要。
(京山游侠于2015-04-10发布于博客园,转载请注明出处。)
对于现在最新的 init 程序 systemd,很多人可能非常想了解 systemd 在系统启动时启动了哪些服务?这些服务的启动顺序如何?每个服务启动花费了多少时间?如果单纯去查看 systemd 的文档和各个服务的脚本,是非常难以搞清楚以上问题的。幸好 systemd 提供了一个非常好的分析工具 systemd-analyze,它可以帮我们分析各个复位启动的顺序、时间等。这样运行该程序
systemd-analyze plot > init.svg
``
就可以以图形化的形式展示各服务启动的时间轴了,如下图:

当然,systemd-analyze 还有其它的输出方式,请自行查看它的文档。
(京山游侠于2016-10-10更新于博客园,转载请注明出处。)												
											探索 Linux 系统的启动过程的更多相关文章
- Linux系统的启动过程
		
Linux 系统启动过程 Linux系统的启动过程可以分为5个阶段: BIOS自检 内核的引导. 运行init. 系统初始化. 用户登录系统. BIOS自检: BIOS是英文"Basic I ...
 - 详解linux系统的启动过程及系统初始化
		
一.linux系统的启动流程 关于linux系统的启动流程我们可以按步进行划分为如下: POST加电自检 -->BIOS(Boot Sequence)-->加载对应引导上的MBR(boot ...
 - Linux系统在启动过程中mbr主引导程序被破坏的解决方案
		
首先,mbr主引导程序被破坏是指系统在启动过程中,磁头找不到/boot分区(windows的启动分区在c盘). 1)下面我们模拟主引导分区被破坏的情况:(在启动分区划分446M的存储大小) 2)重启( ...
 - linux系统的启动过程简要分析
		
接触linux系统运维已经好几年了,常常被问到linux系统启动流程问题,刚好今天有空来梳理下这个过程:一般来说,所有的操作系统的启动流程基本就是: 总的来说,linux系统启动流程可以简单总结为以下 ...
 - Linux系统在启动过程中启动级别发生错误的解决办法
		
一.系统启动级别一共有六个: 0:系统停机模式,系统不可以正常启动 1:单用户模式, root权限,用于系统的维护,禁止远程登陆 2:多用户模式,没有NFS网络支持 3:完整的多用户文本模式,有NFS ...
 - Linux系统在启动过程中grub引导文件丢失的解决方法
		
在/boot/grub2目录下有一个grub.cfg文件:该文件主要是用来自动地引导系统启动内核程序和系统的初始化程序. 问题一:当系统在启动的情况下,我们不小心删除/boot/grub2/grub. ...
 - Linux 系统的启动过程
		
1.基本概念 BIOS 中文名 主板BIOS 外文名 Basic Input/Output System 全 称 基本输入输出系统(全称是ROM-BIOS,是只读存储器基本输入/输出 ...
 - Linux系统在启动过程中内核文件丢失的解决方法
		
在/boot目录下有两个重要的文件,分别是: vmlinuz-3.10.0-123.el7.x86_64 内核文件 initamfs-3.10.0-123.el7.x86_64.img ...
 - linux系统的启动过程及系统初始化
		
(其中/etc/inittab是一个很重要的文件,值得细究http://www.2cto.com/os/201108/98426.html) 其开头的446字节内容特指为"主引导记录&quo ...
 
随机推荐
- JavaScript 对数据处理的5个API
			
JavaScript对数据处理包括向上取整.向下取整.四舍五入.固定精度和固定长度5种方式,分别对应ceil,floor,round,toFixed,toPrecision等5个API,本文将对这5个 ...
 - 戏说HTML5
			
如果有非技术人员问你,HTML5是什么,你会怎么回答? 新的HTML规范... 给浏览器提供了牛逼能力,干以前不能干的事...(确切地说应该是给浏览器规定了许多新的接口标准,要求浏览器实现牛逼的功能. ...
 - C语言  ·  时间转换
			
问题描述 给定一个以秒为单位的时间t,要求用"<H>:<M>:<S>"的格式来表示这个时间.<H>表示时间,<M>表示分 ...
 - ABP文档 - 后台作业和工作者
			
文档目录 本节内容: 简介 后台作业 关于作业持久化 创建一个后台作业 在队列里添加一个新作业 默认的后台作业管理器 后台作业存储 配置 禁用作业执行 Hangfire 集成 后台工作者 创建一个后台 ...
 - 9、 Struts2验证(声明式验证、自定义验证器)
			
1. 什么是Struts2 验证器 一个健壮的 web 应用程序必须确保用户输入是合法.有效的. Struts2 的输入验证 基于 XWork Validation Framework 的声明式验证: ...
 - UWP开发之Mvvmlight实践六:MissingMetadataException解决办法(.Net Native下Default.rd.xml配置问题)
			
最近完成一款UWP应用,在手机端测试发布版(Release)的时候应用莫名奇妙的强行关闭,而同样的应用包在PC端一点问题都没有,而且Debug版在两个平台都没有问题,唯独手机的Release版有问题. ...
 - 通过微信小程序看前端
			
前言 2016年9月22日凌晨,微信官方通过“微信公开课”公众号发布了关于微信小程序(微信应用号)的内测通知.整个朋友圈瞬间便像炸开了锅似的,各种揣测.介绍性文章在一夜里诞生.而真正收到内测邀请的公众 ...
 - 浅谈java异常[Exception]
			
学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:589809992 我们一起学Java! 一. 异常的定义 在<java编程思想 ...
 - C#使用GET、POST请求获取结果
			
C#使用GET.POST请求获取结果,这里以一个简单的用户登陆为例. 1. 使用GET请求获取结果 1.1 创建LoginHandler.aspx处理页面 protected void Page_Lo ...
 - 微服务与Docker介绍
			
什么是微服务 微服务应用的一个最大的优点是,它们往往比传统的应用程序更有效地利用计算资源.这是因为它们通过扩展组件来处理功能瓶颈问题.这样一来,开发人员只需要为额外的组件部署计算资源,而不需要部署一个 ...