linux操作系统启动过程
第一个进程
- 在传统的
Unix和Linux系统中,init进程是系统启动后的第一个进程(PID为1)- 它是系统的祖先进程,负责启动其他所有的进程
- 在许多现代
Linux发行版中,systemd已经取代了传统的init系统- 在这些系统中,
systemd是第一个进程,并且也是PID为1的进程
- 在这些系统中,
- 不过它们的目的都是相同的:初始化系统,启动其他必要的服务和进程,并管理它们的生命周期
- 不过
systemd提供了许多额外的特性和优势,包括并行启动服务、更精细的依赖关系处理、更先进的日志处理等等
- 不过
init进程做了什么事
- 系统服务守护进程
- 网络设置
- 日志和监控
- 设备管理和挂载文件系统
- 用户空间服务
getttylogin
- 调度和计划任务
- 其他系统级服务
信号
- 信号是操作系统用来通知进程某些事件已经发生的机制
进程组
- 每个进程组都有一个唯一的进程组
ID - 在进程组内的所有进程共享相同的
PGID - 当进程被创建时,它的
PGID通常被设置为其父进程的PGID
创建和更改
- 新进程通常继承其父进程的进程组
ID - 但进程也可以通过系统调用,如
setpgid,更改其自身或其子进程的进程组ID
进程组和信号
- 可以向整个进程组发送信号,从而影响进程组中的所有进程
- 可以使用
kill命令向进程组发送一个信号来终止进程组中的所有进程
- 可以使用
作业控制
- 在
shell环境中,进程组常用于实现作业控制。- 用户可以将一组相关进程作为一个作业来管理,例如将其挂起、恢复或终止。
shell通过管理进程组来实现这些功能
- 用户可以将一组相关进程作为一个作业来管理,例如将其挂起、恢复或终止。
领头进程
- 每个进程组有一个领头进程,其进程
ID与进程组ID相同 - 领头进程的终止不会影响进程组的其他成员
会话
- 进程组可以进一步组织为会话
session - 每个会话可以包含多个进程组,其中一个进程组称为前台进程组,其他进程组称为后台进程组
- 通常,与终端交互的进程运行在前台进程组中,而其他进程运行在后台进程组中
前后台进程组
setsid创建的进程组
- 使用
setsid会创建一个新的会话,然后创建一个新的进程组,这个进程组不属于任何终端的前台或后台进程组,因为它与原始终端已经分离
前台进程组
- 与终端交互的进程组被称为前台进程组
- 当你在命令行执行一个命令时,该命令及其子进程通常运行在一个前台进程组中
- 前台进程组可以从终端接收输入,并将输出发送到终端
- 终端通常在前台进程组完成执行之前等待,不接受其他命令
- 前台进程组中的进程可以收到由键盘生成的信号,例如
CTRL+C(SIGINT)和CTRL+Z(SIGTSTP)
后台进程组
- 运行在后台的进程组称为后台进程组
- 这些进程组通常不与终端直接交互,即使他们的输出可以被重定向到终端或文件
- 可以通过在命令行中的命令后添加
&来启动一个后台进程 - 后台进程组中的进程不响应键盘信号,例如
CTRL+C或CTRL+Z,这些信号只由前台进程组接收 - 后台进程组允许用户在不等待完成的情况下继续在同一终端中执行其他命令
前台后台切换
- 放入后台
- 在启动命令时,在命令末尾添加
&符号可以将进程作为后台进程运行 - 如果进程已经在前台运行,可以按
CTRL+Z暂停进程,然后使用bg命令将其继续在后台运行
- 在启动命令时,在命令末尾添加
- 切到前台
- 可以使用
fg命令将后台进程带到前台运行 - 可以通过
jobs命令查看当前后台进程的列表,并使用fg %jobnumber将指定的后台进程带到前台
- 可以使用
|
1 |
fg |
查看前后台程序
- 使用
jobs命令可以查看当前会话中的前台和后台进程
孤儿进程
- 在
Linux和Unix系统中,孤儿进程(Orphan Process)是一个特殊类型的进程,其父进程已经终止,但该孤儿进程仍然在运行
产生
- 当一个父进程终止或退出时,它的所有子进程将成为孤儿进程
- 这些子进程不会立即被终止,而是由
init进程(或在某些系统中称为systemd进程)接管init进程是Linux系统中的第一个进程,其进程ID(PID)通常为1
- 当被
init进程接管后:init进程会定期地等待其子进程- 当孤儿进程终止时,
init进程会收集相关信息并完成清理工作,以确保资源得到正确释放
僵尸进程
Zombie Process是已经完成执行但还在进程表中的进程- 它们不再执行任何操作,但其进程描述符仍然存在
- 僵尸进程通常是因为父进程没有正确地等待其子进程,从而未能清理子进程的结束状态
区别于孤儿进程
- 孤儿进程是一个正常运行的进程,只不过它的父进程已经终止
- 孤儿进程不占用任何不必要的系统资源,而且被
init进程妥善管理
守护进程
- 守护进程(
Daemon Process)是在Unix和Linux系统中运行的后台进程,用于执行特定的服务或应用程序 - 守护进程通常在系统启动时启动,并在整个系统运行期间一直运行,不与任何终端会话关联
特点
- 没有控制终端
- 守护进程不与任何终端关联,因此不接收用户输入,也不向用户显示输出
- 这使它们适合执行后台任务
- 持续运行
- 守护进程通常在系统启动时启动,并在系统关闭时停止
- 它们的生命周期与系统的运行时间相匹配
- 自动重启
- 许多守护进程设计为在意外终止后自动重启
- 这确保了服务的连续可用性
创建
- 创建守护进程涉及一些特定的步骤,以确保与用户会话的完全分离:
- 调用
fork- 通过调用
fork()创建子进程,父进程立即退出 - 这使得子进程不再是调用它的
shell或程序的子进程
- 通过调用
- 创建新的会话
- 子进程可以调用
setsid()创建一个新的会话,并成为新会话的领导者 - 这确保了与原始控制终端的分离
- 子进程可以调用
- 改变工作目录
- 通常将当前工作目录更改为根目录(
/) - 这确保了守护进程的工作目录不会阻止任何文件系统被卸载
- 通常将当前工作目录更改为根目录(
- 设置文件权限掩码
- 通常使用
umask()设置合适的文件权限掩码, - 以确保守护进程创建的文件具有适当的权限
- 通常使用
- 关闭或重定向文件描述符
- 守护进程通常关闭或重新定向标准输入、输出和错误文件描述符
常见守护进程
httpdweb服务器守护进程
sshdSecure Shell守护进程,用于远程登录
cron- 定时任务守护进程
setsid
|
1 |
pid_t setsid(void); |
- 函数在
Linux和其他UNIX-like系统中用于创建一个新的会话(session) - 当进程调用
setsid时,该进程成为新会话的领导者,并与原有的终端、进程组和会话分离- 这在创建守护进程时非常有用,因为它使进程能够在后台运行,与前台用户界面无关
创建新的会话
- 会话是一个或多个进程组的集合,其中一个进程组是前台进程组,其余的是后台进程组
- 当用户登录时,会创建一个会话,并将终端分配给该会话
分离终端
- 调用
setsid的进程不再与其父进程的控制终端关联- 这意味着该进程及其子进程将无法从终端接收输入或向终端发送输出
创建新的进程组
setsid创建一个新的进程组,并将调用进程设置为该组的领导者- 新的进程组
ID和会话ID都设置为调用进程的PID- 由于进程是其新进程组的领导者,因此它不再是其原进程组的成员
调用限制
- 只有不是进程组的领导者的进程才能调用
setsid- 因此,通常在调用
setsid之前首先调用fork,然后使父进程退出 - 这样可以确保调用
setsid的进程不是进程组的领导者
- 因此,通常在调用
返回值
- 如果成功,则返回新的会话
ID - 如果出现错误,则返回
-1,并设置errno以指示错误
理解
- 调用后按上述步骤:创建新的会话,分离终端,创建新的进程组,设置进程组领导者
fork
- 是一个在
Unix和Linux操作系统中使用的系统调用,用于创建一个新的进程 fork通过复制当前进程创建新的进程,新进程被称为子进程,而原始进程被称为父进程- 这里的“复制”意味着子进程从父进程继承了几乎完全相同的内存映像、环境变量、打开的文件描述符等
返回值
fork在父进程中返回新创建的子进程的PID- 在子进程中返回0
- 如果
fork调用失败(例如,因为达到了系统允许的最大进程数),则返回-1
内存复制
- 子进程的地址空间是父进程地址空间的副本
- 但子进程的数据是独立的,所以父进程和子进程对内存的修改不会影响对方
- 通常,这是通过所谓的写时复制(
Copy-On-Write,COW)技术实现的
- 允许父进程和子进程共享未修改的内存页面,直到其中一个进程试图修改
文件描述符
- 子进程继承了父进程的文件描述符
- 这意味着它们共享相同的文件偏移量等
- 如果子进程写入一个文件,父进程从同一文件描述符读取将从新的位置开始
进程属性继承
- 子进程继承了父进程的许多属性,例如用户ID、组ID、资源限制等
进程关系
- 子进程获得其自身的唯一进程
ID,并将其父进程ID设置为创建它的进程的PID- 这在进程间通信和信号处理等方面很重要
信号与处理程序
- 子进程继承了父进程的信号处理设置,但未决信号被清除
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Linux 高性能服务器编程:服务器程序规范12/04
- ♥ CLion:配置C++下Nasm开发环境(debian)08/06
- ♥ Spdlog记述:二07/09
- ♥ 关于SSH08/18
- ♥ Linux 内存映射与普通文件访问的区别03/31
- ♥ C++并发编程 _ 基于锁的数据结构08/19