[内容] Linux 进程的理解

正文:

内容一:Linux 进程的生命周期

1) 诞生或从其它进程中分叉出来
2) 准备好运行或出于可运行的状态
3) 在用户空间或内核空间运行
4) 处于可打断的睡眠状态 (Interruptable sleep) 状态或者不可打断的睡眠状态 (Uninterruptable sleep),此时进程处于睡眠状态 (可能存储在内存中也可能存储在硬盘里的 SWAP 中)
5) 结束或处于僵尸状态

内容二:进程的分类

2.1 按进程的附属关系进行分类

分为父进程和子进程 2 种进程,其中子进程附属于父进程

子进程的特点如下:
子进程有自己单独的进程 ID
子进程的父进程 ID 就是自己父进程的 ID
子进程的资源利用率和处理器计时器会被清空
子进程的待定信号会被清空
子进程不继承父进程的内存锁
子进程不继承父进程的信号
子进程不继承父进程被锁的记录
子进程不继承父进程的计时器
子进程不继承父进程未完成的异步 I/O 操作
子进程不继承父进程任何异步 I/O 文本

2.2 按进程的使用者进行分类

分为用户进程、守护进程、系统进程

用户进程守护进程系统进程
前台运行还是后台运行通常是前台运行通常是后台运行通常是后台运行
是否开机自启通常不开机启动通常是开机自启必须是开机自启
是否可以手动管理可以手动打开或者关闭可以手动打开或者关闭不能手动打开或者关闭
是否等待或者监控服务请求 (例如 Web 服务)通常不等待新的服务请求通常在等待新的服务请求或者定期执行某些新的任务通常在等待新的服务请求
进程权限权力有限,不能超出启动此进程的用户的权力范围权力有限,不能超出启动此进程的用户的权力范围权力较大,能完全访问内核的数据结构
进程运行空间通常在用户空间里运行,可以通过 user-level processes 申请进入内核空间访问内核的数据和结构通常在用户空间里运行,可以通过 user-level processes 申请进入内核空间访问内核的数据和结构只在内核空间里运行

2.3 按进程的状态进行分类

2.3.1 按进程的状态进行分类的种类列表

分为运行状态 (Running State)、可以运行状态 (Runnable State)、可中断睡眠状态 (Interruptable Sleep State)、不可中断睡眠状态 (Uninterruptable Sleep State)、僵尸状态 (Zombie State)、关闭状态 (Defunct State)

进程名进程大类状态旗帜ps 命令中的标志是否在运行
运行状态 (Running State)Running Statep->state = TASK_RUNNINGR正在处理器上运行的进程 (a running process)
可以运行状态 (Runnable State)Runnable Statep->state = TASK_RUNNINGR不在处理器上运行的进程 (a not-running process)
可中断睡眠状态 (Interruptable Sleep State)Sleeping Statep->state = TASK_INTERRUPTABLES不在处理器上运行的进程 (a not-running process)
不可中断睡眠状态 (Uninterruptable Sleep State)Sleeping Statep->state = TASK_UNINTERRUPTABLED不在处理器上运行的进程 (a not-running process)
僵尸状态 (Zombie State)Zombie StateP->state = TASK_ZOMBIEZ不在处理器上运行的进程 (a not-running process)
关闭状态 (Defunct State)Defunct StateN/AN/A不在处理器上运行的进程 (a not-running process)

(补充:这里不可中断睡眠状态 (Uninterruptable Sleep State) 的 “D” 状态,有点像是历史遗留,因为最早这种状态被视为 “Disk Wait” 所以这种状态以 D 开头)

2.3.2 按进程的状态进行分类的种类说明
2.3.2.1 正在运行状态 (Running State)

正在处理器上运行的进程。

2.3.2.2 可以运行状态 (Runnable State)

除了处理器资源无法使用 (例如处理器资源不够或者在使用处理器资源必须要要让 I/O 进行处理的情况),其它方面都已经准备好运行的进程。

2.3.2.3 睡眠状态 (Sleeping State)

