LINUX PID 1和SYSTEMD 专题

 

 

Linux下有3个特殊的进程,idle进程(PID = 0), init进程(PID = 1)和kthreadd(PID = 2)html

idle进程其pid=0,其前身是系统建立的第一个进程,也是惟一一个没有经过fork或者kernel_thread产生的进程。shell


各个进程的主要职责:编程

* idle进程(PID = 0,swapper,也叫idle)由系统自动建立, 运行在内核态 ,建立了第一个用户进程(init进程(PID = 1) /sbin/init)和经过kernel_thread建立第一个内核进程kthreadd(PID = 2) 以后idle进程(PID = 0)进入idle状态,
当没有进程能够被调度的时候运行该进程,不作具体的事情。即 完成加载系统后,演变为进程调度、交换。ubuntu

*init进程完成系统的初始化. 是系统中全部其它用户进程的祖先进程。主要做用是处理僵尸进程。当某个父进程比子进程提早消亡时,父进程会给子进程从新寻找“养父进程”,通常就是进程1,由进程1负责处理该子进程的消亡。
   首先Linux内核启动,而后在用户空间中启动init进程,再启动其余系统进程。在系统启动完成完成后,init将变为守护进程监视系统其余进程。缓存

*kthreadd进程由idle经过kernel_thread建立,并始终运行在内核空间, 负责全部内核线程的调度和管理 
它的任务就是管理和调度其余内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的做用就是运行kthread_create_list全局链表中维护的kthread, 当咱们调用kernel_thread建立的内核线程会被加入到此链表中,所以全部的内核线程都是直接或者间接的以kthreadd为父进程服务器

 

 

 

进程之间的关系:父子关系、兄弟关系
task_struct的 real_parent成员指向父进程,
parent成员指向“养父进程”;
children成员表示该进程的子进程链表;
sibling成员表示该进程的兄弟进程链表。网络

 

