目录
1.进程的基本概念
1.1基本概念
1.11进程与程序的区别:
1.12并发与并行的区别
1.2进程的状态及转换
1.3描述进程—–PCB
task_struct -- PCB的一种
1.4查看进程
ps 命令
top 命令
2.进程创建
3.进程状态
ps aux / ps axj 命令
进程状体具体讲解
R运行状态
S 睡眠状态
D 磁盘休眠状态
T 暂停状态
X 死亡状态 & Z 僵尸状态
僵尸进程
孤儿进程
进程优先级
基本概念
查看系统进程
1.进程的基本概念
1.1基本概念
进程Process是指计算机中已运行的程序,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
我们在编写完代码并运行起来时,在我们的磁盘中会形成一个可执行文件,当我们双击这个可执行文件时(程序时),这个程序会加载到内存中,这个时候我们不能把它叫做程序了,应该叫做进程。
所以说,只要把程序(运行起来)加载到内存中,就称之为进程。
进程=对应的代码和数据 + 进程对应的PCB
进程的概念:程序的一个执行实例,正在执行的程序等
-
狭义观点:程序的一个执行实例,正在执行的程序等
-
内核观点:担当分配系统资源(CPU时间,内存)的实体
程序加载到内存中,名字叫进程
程序和进程的区别:
程序:就是磁盘上的可执行文件文件,并且只占用磁盘上的空间,是一个静态的概念。
进程:被执行之后的程序叫做进程,不占用磁盘空间,需要消耗系统的内存,CPU资源,每个运行的进程的都对应一个属于自己的虚拟地址空间,这是一个动态的概念。
1.11进程与程序的区别:
程序是永存的;进程是暂时的,是程序在数据集上的一次执行,有创建有撤销,存在是暂时的; 程序是静态的观念,进程是动态的观念; 进程具有并发性,而程序没有; 进程是竞争计算机资源的基本单位,程序不是。 进程和程序不是一一对应的: 一个程序可对应多个进程即多个进程可执行同一程序; 一个进程可以执行一个或几个程序
1.12并发与并行的区别
-
并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。所以无论从微观还是从宏观来看,二者都是一起执行的。
-
并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。这就好像两个人用同一把铁锨,轮流挖坑,一小时后,两个人各挖一个小一点的坑,要想挖两个大一点得坑,一定会用两个小时。
并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在,并发能够在单处理器系统中存在是因为并发是并行的假象,并行要求程序能够同时执行多个操作,而并发只是要求程序假装同时执行多个操作(每个小时间片执行一个操作,多个操作快速切换执行)。
1.2进程的状态及转换
进程整个生命周期可以简单划分为三种状态:
就绪态:进程已经具备执行的一切条件,正在等待分配CPU的处理时间。
执行态:该进程正在占用CPU运行。
等待态:进程因不具备某些执行条件而暂时无法继续执行的状态。
进程的调度机制:
时间片轮转,上下文切换,多进程不是说一个进程执行完再执行另一个进程,而是交替执行的,一个进程执行一段时间,然后下一个进程在执行一段时间,依次类推,所有进程执行完之后再回到第一个今年初继续执行以此类推。
1.3描述进程—–PCB
PCB:进程控制块(结构体) 当一个程序加载到内存中,操作系统要为刚刚加载到内存的程序创建一个结构体(PCB),进程信息被放在这个结构体中(PCB),可以理解为PCB是进程的属性的集合。
在Linux操作系统下的PCB是:task_struct task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息
PCB:包含了进程内的所有的属性
为什么存在PCB?
操作系统要进行软件的资源管理,要管理资源就先要对资源描述再组织,要描述就必须要PCB结构体
如何创建进程?
把一个程序加载到内存中变成进程, 操作系统会新增一个PCB, 连接到双向链表中.
如何删除进程?
在双向链表删除对应的PCB, 释放空间
对进程的管理,变成了对进程PCB结构体链表的增删查改
task_struct -- PCB的一种
-
在Linux中描述进程的结构体叫做task_struct。
-
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
task_ struct内容分类
-
标示符:描述本进程的唯一标示符,用来区别其他进程。
-
状态:任务状态,退出代码,退出信号等。
-
优先级:相对于其他进程的优先级。
-
程序计数器:程序中即将被执行的下一条指令的地址。
-
内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
-
上下文数据:进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
-
1/0状态信息:包括显示的1/0请求,分配给进程的I/ ◎设备和被进程使用的文件列表。
-
记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
-
其他信息
1.4查看进程
每个进程都由一个进程号来标识,其类型为pid_t,进程号的范围:0~32767 进程号是由操作系统随机给当前进程分配的,不能自己控制 进程号总是唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再次使用了
通过系统文件的方式产看进程:
ls /proc
大多数进程信息同样可以使用top和ps这些用户级工具来获取
ps 命令
ps
命令用于显示当前系统进程的快照(一次性地显示)。它的使用方式如下:
ps [options]
其中 [options]
是可选的参数,用于过滤和格式化输出。常用的参数包括:
-
-e
或-A
:显示所有进程。 -
-f
:显示完整格式的信息。 -
-u user
:显示特定用户的进程。 -
-aux
:显示所有进程,包括系统守护进程,并显示详细信息,是常用的组合选项。
示例:
ps -aux
top 命令
top
命令用于实时显示系统的运行进程和系统资源的使用情况。它的使用方式如下:
top [options]
top
命令会实时刷新显示系统的运行情况,包括 CPU 占用率、内存使用情况以及各个进程的运行情况。
常用的参数包括:
-
-d delay
:设置刷新间隔时间(秒)。 -
-u username
:只显示特定用户的进程。 -
-p pid[,pid...]
:只显示指定进程的信息。
示例:
top
若要查看指定文件的进程:
ps aux | grep [文件名]
PPID:父进程id(当前进程的父进程的进程号)
PID:当前进程的id
PGID:当前进程所在的组的进程组ID
COMMAND:当前进程的名字
ps aux | grep [文件名] | grep -v grep
列出包含 “test” 关键字的进程,同时排除掉
grep
进程本身
2.进程创建
系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型.父进程是从 main () 函数开始运行的,子进程是在父进程中调用 fork () 函数之后被创建,子进程就从 fork () 之后开始向下执行代码。
#include <sys/types.h>#include <unistd.h>pid_t fork(void);
功能: 用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。 参数:无 返回值(fork函数会返回两次值): 成功:子进程中返回 0,父进程中返回子进程 ID。pid_t,为整型。 失败:返回-1。 失败的两个主要原因是: 1)当前的进程数已经达到了系统规定的上限,这时 errno 的值被设置为 EAGAIN。 2)系统内存不足,这时 errno 的值被设置为 ENOMEM
fork函数会返回两次值: ——给父进程返回子进程的pid。 ——给子进程返回0。 ——失败时,在父进程中返回-1,不创建子进程,并且errno被适当地设置。
看如下代码:
#include<stdio.h>#include<sys/types.h>#include<unistd.h>int main(){printf("I am parent process : pid: %d\n\n" ,getpid() );pid_t ret=fork();printf("ret:%d, pid:%d ppid:%d\n",ret,getpid(),getppid());sleep(1);return 0}
父子进程交替执行:
#include <stdio.h>#include <unistd.h>int main(){pid_t id = fork();if (id < 0){/* 创建失败*/perror("fork");return 1;}else if (id == 0){/* child process(task)*/while (1){printf("I am child , pid:%d , ppid:%d\n ", getpid(), getppid());sleep(1);}}else{/* parent process */while (1){printf("I am parent , pid:%d , ppid:%d\n ", getpid(), getppid());sleep(1);}}printf("you can see me!\n");sleep(1);return 0;}
运行:
以上程序可见 子进程复制了父进程的空间 子进程和父进程都拥有各自独立的空间,所以子进程之修改了子进程的num
总结: 创建 子进程 那么 子进程 复制父进程的资源 父子进程拥有独立的空间
3.进程状态
操作系统存在着五种状态模型:
-
新建态:刚刚创建的进程,操作系统还没有把它加入可执行进程组中。
-
就绪态:进程已经做好准备,只有有机会就会开始执行。
-
运行态:该进程正在执行。
-
阻塞态:进程在某些事件发生前不能执行,如I/O操作完成。
-
退出态:操作系统从可执行进程组中释放出进程,或者自身停止,或者是因为某些原因被取消。
2为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。 下面的状态在kernel源代码里定义
/** The task state array is a strange "bitmap" of* reasons to sleep. Thus "running" is zero, and* you can test for combinations of others with* simple bit tests.*/static const char * const task_state_array[] = {"R (running)", /* 0 */"S (sleeping)", /* 1 */"D (disk sleep)", /* 2 */"T (stopped)", /* 4 */"T (tracing stop)", /* 8 */"X (dead)", /* 16 */"Z (zombie)", /* 32 */};
-
R运行状态(running):并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
-
S睡眠状态 (sleeping):意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep) ) .
-
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待10的结束。
-
T停止状态(stopped):可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
-
t停止状态:体现在代码调试阶段
-
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
查看进程状态
ps aux / ps axj 命令
-
ps aux
:-
ps
表示 “process status”,用于显示当前系统中的进程状态。 -
aux
:是选项,含义如下:-
a
表示显示所有用户的进程,而不仅仅是当前用户的进程。 -
u
表示显示用户与进程相关的详细信息,比如进程的拥有者、CPU 占用、内存占用等。 -
x
表示显示没有控制终端的进程,这样可以包括那些非终端的进程,比如守护进程。
-
因此,
ps aux
会列出系统中所有用户的所有进程信息,包括用户、PID、CPU 占用、内存占用等。 -
-
ps axj
:-
ps
同样表示 “process status”。axj
:是选项,含义如下:-
a
和x
的作用与上述相同。 -
j
表示以作业控制格式显示进程信息,包括作业控制信息和资源限制等。
-
因此,
ps axj
会以作业控制格式显示系统中所有进程的信息,包括作业控制信息和资源限制等。 -
ps aux
和 ps axj
命令都可以用来查看系统中的进程信息,但是它们的输出格式略有不同,ps aux
提供了更为详细的用户与进程相关信息,而 ps axj
提供了作业控制格式的信息
进程状体具体讲解
R运行状态
进程中的R状态不代表正在运行,代表的可被调度。
操作系统会把进程中R状态的进程全放在调度队列中,方便调度。
#include<stdio.h>
#include<unistd.h>
int main()
{while(1)printf(".");return 0;
}
S 睡眠状态
用代码创建一个睡眠状态的进程:
#include<stdio.h>
#include<unistd.h>
int main()
{while(1)sleep(10);return 0;
}
S状态是浅度睡眠,随时可以被唤醒,也可以被杀掉。
D 磁盘休眠状态
可以表示为深度睡眠,该进程不会被杀掉,即使你是操作系统,除非我自动唤醒,才可以恢复。
为了防止这个情况的发生,操作系统就搞了个D状态。
这种状态(D)的进程杀不死。
T 暂停状态
向进程发送SIGSTOP信号,该进程会响应该信号进入暂停状态, 向该进程发送SIGCONT信号,该进程会从暂停状态恢复到可执行状态。
X 死亡状态 & Z 僵尸状态
僵尸状态:一个处于僵尸状态的进程,会等待它的父进程或操作系统对它的信息进行读取,之后才会被释放。
死亡状态:进程被操作系统释放了或者自己退出了。
僵尸进程
当一个进程变为僵尸状态的时候,该进程就变成了僵尸进程。
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
僵尸进程危害
-
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
-
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
-
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因力数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间
孤儿进程
在Linux中,进程的关系主要是父子关系。
一对父子进程中的父进程退出了,子进程还在运行,就会形成孤儿进程。 如果没有进程来回收该子进程的信息,那么会变成僵尸状态,会存在内存泄漏的问题。
为了解决这个问题,该子进程会立即被1号init进程领养。‹
通过代码来模拟僵尸状态的进程:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main(){pid_t id=fork();int count=5;while(1){if(id==0){while(1){printf("i am process..child---.pid:%d,ppid:%d\n",getpid(),getppid());sleep(1);}printf("child quit....\n");exit(1);}else if(id>0){while(count){printf("i am process..father---pid:%d,ppid:%d\n",getpid(),getppid());count--;sleep(1);}exit(0);}}return 0;}
用while :; do ps axj |head -1&&ps axj|grep alone;sleep 1 ;echo "#############" ; done
来监控进程的状态。
进程优先级
基本概念
CPU中的资源是有限的,不可能多个进程一起在CPU上运行,利用优先级把进程有效的先后排好,改善了系统的性能。
-
cpu资源分配的先后顺序,就是指进程的优先权(priority)
-
优先权高的有优先执行权。
查看系统进程
用ps -l
可以查看到进程的优先级
PRI:表示这个进程被执行的优先级,其值越小越早执行 NI:表示这个进程的nice值
PIR and NI nice值表示进程可被执行的优先级的修正值。 PIR=PIR(old)+nice。 当nice为负值时,那么该进程的优先级值会变小,优先级会变高,进程越快被执行。
当然,nice也是有范围的,-20~19,一共40个级别。
查看进程的优先级 用top命令更改已存在的进程的nice
top
命令
接着按r
然后输入进程的PID
输入nice
值
附加其他概念 竞争性:系统进程数目多,而CPU的资源有限,所以进程之间是具有竞争属性的。为了高效完成任务,更合理的竞争相关资源,便有了优先级 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰 并行:多个进程在多个CPU下分别,同时进行运行,这称之为并行 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发