除了处理器资源,其它资源也不够被此进程使用时,进程则会进入睡眠状态。进程可能是自愿进入睡眠状态 (Sleeping State) 也可能是被内核放到睡眠状态 (Sleeping State) 里的。

当进程的资源可用时,一个信号会发送给处理器。当下次调度时,有机会安排这个处于睡眠状态 (Sleeping State) 的进程进入运行状态 (Running State) 或者是可运行状态 (Runnable State)。

有些进程永远不会结束,其会在睡眠状态 (Sleeping State) 和运行状态 (Running State) 之间来回切换。

当一个进程需要资源时,如果可用内存的容量满足这个进程,但是处理器暂时忙不过来,这个进程就会处于 Ready-to-Run state (using main memory),而当处理器又有空余资源时,这个进程就会结束 Ready-to-Run state (using main memory) 从而进入运行状态 (Running State)。而当一个进程需要资源,但内存不够满足此进程用时,则这个进程将会有进入两种状态:可中断睡眠状态 (Interruptable Sleep State) 或者不可中断睡眠状态 (Uninterruptable Sleep State)。

2.3.2.3.1 可中断睡眠状态 (Interruptable Sleep State)

进程在等待某个特殊事件的发生或者等待某个时间窗口,当某个特殊事件或者某个时间窗口发生后则会离开可中断睡眠状态 (Interruptable Sleep State)。

2.3.2.3.2 不可中断睡眠状态 (Uninterruptable Sleep State)
2.3.2.3.2.1 不可中断睡眠状态 (Uninterruptable Sleep State) 的介绍

进程在等待资源可用或者等待超时。通常发生在硬件驱动等待 disk or network I/O 时。
处于这种状态时不会处理信号,如果资源变为可用或者等待时间结束则会直接结束此状态。当处于此状态的进程回归时,会看到处于这段时间时积累的信号。

不可中断睡眠状态 (Uninterruptable Sleep State) 也是属于正常的状态,通常发生的很快因此很难被观察到。但是,如果处于不可中断睡眠状态 (Uninterruptable Sleep State) 的状态超过了合理的等待时间,那此进程会被卡住,排在此进程后面的进程也会跟着被卡住 (例如需要进入同样设备的进程或者依赖此进程的进程),这种情况下通常需要重启系统解决。

2.3.2.3.2.2 查看处于不可中断睡眠状态 (Uninterruptable Sleep State) 的进程的方法
2.3.2.3.2.2.1 方法一:单条查看
2.3.2.3.2.2.1.1 通过 ps 命令查看处于 D 状态进程的 PID 号
# ps auxH | awk '$8 ~ /^D/{print}
2.3.2.3.2.2.1.2 在系统中查看此 PID 号的堆栈信息
# sudo cat /proc/<PID>/stack
2.3.2.3.2.2.2 方法二:批量查看
# for D_PID in $(ps auxH | awk '$8 ~ /^D/{print $2}');do ps -Llp $D_PID;sudo cat /proc/$D_PID/stack;echo;done
2.3.2.4 僵尸状态 (Zombie State)

当子进程被系统叫停时,它会释放数据结构,但是它在进程表中的位置依旧会被保留。之后它会释放信号给它的父进程,由它的父进程决定此子进程是否成功退出并是否取消它在进程表中的位置。

当子进程停止,但是父进程还没有释放此进程时,此子进程将进入僵尸状态 (Zombie State)。如果父进程在释放子进程之前就死掉了,那此子进程将永远处于僵尸状态 (Zombie State)。

补充:

1) top 命令中 us 代表 user mode 用户模式,sy 代表 system mode 内核空间
2) Linux 的平均负载值为平均的 runnable or running processes (R state), and the number of processes in uninterruptable sleep (D state) over the specified interval 之和
3) 1 个处理器上同 1 时间只能运行 1 个进程

参考文献:

https://access.redhat.com/sites/default/files/attachments/processstates_20120831.pdf

[步骤] auditd 日志传送到远端的日志搜集服务器的设置 (auditd 版) (RHEL 版)

正文:

步骤一:在服务端上配置搜集 auditd 日志的设置

1.1 设置搜集 auditd 日志时使用的端口