要说清 Systemd,得先从 Linux 操做系统的启动提及。Linux 操做系统的启动首先从 BIOS 开始,而后由 Boot Loader 载入内核,并初始化内核。内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程,PID 为 1,又叫超级进程,也叫根进程。它负责产生其余全部用户进程。全部的进程都会被挂在这个进程下,若是这个进程退出了,那么全部的进程都被 kill 。若是一个子进程的父进程退了,那么这个子进程会被挂到 PID 1 下面。(注:PID 0 是内核的一部分,主要用于内进换页,参看:Process identifier架构

  SysV Init并发

  PID 1 这个进程很是特殊,其主要就职务是把整个操做系统带入可操做的状态。好比:启动 UI – Shell 以便进行人机交互,或者进入 X 图形窗口。传统上,PID 1 和传统的 Unix System V 相兼容的,因此也叫 sysvinit,这是使用得最悠久的 init 实现。Unix System V 于 1983 年 release。app

  在 sysvint 下,有好几个运行模式,又叫 runlevel。好比:常见的 3 级别指定启动到多用户的字符命令行界面,5 级别指定启起到图形界面,0 表示关机,6 表示重启。其配置在 /etc/inittab 文件中。

  与此配套的还有 /etc/init.d/ 和 /etc/rc[X].d,前者存放各类进程的启停脚本(须要按照规范支持 startstop子命令),后者的 X 表示不一样的 runlevel 下相应的后台进程服务,如:/etc/rc3.d 是 runlevel=3 的。 里面的文件主要是 link 到  /etc/init.d/ 里的启停脚本。其中也有必定的命名规范:S 或 K 打头的,后面跟一个数字,而后再跟一个自定义的名字,如:S01rsyslogS02ssh。S 表示启动,K表示中止,数字表示执行的顺序。

  UpStart

  Unix 和 Linux 在 sysvint 运做多年后,大约到了 2006 年的时候,Linux 内核进入 2.6 时代,Linux 有了不少更新。而且,Linux 开始进入桌面系统,而桌面系统和服务器系统不同的是,桌面系统面临频繁重启,并且,用户会很是频繁的使用硬件的热插拔技术。因而,这些新的场景,让 sysvint 受到了不少挑战。

  好比,打印机须要 CUPS 等服务进程,可是若是用户没有打机印,启动这个服务彻底是一种浪费,而若是不启动,若是要用打印机了,就没法使用,由于sysvint 没有自动检测的机制,它只能一次性启动全部的服务。另外,还有网络盘挂载的问题。在 /etc/fstab中,负责硬盘挂载,有时候还有网络硬盘(NFS 或 iSCSI)在其中,可是在桌面机上,有极可能开机的时候是没有网络的, 因而网络硬盘都不能够访问,也没法挂载,这会极大的影响启动速度。sysvinit 采用 netdev 的方式来解决这个问题,也就是说,须要用户本身在 /etc/fstab 中给相应的硬盘配置上 netdev 属性,因而 sysvint 启动时不会挂载它,只有在网络可用后,由专门的 netfs 服务进程来挂载。这种管理方式比较难以管理,也很容易让人掉坑。

  因此,Ubuntu 开发人员在评估了当时几个可选的 init 系统后,决定从新设计这个系统,因而,这就是咱们后面看到的 upstart 。 upstart 基于事件驱动的机制,把以前的彻底串行的同步启动服务的方式改为了由事件驱动的异步的方式。好比:若是有U盘插入,udev 获得通知,upstart 感知到这个事件后触发相应的服务程序,好比挂载文件系统等等。由于使用一个事件驱动的玩法,因此,启动操做系统时,不少没必要要的服务能够不用启动,而是等待通知,lazy 启动。并且事件驱动的好处是,能够并行启动服务,他们之间的依赖关系,由相应的事件通知完成。

  upstart 有着很不错的设计,其中最重要的两个概念是 Job 和 Event。

  Job 有通常的 Job,也有 service 的 Job,而且,upstart 管理了整个 Job 的生命周期,好比:Waiting, Starting, pre-Start, Spawned, post-Start, Running, pre-Stop, Stopping, Killed, post-Stop 等等,并维护着这个生命周期的状态机。

  Event 分红三类,signalmethod 和 hookssignal 就是异步消息,method 是同步阻塞的。hooks 也是同步的,但介于前面二者之间,发出 hook 事件的进程必须等到事件完成,但不检查是否成功。

  可是,upstart 的事件很是复杂,也很是纷乱,各类各样的事件(事件没有归好类)致使有点凌乱。不过由于整个事件驱动的设计比以前的 sysvinit 来讲好太多,因此,也深得欢迎。

  Systemd

  直到 2010 的有一天,一个在 RedHat 工做的工程师 Lennart Poettering 和 Kay Sievers ,开始引入了一个新的 init 系统—— systemd。这是一个很是很是有野心的项目,这个项目几乎改变了全部的东西,systemd 不但想取代已有的 init 系统,并且还想干更多的东西。

  Lennart 赞成 upstart 干的不错,代码质量很好,基于事件的设计也很好。可是他以为 upstart 也有问题,其中最大的问题仍是不够快,虽然 upstart 用事件能够达到必定的启动并行度,可是,本质上来讲,这些事件仍是会让启动过程串行在一块儿。  如:NetworkManager 在等 D-Bus 的启动事件,而 D-Bus 在等 syslog 的启动事件。

  Lennart 认为,实现上来讲,upstart 实际上是在管理一个逻辑上的服务依赖树,可是这个服务依赖树在表现形式上比较简单,你只须要配置——“启动 B 好了就启动A”或是“中止了A后就中止B”这样的规则。可是,Lennart 说,这种简单实际上是有害的(this simplification is actually detrimental)。他认为,

  • 从一个系统管理的角度出来,他一开始会设定好整个系统启动的服务依赖树,可是这个系统管理员要人肉的把这个原本就很是干净的服务依整树给翻译成计算机看的懂的 Event/Action 形式,并且 Event/Action 这种配置方式是运行时的,因此,你须要运行起来才知道是什么样的。
  • Event 逻辑从头到脚处处都是,这个事件扩大了运维的复杂度,还不如以前的 sysvint。 也就是说,当用户配置了 “启动 D-Bus 后请启动 NetworkManager”, 这个 upstart 能够干,可是反过来,若是,用户启动 NetworkManager,咱们应该先去启动他的前置依赖 D-Bus,然而你还要配置相应的反向 Event。原本,我只须要配置一条依赖的,结果如今我要配置不少不少状况下的 Event。
  • 最后,upstart 里的 Event 的并不标准,很混乱,没有良好的定义。好比:既有,进程启动,运行,中止的事件,也有 USB 设备插入、可用、拔出的事件,还有文件系统设备 being mounted、 mounted 和 umounted 的事件,还有 AC 电源线链接和断开的事件。你会发现,这进程启停的、USB 的、文件系统的、电源线的事件,看上去长得很像, 可是没有被标准化抽像出来掉,由于绝大多数的事件都是三元组:start, condition, stop 。这种概念设计模型并无在 upstart 中出现。由于 upstart 被设计为单一的事件,而忽略了逻辑依赖。

  固然,若是 systemd 只是解决 upstart 的问题,他就改造 upstart 就行了,可是 Lennart 的野心不仅是想干个这样的事,他想干的更多。

  首先,systemd 清醒的认识到了 init 进程的首要目标是要让用户快速的进入能够操做 OS 的环境,因此,这个速度必定要快,越快越好,因此,systemd 的设计理念就是两条:

  • To start less.
  • And to start more in parallel.

  也就是说,按需启动,能不启动就不启动,若是要启动,能并行启动就并行启动,包括大家之间有依赖,我也并行启动。按需启动还好理解,那么,有依赖关系的并行启动,它是怎么作到的?这里,systemd 借鉴了 MacOS 的 Launchd 的玩法(在 Youtube 上有一个分享——Launchd: One Program to Rule them All,在苹果的开源网站上也有相关的设计文档——About Daemons and Services

  要解决这些依赖性,systemd 须要解决好三种底层依赖—— Socket, D-Bus ,文件系统。

  • Socket 依赖。若是服务C依赖于服务S的 socket,那么就要先启动S,而后再启动C,由于若是C启动时找不到S的 Socket,那么C就会失败。systemd 能够帮你在S尚未启动好的时候,创建一个 socket,用来接收全部的C的请求和数据,并缓存之,一旦S所有启动完成,把 systemd 替换好的这个缓存的数据和 Socket 描述符替换过去。
  • D-Bus 依赖D-Bus 全称 Desktop Bus,是一个用来在进程间通讯的服务。除了用于用户态进程和内核态进程通讯,也用于用户态的进程以前。如今,不少的如今的服务进程都用 D-Bus 而不是 Socket 来通讯。好比:NetworkManager 就是经过 D-Bus 和其它服务进程通信的,也就是说,若是一个进程须要知道网络的状态,那么就必须要经过 D-Bus 通讯。D-Bus 支持 “Bus Activation”的特性。也就是说,A要经过 D-Bus 服务和B通信,可是B没有启动,那么 D-Bus 能够把B起来,在B启动的过程当中,D-Bus 帮你缓存数据。systemd 能够帮你利用好这个特性来并行启动 A 和 B。
  • 文件系统依赖。系统启动过程当中,文件系统相关的活动是最耗时的,好比挂载文件系统,对文件系统进行磁盘检查(fsck),磁盘配额检查等都是很是耗时的操做。在等待这些工做完成的同时,系统处于空闲状态。那些想使用文件系统的服务彷佛必须等待文件系统初始化完成才能够启动。systemd 参考了 autofs 的设计思路,使得依赖文件系统的服务和文件系统自己初始化二者能够并发工做。autofs 能够监测到某个文件系统挂载点真正被访问到的时候才触发挂载操做,这是经过内核 automounter 模块的支持而实现的。好比一个 open ()系统调用做用在 “/misc/cd/file1” 的时候,/misc/cd 还没有执行挂载操做,此时 open () 调用被挂起等待,Linux 内核通知 autofsautofs 执行挂载。这时候,控制权返回给 open () 系统调用,并正常打开文件。

  下图来自 Lennart 的演讲里的一页 PPT,展现了不一样 init 系统的启动。

  除此以外,systemd 还在启动时管理好了一些下面的事。

  用C语言取代传统的脚本式的启动。前面说过,sysvint 用 /etc/rcX.d 下的各类脚本启动。然而这些脚本中须要使用 awksedgrepfindxargs 等等这些操做系统的命令,这些命令须要生成进程,生成进程的开销很大,关键是生成完这些进程后,这个进程就干了点屁大的事就退了。换句话说就是,我操做系统干了那么多事为你拉个进程起来,结果你就把个字串转成小写就退了,把我操做系统当什么了?

  在正常的一个 sysvinit 的脚本里,可能会有成百上千个这样的命令。因此,慢死。所以,systemd 全面用 C 语言所有取代了。通常来讲,sysvinit 下,操做系统启动完成后,用 echo $$ 能够看到,pid 被分配到了上千的样子,而 systemd 的系统只是上百。

  另外,systemd 是真正一个能够管住服务进程的——能够跟踪上服务进程所 fork/exec 出来的全部进程。

  • 咱们知道, 传统 Unix/Linux 的 Daemon 服务进程的最佳实践基本上是这个样子的 (具体过程可参看这篇文章“SysV Daemon”)——
    1. 进程启动时,关闭全部的打开的文件描述符(除了标准描述符0,1,2),而后重置全部的信号处理。
    2. 调用 fork () 建立子进程,在子进程中 setsid (),而后父进程退出(为了后台执行)
    3. 再子程中,再调用一次 fork (),建立孙子进程,肯定没有交互终端。而后子进程退出。
    4. 在孙子进程中,把标准输入标准输出标准错误都连到 /dev/null 上,还要建立 pid 文件,日志文件,处理相关信号 ……
    5. 最后才是真正开始提供服务。
  • 在上面的这个过程当中,服务进程除了两次 fork 外还会 fork 出不少不少的子进程(好比说一些 Web 服务进程,会根据用户的请求连接来 fork 子进程),这个进程树是至关难以管理的,由于,一旦父进程退出来了,子进程就会被挂到 PID 1 下,因此,基本上来讲,你没法经过服务进程自已给定的一个 pid 文件来找到全部的相关进程(这个对开发者的要求过高了),因此,在传统的方式下用脚本启停服务是至关至关的 Buggy 的,由于没法作对全部的服务生出来的子子孙孙作到监控。
  • 为了解决这个问题,upstart 经过变态的 strace 来跟踪进程中的 fork () 和 exec () 或 exit () 等相关的系统调用。这种方法至关笨拙。 systemd 使用了一个很是有意思的玩法来 tracking 服务进程生出来的全部进程,那就是用 cgroup (我在 Docker 的基础技术“cgroup 篇”中讲过这个东西)。cgroup 主要是用来管理进程组资源配额的事,因此,不管服务如何启动新的子进程,全部的这些相关进程都会同属于一个 cgroup,因此,systemd 只须要简单的去遍历一下相应的 cgroup 的那个虚文件系统目录下的文件,就能够正确的找到全部的相关进程,并将他们一一中止。

  另外,systemd 简化了整个 daemon 开发的过程:

  • 不须要两次 fork (),只须要实现服务自己的主逻辑就能够了。
  • 不须要 setsid ()systemd 会帮你干
  • 不须要维护 pid 文件systemd 会帮处理。
  • 不须要管理日志文件或是使用syslog,或是处理HUP的日志 reload 信号。把日志打到 stderr 上,systemd 帮你管理。
  • 处理 SIGTERM 信号,这个信号就是正确退出当前服务,不要作其余的事。
  • ……

  除此以外,systemd 还能——

  • 自动检测启动的服务间有没有环形依赖。
  • 内建 autofs 自动挂载管理功能。
  • 日志服务。systemd 改造了传统的 syslog 的问题,采用二进制格式保存日志,日志索引更快。
  • 快照和恢复。对当前的系统运行的服务集合作快照,并能够恢复。
  • ……

  还有好多好多,他接管不少不少东西,因而就让不少人不爽了,由于他在干了不少本不属于 PID 1 的事。

  Systemd 争论和八卦

  因而 systemd 这个东西成了多是有史以来口水战最多的一个开源软件了。systemd 饱受各类争议,最大的争议就是他破坏了 Unix 的设计哲学(相关的哲学能够读一下《Unix 编程艺术》),干了一个大而全并且至关复杂的东西。固然,Lennart 并不一样意这样的说法,他后来又写一篇 blog “The Biggest Myths”来解释 systemd 并非这样的,你们能够前往一读。

  这个争议大到什么样子呢?2014 年,Debian Linux 由于想准备使用 systemd 来做为标准的 init 守护进程来替换 sysvinit 。而围绕这个事的争论达到了空前的热度,争论中充满着仇恨,systemd 的支持者和反对者都在互相辱骂,致使当时 Debian 阵营开始分裂。还有人给 Lennart 发了死亡威胁的邮件,用比特币雇凶买杀手,扬言要取他的性命,在 Youbute 上传了侮辱他的歌曲,在 IRC 和各类社交渠道上给他发下流和侮辱性的消息。这已经不是争议了,而是一种彻彻底底的仇恨!

  因而,Lennart 在 Google Plus 上发了贴子,批评整个 Linux 开源社区和 Linus 本人。他大意说,

这个社区太病态了,全是 ass holes,大家不停用各类手段在各类地方用不一样的语言和方式来侮辱和漫骂我。我仍是一个年轻人,我历来没有经历过这样的场面,可是今天我已经对这种场面很熟悉了。我有时候说话可能不许确,可是我不会像他样那样说出那样的话,我也没有被这些事影响,由于我脸皮够厚,因此,为何我能够在如何大的反对声面前让 systemd 成功,可是,大家 Linux 社区太可怕了。大家里面的有精神病的人太多了。另外,对于 Linus Torvalds,你是这个社区的 Role Model,但惋惜你是一个 Bad Role Model,你在社区里的刻薄和侮辱性的言行,基本从必定程度上鼓励了其它人跟你同样,固然,并不仅是你一我的的问题,而是在你周围汇集了一群和你同样的这样干的人。
送你一句话—— 
A fish rots from the head down !
一条鱼是从头往下腐烂的……

  这篇契文很长,喜欢八卦的同窗能够前往一读。感觉一下 Lennart 当时的心态(我以为能算上是很是平稳了)。

  Linus 也在被一媒体问起 systemd 这个事来(参看“Torvalds says he has no strong opinions on systemd”),
       Linus 在采访里说,

我对 systemd 和 Lennart 的贴子没有什么强烈的想法。虽然,传统的 Unix 设计哲学—— “Do one thing and Do it well”,很不错,并且咱们大多数人也实践了这么多年,可是这并不表明全部的真实世界。在历史上,也不仅有systemd 这么干过。可是,我我的仍是 old-fashioned 的人,至少我喜欢文本式的日志,而不是二进制的日志。可是 systemd 没有必要必定要有这样的品味。哦,我说细节了……

  今天,systemd 占据了几乎全部的主流的 Linux 发行版的默认配置,包括:Arch Linux、CentOS、CoreOS、Debian、Fedora、Megeia、OpenSUSE、RHEL、SUSE 企业版和 Ubuntu。并且,对于 CentOS, CoreOS, Fedora, RHEL, SUSE 这些发行版来讲,不能没有 systemd。(Ubuntu 还有一个不错的 wiki – Systemd for Upstart Users 阐述了如何在二者间切换)

  其它

  还记得在《缓存更新的套路》一文中,我说过,若是你要作好架构,首先你得把计算机体系结构以及不少老古董的基础技术吃透了。由于里面会有不少能够借鉴和相通的东西。那么,你是否从这篇文章里看到了一些有分布式架构类似的东西?

  好比:从 sysvinit 到 upstart 再到 systemd,像不像是服务治理?Linux 系统下的这些服务进程,是否是很像分布式架构中的微服务?还有那个D-Bus,是否是很像 SOA 里的 ESB?而 init 系统是否是很像一个控制系统?甚至像一个服务编排(Service Orchestration)系统?

  分布式系统中的服务之间也有不少依赖,因此,在启动一个架构的时候,若是咱们能够作到像 systemd 那样并行启动的话,那么是否是就像是一个微服务的玩法了?

  嗯,你会发现,技术上的不少东西是相通的,也是互相有对方的影子,因此,其实技术并很少。关键是咱们学在了表面仍是看到了本质。

https://news.cnblogs.com/n/573985/

相关文章
相关标签/搜索