1.进程:
程序:存放在外存中的一段数据组成的文件
进程:是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡
2.进程相关命令:
1.top
动态查看当前系统中的所有进程信息(根据CPU占用率排序)
PID:唯一识别进程的ID号(>0)
优先级:在 Linux 系统中,优先级值范围一般为 -20 到 19,数值越低表示优先级越高;而在 Windows 系统中,优先级数值越高表示优先级越高,通常为 0 到 31。
进程状态:
- R(运行态/就绪态):表示进程正在运行或者已经准备好运行,可被 CPU 调度执行。
- S(睡眠态/可唤醒等待态):表示进程正在等待某个事件的完成,例如等待输入/输出完成或等待某个资源的释放,它可以被唤醒进入运行态。
- D(不可唤醒等待态):表示进程正在等待某个不可中断的事件发生,例如等待硬盘的输入/输出操作完成,此时进程无法被唤醒。
- T(暂停态):表示进程被用户或其他进程暂停执行,可以通过发送信号或其他方式恢复进程的执行。
- Z(僵尸态):表示进程已经完成执行,但是父进程尚未回收其退出状态,进程仍然存在于系统的进程表中,因此被称为"僵尸"。
- X(结束态):表示进程已经正常或异常结束,没有存在于系统中。
q退出
2.nice
以指定优先级来运行进程
示例:
nice -n 优先级 要执行的集成任务
renice
重新设定一个正在运行的进程的优先级
示例:
renice -n 优先级 进程PID
3.kill
杀死指定的进程任务
示例:
kill -9 进程PID
killall
杀死进程名对应的所有进程任务
示例:
killall -9 进程名
4.ps -ef
查看当前时刻所有进程的信息
PPID:父进程的ID号
ps -ef | grep a.out
5.pstree
查看进程树关系
6.ps -aux
查看当前时刻的进程信息
7../a.out &
将a.out任务放在后台执行
8.jobs
查看一个终端下后台执行的所有任务
9.fg 编号
将后台任务放到前台执行
3.进程的创建
32bits
一个进程在运行时,操作系统会为该进程分配 0 - 4G 虚拟内存空间,分为文本段、数据段、系统数据段
文本段:
也称为文本区,存放代码和指令
数据段:
也称为数据区,可以细分为:
1.字符串常量区
2.未初始化全局变量/静态变量
3.已初始化全局变量/静态变量
系统数据段:
包含堆区和栈区
4.进程中虚拟地址和物理地址的关系
实际物理地址:每个进程的虚拟地址空间在执行时需要映射到实际的物理地址空间。每个进程的物理地址空间是独立的,不同的进程之间彼此隔离,互不干扰。物理地址是实际存储器中的地址,存放着进程需要的代码,数据和堆栈等信息。
MMU 内存映射单元:MMU 是 CPU 中的一个硬件单元,负责虚拟地址到物理地址的转换。当一个进程运行时,MMU 会根据虚拟地址到物理地址的映射关系,将虚拟地址中的数据加载到对应的物理地址中执行。MMU 会维护一个页表(Page Table)来记录虚拟地址到物理地址的映射关系。
5.进程的调度:
1.常见的调度算法:
1.先来先执行,后来后执行
2.高优先级调度算法
3.时间片轮转调度算法
4.多级队列反馈调度算法
5.负载均衡调度算法
时间片:
1.CPU在一个任务中的运行时间称为一个时间片
2.宏观并行,微观串行
3.进程的状态:
R 运行态、就绪态
S 睡眠态/可唤醒等待态
D 不可唤醒等待态
T 暂停态
Z 僵尸态
X 结束态
6.进程相关函数接口:
1.进程的创建
fork
pid_t fork(void);
功能:
创建一个子进程,新创建的进程称为原来进程的子进程,原来的进程称为新进程的父进程
参数:
void 缺省
返回值:
成功子进程返回0
父进程返回子进程的PID
失败返回-1
父进程调用fork创建子进程,子进程拷贝父进程的文本段、数据段、系统数据段
getpid
pid_t getpid(void);
功能:
获得调用进程的PID号
getppid
pid_t getppid(void);
功能:
获得调用进程的PPID
练习:
创建一个父进程的2个子进程,子进程中打印自己的PID和父进程的PID
父进程中打印自己的PID和两个子进程的PID
#include "head.h"
int main(void)
{
pid_t pid1;
pid_t pid2;
pid1 = fork();
if (-1 == pid1)
{
perror("fail to fork");
return -1;
}
if (0 == pid1)
{
printf("子进程1 PID:%d PPID:%d\n", getpid(), getppid());
}
else if (pid1 > 0)
{
pid2 = fork();
if (-1 == pid2)
{
perror("fail to fork");
return -1;
}
if (0 == pid2)
{
printf("子进程2 PID:%d PPID:%d\n", getpid(), getppid());
}
else if (pid2 > 0)
{
printf("父进程 PID:%d child1PID:%d child2PID:%d\n", getpid(), pid1, pid2);
}
}
while (1)
{
}
return 0;
}
2.exit
void exit(int status);
功能:
让进程结束
参数:
status:进程结束的状态
返回值:
缺省
exit在主函数中使用和return效果一致
exit会刷新缓存区
_exit
void _exit(int status);
功能:
让进程直接结束
参数:
status:进程结束的状态
返回值:
缺省
#include"head.h"
9 void fun(void)
10 {
11 exit(0);
12 }
13 int main(void)
14 {
15 printf("hello world");
16 fun();
17 printf("how are you");
18 return 0;
19 }
输出hello world
#include"head.h"
9 void fun(void)
10 {
11 _exit(0); //不刷新缓存
12 }
13 int main(void)
14 {
15 printf("hello world");
16 fun();
17 printf("how are you");
18 return 0;
19 }
不打印内容
7.进程的消亡
1.僵尸进程:
进程代码执行结束,空间没有被回收,称为僵尸进程
2.如何避免产生僵尸进程?
1.让父进程先结束
2.让父进程回收子进程空间
3.孤儿进程:
进程的父进程先结束,此时该进程称为孤儿进程,被系统进程收养,进程再结束时,会被系统进程回收进程空间
8.wait
pid_t wait(int *wstatus);
功能:
回收子进程空间
参数:
wstatus:存放子进程结束状态空间的首地址
返回值:
成功返回回收到的子进程PID
失败返回-1
1.wait函数具有阻塞功能
2.wait函数具有同步功能
WIFEXITED(wstatus)
进程是否正常退出
WEXITSTATUS(wstatus)
进程结束状态值
WIFSIGNALED(wstatus)
进程是否被信号杀死
WTERMSIG(wstatus)
获得杀死进程的信号编号
#include "head.h"
int main(void)
{
pid_t pid;
pid_t ret;
int wstatus;
pid = fork();
if (-1 == pid)
{
perror("fail to fork");
return -1;
}
if (0 == pid)
{
printf("子进程开始执行: PID:%d PPID:%d\n", getpid(), getppid());
sleep(10);
printf("子进程即将结束!\n");
exit(10);
}
else if (pid > 0)
{
printf("父进程开始执行: PID:%d\n", getpid());
ret = wait(&wstatus);
if (-1 == ret)
{
perror("fail to wait");
return -1;
}
printf("回收到 %d 子进程空间\n", ret);
if (WIFEXITED(wstatus))
{
printf("正常结束,值为 %d\n", WEXITSTATUS(wstatus));
}
else if (WIFSIGNALED(wstatus))
{
printf("被 %d 号信号杀死\n", WTERMSIG(wstatus));
}
}
return 0;
}