目录
强烈建议全文阅读!!!
强烈建议全文阅读!!!
强烈建议全文阅读!!!
一、进程状态
二、僵尸和孤儿进程
1、僵尸进程 Z(zombie)
2、孤儿进程:
三、进程的阻塞和挂起、运行、切换
1、阻塞态
2、挂起状态
3、进程的运行
4、进程切换
四、进程相关命令
五、进程的创建
五、队列优先级
1、什么是优先级
2、为什么要优先级
3、如何实现优先级
4、如何修改优先级
1、nice值调整
2、r(renice) 命令(常用)
3、setpriority 系统调用
一、进程状态
Linux进程状态:进程状态指的是的是进程在cpu中执行的状态
具体长什么样子?
//展示第一个进程,带头列表
ps axj | head -2
R(running) | 进程运行的状态 |
S(sleeping | 休眠状态(等待“资源”就绪 / 可中断睡眠) |
T(stopped) | 让进程暂停,等待进一步被唤醒 |
t(trcing stop) | 进程遇到追踪,进程暂停,例如代码调试 |
X(dead) | 死亡状态 |
D(disk sleep) | Linux下特有的一个进程状态,不可被杀、深度睡眠、不可中断睡眠(如何消除D状态?进程自己醒来 / 重启 -- 断电) |
Z(zombie) | 僵尸进程 |
+号:表示前台运行,没有+表示后台运行
调式代码的过程,就是通过进程暂停来实现的
杀死进程命令:
kill -number process:
查看所有的进程控制命令:
kill -l
一个数字对应一种控制
例如: 9 :杀掉进程、18:继续命令、19:暂停命令
信号名称 | 信号编号 | 含义 |
---|---|---|
SIGHUP | 1 | Hangup,终端连接断开 |
SIGINT | 2 | Interrupt,终端中断信号 |
SIGQUIT | 3 | Quit,终端退出信号 |
SIGILL | 4 | Illegal instruction,非法指令 |
SIGTRAP | 5 | Trace/Breakpoint trap,追踪/断点陷阱 |
SIGABRT | 6 | Abort,异常终止 |
SIGBUS | 7 | Bus error,总线错误 |
SIGFPE | 8 | Floating point exception,浮点异常 |
SIGKILL | 9 | Kill,强制终止 |
SIGUSR1 | 10 | User-defined signal 1,用户自定义信号1 |
SIGSEGV | 11 | Segmentation fault,段错误 |
SIGUSR2 | 12 | User-defined signal 2,用户自定义信号2 |
SIGPIPE | 13 | Broken pipe,管道破裂 |
SIGALRM | 14 | Alarm clock,闹钟信号 |
SIGTERM | 15 | Termination,终止信号 |
SIGSTKFLT | 16 | Stack fault,协处理器栈错误 |
SIGCHLD | 17 | Child status has changed,子进程状态改变 |
SIGCONT | 18 | Continue,继续执行暂停的进程 |
SIGSTOP | 19 | Stop,停止进程的执行 |
SIGTSTP | 20 | Terminal stop,终端停止信号 |
SIGTTIN | 21 | Background read from tty,后台进程尝试读取控制终端 |
SIGTTOU | 22 | Background write to tty,后台进程尝试写控制终端 |
SIGURG | 23 | Urgent condition on socket,套接字上的紧急情况 |
SIGXCPU | 24 | CPU time limit exceeded,超出CPU时间限制 |
SIGXFSZ | 25 | File size limit exceeded,超出文件大小限制 |
SIGVTALRM | 26 | Virtual alarm clock,虚拟闹钟信号 |
SIGPROF | 27 | Profiling alarm clock,分析器闹钟信号 |
SIGWINCH | 28 | Window size change,窗口大小改变 |
SIGIO | 29 | I/O now possible,异步I/O事件 |
SIGPWR | 30 | Power failure,电源故障 |
SIGSYS | 31 | Bad system call,系统调用错误 |
二、僵尸和孤儿进程
1、僵尸进程 Z(zombie)
进程已经运行完毕,但是需要维持自己的退出信息,在自己的进程PCB中会记录自己的退出信息,未来让父进程来读取
如果没有读取,就会一直维持僵尸进程状态
进程 = 内核数据结构task_struct + 进程代码和数据
当运行完毕时,进程代码和数据会被释放,凡是内核数据结构不会被释放,依然存在,等待被读取
就会存在占用内存空间,也就是内存泄漏
僵尸进程不能被kill
因为僵尸进程也相当于死掉了,不能死两次
X(dead):死亡状态,例如用waitpid(等待队列)访问僵尸进程后,就会显示X状态
2、孤儿进程:
当父进程先退出,子进程就变成了孤儿进程,因为爹死了,没有爹了
孤儿进进程一般都会被1号进程(OS)领养,以保证子进程被正常回收
命令行中启动的子进程的父进程是bash,bash会自动回收子进程
三、进程的阻塞和挂起、运行、切换
1、阻塞态
相当于S(sleep)状态,D(dead)也算
进程本身就是一个软件
操作系统对外部设备的管理,其原理也是类似于硬件的方式,先描述,在组织
那么,每一个设备都对应一个设备结构体对象
在这个设备结构体内部有一个等待队列
例如,当进程A需要用到键盘这个设备时
就把进程A这个PCB放到键盘的等待队列中进行排队
其他进程对应需要对应资源,也是这个道理
因此,不是只有CPU才有运行队列
各个设备也有各自的等待队列
那么,当把进程从CPU的运行队列拿到设备的等待队列时
此时该进程就不在运行队列,就不会被调度
这个状态,就叫做进程的阻塞状态
有了这个理解,对于进程的状态变化,还包括PCB被放到不同的队列中
同时,进入等待队列/运行队列的是进程的PCB而不是代码和数据
2、挂起状态
当一个进程处于阻塞状态时,他的PCB是在等待某个设备的资源,也就是在等待某个设备的等待队列中
此时,该进程不在CPU的运行队列中,那么也就不会被调度,不会被调度,那么进程的代码和数据也就没有用
所以,为了让操作系统的内存有更多的空间,就暂时的把该进程的代码和数据唤出到磁盘的swap区
当该进程被CPU调度执行时,又把对应的代码和数据唤入到内存中
这是操作系统的一种运行策略,相当于在已有硬件的条件下实现了2倍的内存空间
但是频繁换入唤出会导致效率问题(时间换空间策略)
3、进程的运行
每个CPU都有一个运行队列
struct runqueue
{
task_struct *head;
...
};
进程在运行队列中,进程状态就处于R(running)状态
时间片:
一个进程进入CPU运行,不会一直运行到结束才停止,而是基于时间片进行调度
在task_struct的一个属性中,有一个与调度相关的信息,叫做时间片
这个时间片用来分配进程使用CPU的时间
当运行时间片结束后,CPU就会把该进程移除,再放到运行队列尾部
同时,让多个进程以切换的方式进行调度,使得在一个时间段内同时得以推进代码,这就叫做并发
但是Linux实际上不是这样调度的,而是一个叫做O(1)调度的算法
多个CPU对应多个运行队列
此时,任何时刻都会有多个进程在真正意义上的同时运行,即并行
4、进程切换
CPU寄存器保存进程的所有临时数据
例如,当A进程执行到一半时,或由于优先级 / 时间片等因素进程切换时
CPU需要切换调度进程,执行新的进程B
此时,寄存器中原来保存的进程A的临时数据会被保存在A进程的PCB中
这个数据,叫做上下文信息
当,A进程再次被调度时,进程A要从上次被执行的位置开始
此时,寄存器需要恢复原来A进程的,即什么时候结束,什么时候开始
所以,进程切换最最重要的事情是,上下文数据的保护和恢复
CPU内部的所有寄存器的临时数据,叫做进程的上下文
四、进程相关命令
父进程是bash,命令解释器;函数用man查找
查看所有进程:
ps axj
行过滤:
| grep
显示进程属性和进程信息:
ps axj | head -1 && ps axj grep XXX
获取进程id :这是一个函数
gitpid()
获取父进程id:
ditppid()
创建子进程:
fork()
启动进程:
./XXX
结束进程:
Ctrl + c
杀掉进程:
kill -9 pid
更该cwd,当前工作目录
chdir
cwd:current work dir
获取环境变量
getenv()
查看进程优先级 +a所有进程
ps -al
查看进程:在根目录,/proc文件,实时保存内存中运行的进程(process)
cd ~
cd /proc
五、进程的创建
为什么要创建子进程?
想让子进程执行和父进程不一样的任务
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
pid_t pid;
// 创建子进程
pid = fork();
if (pid < 0) {
// 错误处理
fprintf(stderr, "进程创建失败\n");
return 1;
} else if (pid == 0) {
// 子进程执行代码
printf("这是子进程,PID为:%d\n", getpid());
// 在这里可以执行子进程的任务
} else {
// 父进程执行代码
printf("这是父进程,PID为:%d,子进程PID为:%d\n", getpid(), pid);
// 在这里可以执行父进程的任务
}
return 0;
}
fork的返回值:会有两个返回值,返回两次
0,子进程
-1,创建进程失败
>0,子进程的pid,执行父进程
为什么返回两次?
(为什么?这个知识有关地址空间,可以看博主相关文章)
进程特点:具有独立性
五、队列优先级
1、什么是优先级
本质是指定一个进程获取某种资源的先后顺序
优先级如何控制?是PCB的内部字段,int prio
在Linux中,优先级数字越小,优先级越高
2、为什么要优先级
因为CPU的资源是有限的,而进程很多
3、如何实现优先级
用进程优先级值和一个nice值,分别是PRI
PRI(Process Priority):进程优先级,值越小优先级越高
NI(nice):进程优先级的修正数据,当时一个进程执行完毕时,如果需要修改优先级,就会用nice值来调整优先级,新的优先级 = 优先级(80) + nice值,达到对应进程优先级动态修正的过程
nice值是有范围的,一般是[-20 ,19],40个数字,即40个级别,目的是确保资源分配的公平
每次调整优先级,都是从80开始
4、如何修改优先级
(一般不推荐调整进程的优先级)
在Linux系统中进行Nice 值调整:
1、nice值调整
使用 nice 命令或系统调用 setpriority 来调整进程的优先级。
nice 命令可以通过设置不同的优先级来启动命令或进程。较小的值表示较高的优先级。
示例:
nice -n -10 ./my_process # 启动一个优先级较高的进程
2、r(renice) 命令(常用)
可以使用 renice / r命令来更改正在运行的进程的优先级。
示例:
renice -n 10 -p 12345 # 将进程PID为12345的优先级调整为更低
3、setpriority 系统调用
可以通过编程方式使用 setpriority 系统调用来调整进程优先级。
示例(C语言):
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int which = PRIO_PROCESS;
id_t pid = getpid();
int priority = -10; // 设置较高的优先级
if (setpriority(which, pid, priority) != 0) {
perror("设置优先级失败");
return 1;
}
printf("优先级已设置为 %d\n", getpriority(which, pid));
return 0;
}
应用卡住的本质是:进程没有被执行调度