(只在服务端上执行以下步骤)

# vi /etc/audit/auditd.conf

将部分内容修改如下:

......
tcp_listen_port = 1000 
......

(补充:这里以设置 TCP 端口号 1000 作为搜集 auditd 日志时使用的端口为例)

1.2 在 SELinux 上给 auditd 搜集日志时使用的端口打上 auditd SELinux 标签

(只在服务端上执行以下步骤)

# semanage port -a -t audit_port_t -p tcp 1000

(补充:这里以给 TCP 端口号 1000 打上 auditd 的 SELinux 标签为例)

1.3 让刚刚设置的配置文件生效

(只在服务端上执行以下步骤)

# service auditd restart

步骤二:在客户端将 auditd 日志传送到远端的日志搜集服务器

2.1 安装 audispd-plugins 组件 (只在 RHEL 8 和 RHEL 8 之后版本的 RHEL 上操作)

(只在客户端上执行以下步骤)

# dnf install audispd-plugins

(注意:只在 RHEL 8 和 RHEL 8 之后版本的 RHEL 上进行此操作)

2.2 将 auditd 日志传送到远端的日志搜集服务器

2.2.1 设置 auditd 日志要发送到的目标 IP 地址和目标端口

(只在客户端上执行以下步骤)

如果是 RHEL 7 和 RHEL 7 之前的版本 RHEL:

# vim /etc/audisp/plugins.d/syslog.conf

将部分内容修改如下:

......
remote_server = 192.168.0.1
......
port = 1000
......

(补充:这里以将 audit 日志发送到 IP 地址是 192.168.0.1 端口是 1000 的远端日志搜集服务器的为例)


如果是 RHEL 8 和 RHEL 8 之后版本的 RHEL:

# vim /etc/audit/plugins.d/syslog.conf

将部分内容修改如下:

......
remote_server = 192.168.0.1
......
port = 1000
......

(补充:这里以将 audit 日志发送到 IP 地址是 192.168.0.1 端口是 1000 的远端日志搜集服务器的为例)

2.2.2 设置 auditd 日志发送到远端日志搜集服务器的其它信息

(只在客户端上执行以下步骤)

如果是 RHEL 7 和 RHEL 7 之前的版本 RHEL:

# vim /etc/audisp/plugins.d/syslog.conf

将部分内容修改如下:

......
active = yes
direction = out
path = /sbin/audisp-remote
type = always
#args =
format = string
......

如果是 RHEL 8 和 RHEL 8 之后版本的 RHEL:

# vim /etc/audit/plugins.d/syslog.conf

将部分内容修改如下:

......
active = yes
direction = out
path = /sbin/audisp-remote
type = always
#args =
format = string
......

2.3 让刚刚修改的 auditd 配置文件生效

(只在客户端上执行以下步骤)

# service auditd restart

步骤三:检测 auditd 日志是否传送到了远端的日志搜集服务器

3.1 在本地生成 auditd 日志用于检测

(只在客户端上执行以下步骤)

# auditctl -m "This is a test auditd message from client"

(补充:这里以在 auditd 日志里写入 1 条内容是 “This is a test auditd message from client” 的日志为例)

3.2 在远端的日志搜集服务器检测是否收到 auditd 日志

(只在服务端上执行以下步骤)

(步骤略)

参考文献:

https://access.redhat.com/solutions/28676

[步骤] auditd 日志传送到远端的日志搜集服务器的设置 (rsyslog 版) (RHEL 版)

正文:

步骤一:将 auditd 日志转换为系统日志

1.1 安装 audispd-plugins 组件 (只在 RHEL 8 和 RHEL 8 之后版本的 RHEL 上操作)

# dnf install audispd-plugins

(注意:只在 RHEL 8 和 RHEL 8 之后版本的 RHEL 上进行此操作)

1.2 将 auditd 日志转换为系统日志

如果是 RHEL 7 和 RHEL 7 之前的版本 RHEL:

# vim /etc/audisp/plugins.d/syslog.conf

将部分内容修改如下:

......
active = yes
......
args = LOG_INFO LOG_LOCAL3
......

