内核有进程管理、文件系统、网络功能、内存管理、驱动程序、安全功能等功能。模式切换(context switch)指的是在用户模式和内核模式之间切换。
推荐阅读:《linux内核设计与实现》 《深入理解linux内核》
进程(Process)是计算机程序运行的实例,每一个进程都是由父进程派生的,每个进程都有一个PID来标识,由内核进行分配,进程号的范围是2-32768,进程号可以重用,当一个进程终止后,其进程 ID 就可以再次使用了。大多数 UNIX 系统实现延迟重用算法,使得赋予新建进程的 ID 不同于最近终止进程所使用的 ID。当进程号用尽时,会从2开始新一轮循环。
linux系统启动时,第一个运行的进程是init,进程号为1,系统启动后产生的所有进程都由 init 进程衍生而来,是所有进程的父进程。当子进程终止时,它与父进程会保持关联,直到父进程正常结束,如果父进程异常终止,子进程会将init作为父进程,init发现僵尸进程时会将其释放。
每个进程除了有 PID 还会有 PPID,也就是父进程 ID,通过 PPID 可以找到父进程的信息。系统启动后所有的进程都由 init 进程衍生而来。因为所有进程都来自于一个进程,所以 Linux 的进程模型也叫做进程树。使用 pstree
命令可以查看系统当前的进程树
在同一个时刻,一个 CPU 核心上只能运行一个进程,CPU 在某一个时刻运行哪个进程需要依靠操作系统内核来进行调度。操作系统为每个进程分配一个优先级,系统内核根据优先级来调度进程运行。
Linux 中共有 0~139 种优先级,其中 1-99 被称为实时优先级,数字越大优先级越高。100-139 被称为动态优先级,数字越小,优先级越高,内核可以调整进程的动态优先级,还可以使用 nice
或 renice
指令调整进程的动态优先级,nice的值为-20-19
Linux 系统使用了抢占式的进程调度。这意味着,当一个进程进入 TASK_RUNNING 状态(即准备运行状态)时,内核检查次进程的优先级,并与当前正在运行的进程的优先级进行比较,如果次进程的优先级更大,当前运行的进程被中断,又调度器重新挑选一个进程运行。
Linux 系统对每种优先级都维护一个运行队列和过期队列,系统每次从优先级最高的运行队列中挑选进行运行,然后放入其过期队列中。当运行队列中的进程全部进入过期队列后,再将过期队列和运行队列对调。
在一个多任务操作系统当中,可能存在着上千个进程,而物理内存只有一个,为了防止进程访问原本不属于本进程的内存空间,现代操作系统都会使用「内存保护」技术。
每一个进程都运行在它自己的内存沙箱(sandbox)中,这个沙箱被称作「虚拟地址空间」(virtual address space),在 32 位的系统中,它是一个 4GB 大小的内存地址空间,虚拟内存是线性可编址的,其使用单位是页(page),对应的物理内存被称为页框(page frame)。这些虚拟的地址通过页表(page table)映射至真实的物理内存,页表由操作系统内核和处理器(内存管理单元)负责管理。每个进程都有它自己的页表。这里需要注意,所有的进程都运行在「虚拟内存」中,即使是内核本身也一样。因此虚拟地址空间中的一部分是专门供内核使用的。
系统中可能存在大量进程,而 CPU 的数量是有限的,因此进程并不一定处于运行状态。在 Linux 系统中,进程有下面这些状态:
Executing/Running: 进程正在 CPU 上运行。
Ready: 进程处于准备运行状态,它被放置在一个运行队列中,等待系统分配 CPU 资源给它。
Stopped: 进程被停止,通常是通过接收一个信号,正在被调试的进程可能处于停止状态。暂停于内存中,但不会被调度,除非手动启动
Uninterruptible: 不可中断睡眠,处于这个状态的进程通常需要等待某个资源,而且在等待过程中进程会忽略任何信号。被磁盘设备 I/O 所阻塞的进程可能处于这个状态。
Interrruptible: 可中断睡眠状态,进程需要等待某个特定的条件为真,才会继续运行,可中断睡眠状态的进程可以被信号唤醒。
Zombie: 子进程已经结束,而父进程没有调用 wait() 或者 waitpid() 系统调用获取子进程的终止状态,导致进程的进程描述符没有被回收。
为了管理进程,内核需要追踪每个进程的运行状态,例如进程的优先级,PID,进程的地址空间等信息。内核使用一个 task_struct 类型的结构体来保存进程信息,它被称为进程描述符,对于每个进程,内核都为其创建一个进程描述符,内核使用双向链表的结构来存储这些进程描述符。多个任务的task struct组成的链表叫做task list。
进程不是凭空创建的,每个进程都是由其父进程衍生而来,在 Linux 系统中,父进程通常使用 fork()
, vfork()
或 clone()
等系统调用来生成子进程。
fork 创建的进程成被称为「子进程」(child process)。例如,在 shell 中执行一个命令时,shell 进程就会调用 fork() 产生一个子进程,然后子进程调用 exec() 执行命令程序,进程结束后返回控制至父进程 shell 进程。
写时复制
在 Linux 系统中,进程使用 fork() 产生的子进程时,并没有立即为子进程分配物理页框。Linux 系统使用了写时复制(Copy On Write, COW)技术。这意味着子进程被创建时,与其父进程共享相同的物理页框(page frame),子进程实际使用的是其父进程的堆栈空间,内核将这些共享区域标记为只读。当父、子进程中的任一个试图修改这些区域时,内核会为修改区域的那块内存制作一个副本,并标记为可写,对于原来的共享内存页框,内核会检查是否此页框只被一个进程所使用,如果只被一个进程使用,那么此页框也为可写。这样做的原因是子进程的生命周期可能很短,使用「写时复制」技术可以按需为进程分配内存,使得内存的分配更加高效。
僵尸进程(Zombie)
当一个进程完成它的工作终止之后,它的父进程需要调用 wait() 或者waitpid() 系统调用取得子进程的终止状态。
一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中而未被释放。 这种进程称之为僵死进程。
孤儿进程(Orphan Process)
如果父进程产生子进程后终止了,且子进程继续运行,子进程被称为孤儿进程,孤儿进程由 init 进程收养,它的 PPID 变为 1。
守护进程(daemon):是一种后台服务进程,它们通常不与终端关联,用户空间守护进程的父进程是 init 进程。Linux 中的很多服务都以守护进程模式运行,它们不会随着终端的退出和登录而改变进程状态。
用户进程/前台进程:和终端相关,通过终端启动的进程,也可以在前台启动的进程,以守护模式运行
根据cpu和io来区分进程类型又可分为 cpu-bound(cpu密集型)和io-bound(io密集型)
pstree可以查看系统当前的进程树,pstree命令在psmisc软件包中,包中还有fuser、killall、和pstree.x11命令。pstree.x11命令和pstree功能一样,只不过pstree.x11退出需确认。
ps(process status)进程状态快照查看,单独使用ps
会显示当前用户在当前终端所运行的进程。
回顾:终端
Linux中默认提供了六个命令行终端和一个图形终端:tty1
tty7。其中tty1tty6是命令行终端(就是全屏,黑底白字的控制台),tty7是图形终端(也就是平时我们启动桌面版时默认登录的图形用户界面,也是全屏的)。可以通过Ctrl+Alt+F1~F7切换这7个终端。除了这7个基本的“大环境”终端,我们还可以在tty7中开很多不是全屏的终端,也就是我们平时用来输入命令行的图形终端(ctrl + shift + T快捷键),这些终端的名字是以pts开头的。pts是pseudo terminal slave的缩写,表示“伪终端从属”。如果我新开一个图形终端,那么显示名称为pts/0,如果再开一个图形终端,那么它的名字就是pts/1,依次类推。
在/proc
目录下有大量数字目录,每一个数字都代表一个进程号,每个数字目录下的文件都是进程的状态信息,在数字目录下有一个文件是cmdline
可以查看此进程是由哪个命令启动的。
ps支持两种风格的选项格式,一种是Unix风格,另外一种是BSD风格
ps的常用选项
eu/axu:以用户为中心显示所有进程。中括号表示的是内核线程
a:与终端相关的进程
x:与终端无关的进程
e:显示所有进程
u:以用户为中心组织进程状态信息显示
-ef:以完整格式显示所有进程
-f:显示完整格式的程序信息
-eFH:以层级格式显示进程相关信息,类似的还有-ejH
-F:显示更完整格式的进程信息
-H:以层级格式显示
-j:任务格式,实际也是显示不同的字段
-eo\axo [指定要显示的字段]:显示自定义字段的进程列表,eg.ps -eo pid,psr,command
-o:自定义先是字段
常用的字段有:ni(nice值);pri(优先级);psr(processor CPU);rtptrio(实时优先级);ppid(父进程)等
-u USERNAME:列出此用户运行的进程。此处比运行ps多了许多进程,因为这些进程不运行在此前的终端中
ps axu各字段的含义
USER | PID | VSZ/VIRT | RSS/RES | STAT | START | TIME |
---|---|---|---|---|---|---|
进程属主 | 进程号 | virtual memory size虚拟内存集 | resident size常驻内存集 | 进程状态 | 启动时间 | 进程运行占用cpu的累计时间 |
STAT状态的含义
R:running,正在运行或在队列中的进程 S:interruptable sleeping,睡眠,通常是等待信号 D:uninterruptable sleeping,不可中断的睡眠,例如等待IO T:stopped Z:zombie
+:前台进程 l:多线程进程
N:低优先级进程 <:高优先级进程
s:session leader包含子进程
pgrep、pkill的一般使用格式pgrep|pkill [options] pattern
。例如pgrep -u root -l
-u uid:effective user
-U uid:real user
-t terminal:与指定终端相关的进程
-l:显示进程名,只对pgrep有效
-a:显示完整格式的进程名,只对pgrep有效
-P pid:显示其父进程为此处指定的进程的进程列表
pidof可以根据进程名获取其pid。一般用法pidof 进程名
ps命令是静态查看当前的系统的统计信息,无法动态刷新,top命令可以实时显示进程状态,默认每3秒刷新一次。top不会显示全部的进程,只显示排在前的一部分。
top命令默认以已占据的cpu百分比排序,也可以在top界面按M键以占据内存百分比排序,T键按累计占用排序,P键按占据的cpu百分比排序。
-
首部uptime信息
首部top信息显示了关于计算机的运行时间和平均负载等信息,这些信息也可以使用uptime来查看。uptime信息的显示和隐藏使用l键,不过只在CentOS 7中有效。
load average是平均负载,表示一段时间内的平均活跃进程数,三个数字分别表示一分钟、五分钟、十五分钟负载。0.05就表示15分钟内平均有0.05个进程使用CPU,CPU有5%的时间是活跃的。
单核cpu超过1即为过载,双核cpu超过2即为负载,依此类推。
tload命令可以显示随时间变化的负载曲线。
-
任务和task信息
此行显示的是进程和cpu的统计信息,使用t可以显示和隐藏,cpu单独统计使用数字1键。
-
memory信息
此行提示的内存和交换分区的统计,显示或隐藏使用m键。这一行信息也可以一用free -m来显示
退出top使用q键,修改刷新时间间隔使用s键然后指定刷新时间,终止指定进程用k键,以用户过滤显示使用u键。
常用选项
-d number:指定默认刷新频率
-b:以批次方式,-n NUM:显示多少批次
htop和top相似,但比top命令功能更多一些,界面也更直观一些,安装要使用Fedora-epel源。
在htop命令中常用的内建命令:
s:跟踪选定进程的系统调用,如果提示没有安装strace,则要安装strace,yum -y install strace
l:显示选定进程打开的文件列表
a:将选定的进程绑定至某指定cpu核心
t:显示进程树
-d NUM:指定刷新时间
-u UNAME:仅显示指定用户的进程
glances是一个跨平台的系统监控工具,基于python,支持cs架构,glances也有很多内建命令,使用h获取帮助。glances也需要epel源。
在glances中绿色表示OK,蓝色表示Careful,紫色表示Warning,红色表示Critical。
-b:以字节byte为单位显示网卡速率
-d:关闭磁盘io模块
-f /path/to/somewhere:输出文件位置及其格式
-o {HTML|CSV}:输出格式
-m:禁用mount模块
-n:禁用网络模块
-t num:延迟时间间隔
-l:每个cpu的相关数据单独显式
c/s模式的glances使用
服务模式:glances -s -B IPADDR(指明监听于本机的哪个地址)直接使用glances -s会使用默认的绑定地址0.0.0.0,这样会监听所有网络的指定接口。-B用于绑定地址,-p用于绑定监听端口
客户端模式:glances -c Server_ipaddr(要连入的服务器地址),-p用于指定服务器端口
需要注意的是glances的密码是明文传输的,最好不要使用默认端口,并且是在内部使用。
vmstate查看虚拟内存状态,是比较早期的linux命令,一般使用格式vmstat [options][delay [count]] 。
vmstat各字段含义
procs
r:等待运行的进程的个数
b:处于阻塞状态(不可中断睡眠态)的进程个数
memory
swpd:交换内存的使用总量
free:空闲物理内存总量
buffer:用于buffer的内存总量
cache:用于cache的内存总量
swap
si:数据进入swap中的数据速率,单位是kb/s
so:数据离开swap的数据速率
io
bi:从块设备读入数据到系统的速率,单位是kb/s
bo:保存数据到块设备的速率
system
in:interrupts,中断速率
cs:context switch,进程切换速率
cpu
us: 用户空间占据cpu的比例
sy:内核空间占据cpu的比例
id:idle空闲比例
wa:等待io完成所消耗的时间比例
st:被偷走的时间比例
-s:显示统计数据
dstat是vmstat的替换工具, 统计系统资源并显示
-c:显示cpu的相关信息,dstat默认会显示cpu的相关信息
-C:单独指定某颗cpu的信息
-d:显示磁盘的读写速率,默认显示
-D:单独显示指定的某块硬盘
-g:显示page的相关信息
-i:显示中断的统计信息
-l:显示load的统计信息
-m:显示memory相关统计信息
-n:显示network相关统计信息
-p:显示进程统计数据
-r:显示io请求相关的统计数据
-s:显示swapped相关统计数据
--top-cpu:显示最占用cpu的进程
--top-io:显示最占用io的进程
--top-mem:显示最占用内存的进程
--top-lantency:显示延迟最大的进程
--ipc:进程间通信的相关统计数据
pmap报告进程的内存映射关系,一般使用格式pmap [options] pid。pmap实际上也是读取/proc/PID/maps文件
-x:显示详细格式的信息
kill通过向进程发送控制信号来对进程进行管理。显示当前系统的可用信号可以使用kill -l也可以使用man 7 signal。
例如kill PID1 PID2 PID3 ...,使用kill -9 PID强制结束进程
常用的信号:
-
SIGHUP:无需关闭进程而让其重读其配置文件
-
SIGINT:中止正在运行的进程;相当于ctrl+c
-
SIGKILL:杀死正在运行的进程
-
SIGTERM:终止正在运行的进程
-
SIGCONT
-
SIGSTOP
指定信号的方法:
信号的数字标识:1,2,9
信号的完整名称:SIGHUP
信号的简写名称:HUP
**向进程发信号:**kill [ -SIGNAL] PID
终止"名称"之下的所有进程:killall [ -SIGNAL ] Program
一个作业可能会包含一个或多个进程,主要是完成一个完整任务。
前台作业:通过终端启动,运行时一直占用终端
后台作业:可以通过终端启动,但启动后即转入后台运行(释放终端)
如何让作业运行于后台?
(1)运行中的作业:ctrl+z
(2)尚未启动的作业:COMMAND &
此类作业虽然被送到后台运行,但其依然与终端相关。如果希望送往后台后,剥离与终端的关系,要使用nohup COMMAND &
查看所有作业:jobs,作业号后的加号代表系统会默认操作此作业,减号的次之
作业控制:
fg [[%] JOB_NUM]:把指定的后台作业调回前台
bg [[%] JOB_NUM]:让送往后台的作业在后台继续运行
kill [%JOB_NUM]:终止指定作业
调整静态优先级100-139使用nice和renice。进程默认启动时的nice值为0,优先级为120
nice命令的一般使用格式:nice [option] [COMMAND [ARG]...]
renice可以对已经运行的进程进行调整nice值,一般使用格式:renice [-n] priority [-gpu] pid...,普通用户无法降低nice值
查看调整的效果:ps axo pid,comm,ni
在未来的时间点执行一次任务:at、batch
周期性的运行某任务:cron
at命令
at命令的一般使用格式at [option] TIME,作业的执行结果以邮件通知相关用户
时间指定:
HH:MM[YYYY-mm-dd];noon|midnight|teatime;tomorrow;now+num{minutes,hours,days,weeks}
ctrl+d提交任务
at -l 列出等待运行的作业,也可以使用atq
at -d 作业号:删除作业
-q QUEUE:
-l:列出指定队列中等待运行的作业,相当于atq
-d:删除指定的作业,相当于atrm
-c:查看具体作业任务
-f /path/from/somefile:从指定的文件读取任务
linux的邮件服务:
smtp(简单邮件传输协议),用于传送邮件;
pop3(post office protocol)和imap4(Internet mail access protocol):用于接收邮件
mailx是一个简单的,纯命令行的MUA,在centos6是mail。一般使用格式mailx [ -s "subject"] username[@hostname]
邮件正文的生成:
--直接给出,ctrl+d结束输入
--输入重定向
--通过管道echo -e “how are you/nhow old are you” |mail
batch命令
让系统自行选择空闲时间去执行此处指定的任务
在centos7中有3各相关程序包:cronie主程序包,提供了crond守护进程以及相关辅助工具;cronie-anacron是cronie的补充程序,用于监控cronie任务执行状况。如果cronie中的任务在过去该运行的时间点未能正常运行,则anacron会随后启动一次此任务;crontabs包含centos提供系统维护任务。
在centos7中使用systemctl status crond来查看crond守护进程是否运行,这也是后续一切工作正常运行的前提。在centos6中使用service crond status查看。
计划要周期性执行的任务提交给crond,由其来实现定时运行。有两种方法来提交周期性任务,对于系统cron任务(系统维护任务),使用/etc/crontab文件来实现;对于用户cron任务,使用crontab命令来实现。
运行结果以邮件通知给相关用户,如果不想接收邮件,可以使用COMMAND > /dev/null或COMMAND &> /dev/null
对cron任务来讲,%有特殊用途,使用需要转义,单引号内的%可以不用转义。
系统cron任务
作业的指定格式:
例如:每天晚上9:10分运行echo命令:10 21 * * * simon /bin/echo “HI” ,最好使用绝对路径
时间表示法:
(1)特定值
给定时间点有效取值范围内的值
(2)*
给定时间上有效取值范围内的所有值
(3)离散取值:,
#,#,#
(4)连续取值:-
#-#
(5)在指定时间范围内定义步长
/#:#即为步长
每三小时运行echo命令:0 */3 * * * simon /bin/echo " How old are you!"
用户cron任务
每个用户都有专用的cron任务文件,在/var/spool/cron/USERNAME
用户使用crontab -e定义任务,使用contab -l列出自己定义的任务,使用contab -r移除所有任务
-i:交互式模式,同-r一起使用,可以有选择的移除指定的任务
-u USER:仅root可执行,代为为指定用户管理cron任务
思考:
如何实现秒级运行任务?* * * * * for min in 0 1 2;do echo "hi" ; sleep 20; done---每20秒运行一次
如何实现每7分钟运行一次任务?不能被整除不会精确运行,利用sleep命令
1、每4小时备份一次/etc目录至/backup目录中,保存的文件名称格式为“etc-yyyy-mm-dd-HH.tar.xz”;
2、每周2, 4, 7备份/var/log/messages文件至/logs目录中,文件名形如“messages-yyyymmdd”;
3、每两小时取出当前系统/proc/meminfo文件中以S或M开头的信息追加至/tmp/meminfo.txt文件中;
4、工作日时间内,每小执行一次“ip addr show”命令;