一、进程概述
1、文件
文件:是磁盘中的一段被标记的存储空间,本质上是一段数据流,这个标记叫文件名
文件名可以引用这段数据
文件存储格式:
二进制格式:
文本格式(ASCII码):
可执行文件:独特的ELF格式,前几个字节标识文件的格式,
程序=指令+数据=算法+数据结构
2、进程
进程是程序的一个具体实现,同一个程序可以执行多次,每次都可以在内存中开辟独立的空间来装载,从而产生多个进程,不同的进程还可以拥有各自独立的IO接口。
进程ID(Process ID,PID)号码被用来标记各个进程
UID,GID和SELinux语境决定进程对文件系统的存取和访问权限,通常从执行进程的用户来继承
每个进程都存在生命周期:从创建开始,到运行结束就是整个进程的一个生命周期,而程序是个静态的文件.
task struct(任务结构体):Linux内核存储进程信息的数据结构格式,内核就可以追踪到进程
task list(任务列表):多个任务的task struct组成的链表
每个进程意识不到别的进程的存在,以为只有自己和内核占用整个内存
进程的启动和调度都是内核完成的
线性地址空间(虚拟内存):内核为每个进程虚拟出一个内存空间,
page frame:内存叶框,
init是所有进程的父进程
父进程通过fork()生成子进程,
线程:是进程的执行流,可以共享资源,线程是线程库实现的,
linux单内核:没有专门的线程管理工具,也看成进程,轻量级进程
3、进程和线程的区别
进程(process):是程序的具体实现,是已运行程序的副本,进程是程序的基本执行实体,进程本身不是基本运行单位,而是线程的容器
线程(thread):是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
1)进程是系统进行资源分配的基本单位,有独立的内存地址空间;线程是CPU调度的基本单位,没有单独地址空间,有独立的栈,局部变量,寄存器, 程序计数器等。
2)创建进程的开销大,包括创建虚拟地址空间等需要大量系统资源; 创建线程开销小,基本上只有一个内核对象和一个堆栈。
3)一个进程无法直接访问另一个进程的资源;同一进程内的多个线程共享进程的资源。
4)进程切换开销大,线程切换开销小;进程间通信开销大,线程间通信开销小。
5)线程属于进程,不能独立执行。每个进程至少要有一个线程,成为主线程
4、进程的创建
当计算机开机的时候,内核(kernel)只建立了一个init进程。Linux kernel并不提供直接建立新进程的系统调用。剩下的所有进程都是init进程通过fork机制建立的。新的进程要通过老的进程复制自身得到,这就是fork,fork是一个系统调用。进程存活于内存中。每个进程都在内存中分配有属于自己的一片空间 (address space)。当进程fork的时候,Linux在内存中开辟出一片新的内存空间给新的进程,并将老的进程空间中的内容复制到新的空间中,此后两个进程同时运行。
当父进程创建子进程时,子进程和父进程使用的是同一段内存空间;一旦子进程需要对该段内存空间的数据进行修改时,就会复制该段内存空间的数据到另外一段内存空间,子进程就指向了该段新的内存空间
这种机制叫CoW(写时复制),如果子进程不对数据进程修改,跟父进程使用的内存空间都是同一个,但是一旦子进程要修改数据,就复制一份数据到另一块内存供子进程单独使用,而从此之后,子进程就一直使用新的内存空间了
进程的终止:子进程完成一定的任务之后,释放掉自己占用的资源,然后父进程对子进程进行回收
老进程成为新进程的父进程(parent process),而相应的,新进程就是老的进程的子进程(child process)。一个进程除了有一个PID之外,还会有一个PPID(parent PID)来存储的父进程PID。如果我们循着PPID不断向上追溯的话,总会发现其源头是init进程。所以说,所有的进程也构成一个以init为根的树状结构。
[root@Note3 ~]# pstree init─┬─ManagementAgent───2*[{ManagementAgen}] #centos7把init改成了systemd ├─NetworkManager ├─VGAuthService ├─atd ├─auditd───{auditd} ├─console-kit-dae───63*[{console-kit-da}] ├─crond ├─cupsd ├─dbus-daemon ├─dhclient ├─hald─┬─hald-runner─┬─hald-addon-acpi │ │ └─hald-addon-inpu │ └─{hald} ├─master─┬─pickup │ └─qmgr ├─6*[mingetty] ├─modem-manager ├─polkitd ├─rsyslogd───3*[{rsyslogd}] ├─sshd─┬─3*[sshd───bash] │ └─sshd───bash───bash───pstree ├─ssserver ├─udevd───2*[udevd] ├─vmtoolsd───{vmtoolsd} └─wpa_supplicant
5、CPU的工作机制
CPU的工作机制:
每台服务器有很多进程等待CPU来处理,每个进程都必须轮流让CPU来处理,如果第一个进程一直霸占着CPU不放,就会导致后面的进程一直无法处理。
CPU是分时的:一个CPU在同一时刻只能执行一个任务,CPU自身的寄存器中存放着正在运行的进程的状态信息,当该进程分配的时间片内没有完成任务时,也会被内核调度出去,去执行下一个进程,但是之前没有完成任务的进程数据不会就此丢失,而是会放到内核指定的一个task struct(任务结构体)来存放进程信息,这就称为保存现场,当再一次轮到这个没有完成的进程到CPU执行时,内核就会再度调回这个保存现场的数据(这就被称为恢复现场),来继续执行,直到完成任务.
6、内存的工作机制
内存的工作机制是分空间的:内存分配方式有点类似磁盘,分成很多块,但在内存里不叫块,而是叫页框(page frame)用于存储数据,一般一个page的大小为4K。分配的页框可以有多个,且可以不连续。
进程需要用到内存数据的时候,怎么去找内存中不连续的数据?
内核会虚拟一个内存空间面向进程,进程不需要直接与实际的物理存储的内存打交道,只需与内核虚拟出来的内存打交道,内核虚拟出来的内存的大小,与计算机平台的位数有关系,内核虚拟出来的内存空间叫做线性地址空间,内核虚拟出来的内存对进程而言是连续,且独占的。这样,进程读写内存数据时就认为自己读取的是一个连续的地址空间,且是独占的,这段虚拟出来的空间中,只有真正被使用的部分,内核才会在物理内存上分配空间进行存储
swap分区:为了防止过多的进程将实际物理内存占满导致程序无法运行,从而有了swap交换分区的概念,swap分区实际是用来临时存放内存中暂时用不到的页面数据。置换时,通常采用LRU算法(最近最少使用)将最近最少用到的数据暂存在交换分区中,
不能交换的进程称为常驻进程集,能够被交换的进程称为虚拟进程集
一次磁盘IO分为两段进行:第一段是将数据从磁盘拿到内存中内核空间,第二段时将内核空间的数据复制一份放到用户空间
linux运行中的内核的相关信息是通过/proc伪文件系统输出的,各进程都有一个以其PID命名的子目录,每个子目录中有许多文件存储了进程的相关状态信息
7、上下文切换
在进程运行过程中,进程的运行信息被保存于处理器的寄存器和它的缓存中。正在执行的进程加载到寄存器中的数据集被称为上下文。为了切换进程,运行中进程的上下文将会被保存,接下来的运行进程的上下文将被被恢复到寄存器中。进程描述和内核模式堆栈的区域将会用来保存上下文。这个切换被称为上下文切换。过多的上下文切换是不受欢迎的,因为处理器每次都必须清空刷新寄存器和缓存,为新的进程制造空间。它可能会引起性能问题。
8、中断处理
中断处理是优先级最高的任务之一。中断通常由I/O设备产生,例如网络接口卡、键盘、磁盘控制器、串行适配器等等。中断处理器通过一个事件通知内核(例如,键盘输入、以太网帧到达等等)。它让内核中断进程的执行,并尽可能快地执行中断处理,因为一些设备需要快速的响应。它是系统稳定的关键。当一个中断信号到达内核,内核必须切换当前的进程到一个新的中断处理进程。这意味着中断引起了上下文切换,因此大量的中断将会引起性能的下降。
在Linux的实现中,有两种类型的中断。硬中断是由请求响应的设备发出的(磁盘I/O中断、网络适配器中断、键盘中断、鼠标中断)。软中断被用于处理可以延迟的任务(TCP/IP操作,SCSI协议操作等等)。你可以在/proc/interrupts文件中查看硬中断的相关信息。
在多处理器的环境中,中断被每一个处理器处理。绑定中断到单个的物理处理中能提高系统的性能。
二、进程管理
1、进程分类
守护进程:daemon,跟终端无关,由内核在系统引导过程中启动的进程
用户前台进程:用户通过终端启动的进程,跟终端相关,ps查看STAT显示为+,终端关闭,进程也将被关闭
注意:也可把前台启动的进程收网后台,以守护模式运行
cpu密集型
IO密集型
2、进程的状态
运行态:running
当进程正在被CPU执行,或等待运行(已经准备就绪随时可由调度程序执行),则该进程处于运行态(running).
睡眠态:sleeping
可中断睡眠:interuptible,认为自己的任务完成了
处于这个状态的进程不会被系统调度,当系统产生了一个中断或者释放了进程正在等待的资源,或者收到一个信号,都可以唤醒进程转换到就绪态(运行态)。
不可终端睡眠:uninteruptible,被IO阻塞
与可中断睡眠转态类似,但只能被wake_up()函数唤醒才能转换为就绪态。
就绪态:runnable
当系统资源已经可用时,进程就被唤醒而准备进入准备运行状态
停止态:stopped,不可被调度并运行
进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时会转换为暂停转态,向其发送SIGCONT信号可让其转换为可运行状态。
僵死态:zombie,没有父进程,不释放内存
当进程已经停止运行,但其父进程还没有询问其状态时,该进程处于僵死状态。
3、进程优先级和nice值
当我们在执行多个作业时,其实每个作业都会进入到CPU的任务分配中,等待CPU来执行,而CPU会根据每个作业的优先级(priority)来判断谁比较重要,所以有的作业就会优先执行。
Linux 系统中,每个进程都会拥有一个“优先级(priority)”属性,利用该属性,让CPU判断哪个作业比较重要,哪个作业会优先执行,这使得系统资源分配得更合适。我们可以使用ps来观察优先级。
PRI就是Priority的简写,NI是nice的简写,它们凑在一起,才产生当前的PRI值。PRI越小,表示该进程“优先级越高”,PRI是由系统动态产生的,不是一直固定的值。NI(nice)则是我们额外提供的一个数值,它可以影响PRI的值,它的关联性是这样的:
PRI(new)= RPI(old)+ nice
进程调度:通过调整进程的优先级来实现
优先级:0-139 数字越小,优先级越高
实时优先级:0-99,由内核调整的,
静态优先级:100-139,用户可通过调整nice值来调整优先级,
进程的nice值:用来手动调整调整进程的优先级 #nice值越大优先级越低
-20,19
对应优先级:100,139
进程的默认nice值为0,优先级为120
普通用户只能调大进程的nice值,优先级越低,
调整nice值的方法:
对于尚未启用的程序:nice -n N COMMAND
对于运行中的进程:renice N PID
[root@Note3 ~]# ps axo nice,command|grep ping 0 ping 192.168.10.10 0 grep ping [root@Note3 ~]# nice -n -5 ping 192.168.10.10 PING 192.168.10.10 (192.168.10.10) 56(84) bytes of data. 64 bytes from 192.168.10.10: icmp_seq=1 ttl=128 time=0.422 ms 64 bytes from 192.168.10.10: icmp_seq=2 ttl=128 time=0.231 ms 64 bytes from 192.168.10.10: icmp_seq=3 ttl=128 time=0.216 ms 64 bytes from 192.168.10.10: icmp_seq=4 ttl=128 time=0.229 ms 64 bytes from 192.168.10.10: icmp_seq=5 ttl=128 time=0.385 ms [root@Note3 ~]# ps axo pid,nice,command|grep ping 22624 -5 ping 192.168.10.10 22703 0 grep ping [root@Note3 ~]# renice 10 22624 22624: old priority -5, new priority 10 [root@Note3 ~]# ps axo pid,nice,command|grep ping 22624 10 ping 192.168.10.10 22780 0 grep ping
4、进程间通信(IPC)
SIGNAL 信号
kill命令能用于实现向其它进程发送信号
使用格式:
kill -SIGNAL 进程名
查看信号:
kill -l
man 7 signal
信号表示方式:
(1) 完整名称,例如SIGINT
(2) 简写名称,例如INT
(3) 数据代称,例如1,2,9,15
常用信号:
SIGHUP:1 通知进程重读其配置文件以让新的配置生效,但不用重新启动进程;
SIGINT:2 打断正在运行中的进程,相当于键盘组合键Ctrl+c
SIGKILL:9 强行中止正在运行中的进程
SIGTERM:15 安全中止正在运行中的进程,kill默认使用的信号
SIGSTOP:19 暂停进程
SIGCONT:18 继续运行指定进程
kill命令常用命令:
kill [-SIGNAL] COMMAND
killall [-SIGNAL] 进程名
pkill
基于名称和其他属性查找或信号处理,常用于踢出用户
1. 将某个终端的用户踢出
pkill -kill -t pts/2(终端号) 或 pkill -9 -t pts/2
2. 按用户名踢出用户
pkill -kill -U test(用户名) #大小u写都可以
二、作业管理
作业是shell里面的一个概念,我们的所有操作都是提交给shell,然后通过shell进行解释后再执行
区别:进程是一个程序在一个数据集上的一次执行,而作业是用户提交给系统的一个任务。
关系:一个作业通常包括几个进程,几个进程共同完成一个任务
一个作业是我们提交给shell的一项任务或者批处理,和操作系统无关。
进程是具体执行的一个可执行程序,是操作系统调度的对象。
1、作业分类
前台作业:通过终端启动,并且在停止之前也会一直占据终端;
后台作业:作业启动之时与终端无关,或者是在前台启动,但启动后转为与终端无关模式运行;
非守护进程类的程序,启动以后都在前台工作
如何让作业运行于后台?
对于已经启动并处于运行中的作业:
Ctrl+z
注意: 作业被送往后台后,默认处于stopped状态;
对于尚未启动的作业:
COMMAND & #在后台运行,但如果进程有输出的话仍会输出于标准输出
注意: 此两类方式相关作业,仍然与终端相关;这意味着,终端终止,将会导致与此终端相关的所有作业被终止;
剥离进程与终端的关系: #不会随终端关闭而终止
nohup COMMAND &
jobs:
显示当前shell 环境中已启动的作业状态。
[root@Note3 ~]# jobs [1]+ Stopped nice -n -5 ping 192.168.10.10
作业号、作业状态、命令
+号是表示默认将被fg调回前台的作业
-号是下一个变成+号的作业
用linux的时候经常会碰到类似这种情形,复制,下载一个很大的文件或编辑一个文件,任务占据着界面不能做其他操作,这个时候想不暂停或中止任务去做别的操作就可以将正在执行的命令送往后台去运行。
作业控制命令:
fg [[%]job_num]:把指定的作业调回前台继续执行;默认将带+号的作业调回到前台,
bg [[%]job_num]:把调往后台的指定的作业启动起来,让其后台默默运行;但此作业必须支持运行于后台;
kill [%job_num]:终止指定的作业;
前台进任务,后台任务,守护进程的区别参考:http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html