(补充:这里以将 auditd 日志转换成系统的 local3 日志为例)


如果是 RHEL 8 和 RHEL 8 之后版本的 RHEL:

# vim /etc/audit/plugins.d/syslog.conf

将部分内容修改如下:

......
active = yes
......
args = LOG_INFO LOG_LOCAL3
......

(补充:这里以将 auditd 日志转换成系统的 local3 日志为例)

步骤二:将系统日志中的 auditd 日志传送到远端的日志搜集服务器

# vi /etc/rsyslog.conf

添加以下内容:

......
local3.info        @remotesyslog.com
local3.info        stop

(补充:这里以将 local3 日志传送到远端的日志搜集服务器 remotesyslog.com 为例)

步骤三:让刚刚修改的 auditd 配置文件生效

如果是 RHEL 6 和 RHEL 6 之前的版本 RHEL:

# service rsyslog restart
# service auditd restart

如果是 RHEL 7 和 RHEL 7 之后版本的 RHEL:

# systemctl restart rsyslog
# service auditd restart

步骤四:检测 auditd 日志是否传送到了远端的日志搜集服务器

4.1 在本地生成 auditd 日志用于检测

# auditctl -m "This is a test auditd message from client"

(补充:这里以在 auditd 日志里写入 1 条内容是 “This is a test auditd message from client” 的日志为例)

4.2 查看本地的系统日志

# tail -f /var/log/messages
...... auditd[......]: ......

(补充:此时可以看到 /var/log/messages 系统日志里有和 auditd 相关的日志)

4.3 在远端的日志搜集服务器检测是否收到 auditd 日志

(步骤略)

参考文献:

https://access.redhat.com/solutions/28676

[内容] Linux console 口显示日志的设置

正文:

内容一:日志消息的优先级

级别关键字描述 内容
0 EMERG 致命级 (KERN_EMESG) 紧急,系统本身已经无法再运行必须马上拯救
1 ALERT 警戒级 (KERN_ALERT) 警报,系统出现了重大错误必须马上处理的情况
2 CRIT 临界级 (KERN_CRIT) 严重,系统出现了严重的情况
3ERR 错误级 (KERN_ERR) 错误,系统出现了错误的情况
4 WARNING告警级 (KERN_WARN) 警告,系统出现了需要警告的情况
5 NOTICE 注意级 (KERN_NOTICE) 注意,系统出现了需要注意的情况
6 INFO 通知级 (KERN_INFO) 信息,系统出现了一些情况
7 DEBUG 调试级 (KERN_DEGUG) 调试,系统出现了程序或服务调试的情况

内容二:查看 console 口显示日志的设置

# cat /proc/sys/kernel/printk
4       4       1       7


补充:
1) 第 1 个 4 代表只有比等级为 4 的日志更紧急的日志会显示在 console 口
2) 第 2 个 4 代表当某条日志没有等级时默认会将其日志等级设置为 4
3) 第 3 个 1 代表会显示在 console 口上的日志的最高紧急度 (在这个文件中 1 已经是所有等级中紧急度最高的等级了)
4) 第 4 个 7 代表当系统启动时,启动时所产生的日志默认的紧急等级

内容三:修改 console 口显示日志的等级

3.1 修改 console 口显示日志的等级

# sysctl -w kernel.printk=3

或者:

# echo "3" > /proc/sys/kernel/printk

(补充:这里以设置把等级为 3 或者更紧急的日志显示在 console 口为例)

3.2 查看修改 console 口显示日志的等级

# cat /proc/sys/kernel/printk
3       4       1       7


补充:
1) 第 1 个 3 代表只有比等级为 4 的日志更紧急的日志会显示在 console 口
2) 第 2 个 4 代表当某条日志没有等级时默认会将其日志等级设置为 4
3) 第 3 个 1 代表会显示在 console 口上的日志的最高紧急度 (在这个文件中 1 已经是所有等级中紧急度最高的等级了)
4) 第 4 个 7 代表当系统启动时,启动时所产生的日志默认的紧急等级

参考文献:

https://linuxconfig.org/introduction-to-the-linux-kernel-log-levels