一、前言
1.1 程序和进程
程序是为了完成某种任务而设计的软件,比如OpenOffice是程序。什么是进程呢?进程就是运行中的程序。
一个运行着的程序,可能有多个进程。 比如 LinuxSir.Org 所用的 www 服务器是 apache 服务器,当管理员启动服务后,可能会有好多人来访问,也就是说许多用户来同时请求 httpd 服务,apache 服务器将会创建有多个 httpd 进程来对其进行服务。
1.2 进程的类型
在 Linux 中主要有两种类型的进程:
- 前台进程(也称为交互式进程):这些进程由终端会话初始化和控制。换句话说,需要有一个连接到系统中的用户来启动这样的进程,它们不是作为系统功能/服务的一部分自动启动。
- 后台进程(也称为非交互式/自动进程):这些进程没有连接到终端,它们不需要任何用户输入。
1.3 SSH
Secure Shell 的缩写,它是一个协议,既可以远程登录,也可以用这个协议传送文件。SSH 对比 Telnet 的一个重要优势就是所有传输都是经过加密的,使用了MD5、RSA、DES等密码算法,保证数据的机密性、完整性、不可重放攻击。整个过程中包括密钥协商和交换,以及数据加密传送和解密等等。
OpenSSH 是 SSH 的开源实现,因此用户可以免费使用到这种安全服务。一句话概括OpenSSH:SSH 使用加密的远程登录实现,可以有效保护登录及数据的安全。
OpenSSH 包含两个程序:
- SSHD:服务进程,以守护进程的方式运行
- SSH:客户端,用以连接SSH服务
二、ps 命令
ps 命令用于显示当前进程(process)的状态。
2.1 基本语法
ps [options] [--help]
2.2 选项
- a:显示所有进程
- -a:显示同一终端下的所有程序
- -A:显示所有进程
- c:显示进程的真实名称
- -N:反向选择
- -e:等于“-A”
- e:显示环境变量
- f:显示程序间的关系
- -H:显示树状结构
- r:显示当前终端的进程
- T:显示当前终端的所有程序
- u:指定用户的所有进程
- -au:显示较详细的资讯
- -aux:显示所有包含其他使用者的行程
- -C <命令>:列出指定命令的状况
- --lines <行数>:每页显示的行数
- --width <字符数>:每页显示的字符数
- --pid <进程id> 或 -p <进程id>:列出指定进程 id 的进程状况
- --help:显示帮助信息
- --version:显示版本显示
2.3 常用组合
1)BSD 风格,前面没 -
ps aux # 显示正在内存中的进程
输出字段:
- USER:进程的属主
- PID:进程的 ID
- %CPU:进程占用的 CPU 百分比
- %MEM:占用内存的百分比
- NI:进程的 NICE 值。数值大,表示较少占用 CPU 时间
- VSZ:进程虚拟大小
- RSS:驻留中页的数量
- TTY:终端ID
- STAT:进程状态。D:Uninterruptible sleep (usually IO);R:running;S:处于休眠状态;T:停止或被追踪;W:进入内存交换(从内核2.6开始无效);X:死掉的进程;Z:僵尸进程;<:优先级高的进程;N:优先级较低的进程;L:有些页被锁进内存;s:会话创建者,这个启动后,会引起很多其他进程,如果它被终止,与它相关的进程也会终止;l:多线程进程;+:前台进程
- WCHAN:正在等待的进程资源
- START:启动进程的时间
- TIME:进程消耗 CPU 的时间
- COMMAND:命令的名称和参数
2)标准风格
ps -ef # 显示所有进程,并显示完整格式程序信息
输出字段:
- UID:进程用户ID
- PID:进程的 ID
- PPID:父进程
- C:CPU 调度情况,即是是由 CPU 用于计算执行优先级的因子。
- STIME:进程启动的时间
- TTY:终端ID
- TIME:进程消耗 CPU 的时间
- CMD:启动进程的命令
三、进程操作
3.1 pidof 命令
pidof 命令用于查找指定名称的进程的进程号 id 号。
1)基本语法
pidof [选项] 进程名
2)选项
- -s:仅返回一个进程号
- -c:仅显示具有相同 root 目录的进程
- -x:显示由脚本开启的进程
- -o:指定不显示的进程 id
3)实例
pidof systemd
pidof top
pidof httpd
执行结果:
3.2 pgrep 命令
pgrep 命令以名称为依据从运行进程队列中查找进程,并显示查找到的进程 id。每一个进程 id 以一个十进制数表示,通过一个分割字符串和下一个 id 分开,默认的分割字符串是一个新行。对于每个属性选项,用户可以在命令行上指定一个以逗号分割的可能值的集合。
1)基本语法
pgrep [选项] 进程名
2)选项
- -o:仅显示找到的最小(起始)进程号
- -n:仅显示找到的最大(结束)进程号
- -l:显示进程名称
- -P:指定父进程号
- -g:指定进程组
- -t:指定开启进程的终端
- -u:指定进程的有效用户 id
3)实例
pgrep -ln txl_me
执行结果:
3.3 kill 命令
kill 命令用来删除执行中的程序或工作。kill 可将指定的信息送至程序。预设的信息为 SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL(9) 信息尝试强制删除程序。程序或工作的编号可利用 ps 指令或 job 指令查看。
1)基本语法
pkill [选项] 进程id
2)选项
- -a:当处理当前进程时,不限制命令名和进程号的对应关系
- -l <信息编号>:若不加 <信息编号> 选项,则 -l 参数会列出全部的信息名称
- -p:指定 kill 命令只打印相关进程的进程号,而不发送任何信号
- -s <信息名称或编号>:指定要送出的信息
- -u:指定用户
3.4 pkill 命令
pkill 命令可以按照进程名杀死进程。pkill 和 killall 应用方法差不多,也是直接杀死运行中的程序;如果您想杀掉单个进程,请用 kill 来杀掉。
1)基本语法
pkill [选项] 进程名称(同时也支持类似grep指令中的匹配模式)
2)选项
- -o:仅向找到的最小(起始)进程号发送信号
- -n:仅向找到的最大(结束)进程号发送信号
- -P:指定父进程号发送信号
- -g:指定进程组
- -t:指定开启进程的终端
3.5 特殊
要查找当前 shell 的进程 ID 以及它父进程的进程 ID,可以运行:
echo $$
echo $PPID
执行结果:
四、Linux 如何识别进程
由于 Linux 是一个多用户系统,意味着不同的用户可以在系统上运行各种各样的程序,内核必须唯一标识程序运行的每个实例。
程序由它的进程 ID(PID)和它父进程的进程 ID(PPID)识别,因此进程可以被分类为:
- 父进程:这些是在运行时创建其它进程的进程
- 子进程:这些是在运行时由其它进程创建的进程
每个新进程产生,需要有个父进程来带领子进程运行(这个子进程还可能再运行子进程),这一串进程可以称为进程组。
4.1 子进程和父进程
他们的关系是管理和被管理的关系,当父进程终止时,子进程也随之而终止。但子进程终止,父进程并不一定终止。比如httpd服务器运行时,我们可以杀掉其子进程,父进程并不会因为子进程的终止而终止。
在进程管理中,当我们发现占用资源过多,或无法控制的进程时,应该杀死它,以保护系统的稳定安全运行;
4.2 init 进程
/sbin/init 进程是系统中所有进程的父进程,它是启动 Linux 系统后第一个运行的程序;它管理着系统上的所有其它进程。它由内核自身启动,因此理论上说它没有父进程。
init 进程的进程 ID 总是为 1。它是所有孤儿进程的收养父母。(它会收养所有孤儿进程)。
4.3 C 模拟多进程
int fork() :创建一个与原进程完全相同的进程,原来的进程成为新进程的父进程。依赖 unistd.h 头文件。
一旦执行 fork(),本尊进程变成了父进程,新的那个是子进程。
返回值:
- >0:代表是父进程
- =0:代表是子进程
- <0:出错
» txl_me.c
#include <stdio.h> #include <unistd.h> int main() { int i; int pid; for(i=0; i<=4; i++) { pid = fork(); if(pid<=0) break; // 保证让每个子进程都是同一个父创建的 } printf("start "); sleep(20); printf("end "); }
运行结果:
五、守护进程
Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。
守护进程一般在系统启动时开始运行,并作为服务一直运行。除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。
一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。
守护进程的名称通常以 d 结尾,比如 sshd、xinetd、crond 等。
守护进程都有下面几个特点:
- 没有控制终端,终端名设置为 ? 号:也就意味着没有 stdin 0 、stdout 1、stderr 2
- 父进程不是用户创建的进程,init 进程或者 systemd(pid=1)以及用户人为启动的用户层进程一般以 pid=1 的进程为父进程,而以 kthreadd 内核进程创建的守护进程以 kthreadd 为父进程
- 守护进程一般是会话首进程、组长进程
- 工作目录为 (根),主要是为了防止占用磁盘导致无法卸载磁盘
5.1 C 模拟僵尸守护进程
原理:将程序的父进程 ppid 指向 init 进程。
int setsid() :使进程成为会话组长。调用成功后,返回新的会话的ID,调用 setsid 函数的进程成为新的会话的领头进程,并与其父进程的会话组和进程组脱离。
由于会话对控制终端的独占性,进程同时与控制终端脱离。
» txl_me.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { int pid; pid = fork(); if(pid>0) exit(0); // 结束父进程,子进程继续 setsid(); while(1) { } }
运行结果:
查看进程:
六、使用 chkconfig 工具创建服务
chkconfig 命令主要用来查询、更新(启动或停止)系统服务的运行级信息。谨记 chkconfig 不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接。
6.1 基本语法
chkconfig [选项]
6.2 选项
- --list [name]:显示所有运行级系统服务的运行状态信息(on 或 off),如果指定了 name,那么只显示指定的服务在不同运行级的状态
- --add <name>:增加所指定的系统服务,让 chkconfig 得以管理它,并同时在系统启动的叙述文件内增加相关数据,chkconfig 确保每个运行级有一项启动(S)或者杀死(K)入口,如有缺少,则会从缺省的 init 脚本自动建立
- --del <name>:删除所指定的系统服务,不再由 chkconfig 指令管理,并把相关符号连接从 /etc/rc[0-6].d 删除。
- --level <等级代号><name>:指定读系统服务要在哪一个执行等级中开启或关毕
- 等级0表示:关机
- 等级1表示:单用户模式
- 等级2表示:无网络连接的多用户命令行模式
- 等级3表示:有网络连接的多用户命令行模式
- 等级4表示:保留
- 等级5表示:带图形界面的多用户模式
- 等级6表示:重新启动
6.3 创建服务脚本
1)进入 /etc/init.d,创建 txld 脚本
#add for chkconfig #chkconfig:35 80 10 #description:i am txl #processname:txld PID_FILE='/etc/txld.pid' start() { if [ -f $PID_FILE ] then echo 'txl is already running!' else echo $$>$PID_FILE /home/txl/bin/txl_me echo 'txl is started!' fi } stop() { pkill txl_me rm $PID_FILE echo 'txl is stoped!' } case $1 in start) start ;; stop) stop ;; *) echo 'unknow' ;; esac
第二行 #chkconfig:表示告诉 chkconfig 缺省启动的运行级、启动和停止的优先级。如果某服务缺省不在任何运行级启动,那么使用 - 代替运行级
第三行 #description:表示对服务进行描述,可以用 跨行注释
第四行 #processname:表示服务名称
2)赋予权限
chmod +x txld
3)添加并启动服务
chkconfig --add txld
然后就可以通过 service 启动和停止该服务了:
service txld start
service txld stop