目录
在 Linux 下跑起来的程序就叫做进程;
可以和文件对比来理解,我们知道 文件是由文件内容和文件属性组成的,而进程是由自己的属性和对应的文件组成,在操作系统中,进程的数据和属性存在PCB中,在Linux中,PCB等于task_struct;
操作系统通过对 task_struct 来管理进程,就相当于在大学,学校通过对我们的数据属性来管理我们一样;
进程 = task_struct + 文件(文件属性+文件内容)
当一段代码编译好后生成可执行程序后,当执行这个可执行程序,必然会产生一个以可执行程序同名的进程,那么进程如何查看呢?
ps ajx
ps ajx | head -1
ps ajx | grep 进程名
ps ajx | grep 进程名 | grep -v grep
grep -v grep可以去掉grep的进程:
ps ajx | head -1 && ps ajx | grep 进程名 | grep -v grep
打印第一行信息和指定进程:
目录 proc 保存的是当前系统的实时进程信息,我们可以通过 进程对应的 PID来验证此目录
进程的一个进程只有一个pid,pid与进程相互对应,在 proc 目录下,存有进程的pid:
上述用 ps ajx 来查看进程的信息,可以获取到进程的 pid :
系统调用的方式:
对比两种方式,我们发现同一个进程,pid不同,原因是因为当一个进程结束后,再次调用此进程,系统会重新分配空间给进程,pid也会改变;
与创建变量,每一次运行时变量地址都不同是一个道理
ppid是父进程,父进程控制子进程,通过子进程来完成一个程序的执行
可以通过上述两种方式查看,这里看一下系统调用的方式:
进程既然已经存在了,那么proc中一定存在此进程的pid:
若此时我们结束掉此进程:
再次开始进程:
cwd:进程当前的工作路径;
进行文件操作时,默认文件的创建路径是当前路径,这个当前路径指的就是 cwd -> 进程的工作路径
exe:进程对应的可执行程序的磁盘文件
我们用 ./ 来将程序运行起来,起始就是手动创建了一个子进程,除了手动创建,还可以通过系统调用来创建子进程
调用成功,给子进程返回 0,父进程返回子进程的 pid
有两个返回值
调用失败,返回 -1
子进程也会有自己的 task_struct 来存储自己的属性,其中 task_struct 内的大部分数据都是从父进程的 task_struct 中继承下来的;
fork() 以上和父进程执行相同的代码;
fork() 以下的代码段共享:父进程和子进程通过变量的返回值不同,执行不同的代码块,代码共享,子进程无自己的代码,但是数据独立,有自己的数据
因此同一个变量,能打印出来两个不同的值
为了识别子进程,一个父进程下可能会有多个子进程,将子进程的pid返回给父进程,父进程可以快速的识别这是哪一个子进程;
子进程返回0,可以知道自己被创建成功了,并且子进程找父进程的成本低,直接系统调用就可以了,因此简单的返回一个0就可以了。
只要 PCB 在运行队列中,那么此进程就叫做运行态,代表已经准备好了,可以随时调度
进程还在,只不过永远不在运行了,随时等待被释放;为什么不是立即被释放呢?因为操作系统可能在进行其他操作,不能理解释放,所以才有了终止态
进程申请 cpu 资源进行执行操作,需要在其队列中排队;那么申请其他资源:网卡、磁盘、音响等,也是需要在对应资源的队列中排队的;
当 cpu 执行到一个进程时,此进程需要调用其他资源,但是资源还没有准备好,那么此进程的 PCB 将会从 cpu 的运行队列转移到需要调用的资源的运行队列中等待,不会执行此进程,不会阻挠 cpu 调用其他进程,此时这个进程就是卡住的状态,叫做进程阻塞。
在 cpu 运行队列中的进程,他们的代码和数据是被加载到了内存中的,当内存不够用的情况下,操作系统会将短期内不会被 cpu 调度的进程的代码和数据置换到磁盘上
以不用调用外设的死循环为例,cpu 一直在调用此进程,那么它就为运行态:
linux下的浅睡眠状态可以被随时唤起、可以被手动终止,因此又称为可中断睡眠
当进程要访问非磁盘的外设时,总是需要等待资源就绪的,Linux下的PCB叫做 task_struct ,task_struct会在资源的运行队列中等待,这就是浅睡眠状态:
我们在运行,为什么是阻塞态?
因为 cpu 处理进程的速度是非常快的,外设的访问速度是跟不上cpu的处理速度的,在调用进程的时候,大部分的时间都是在等待外设资源就绪,因此这里的进程是阻塞状态;
disk sleep--深度睡眠状态,如果进程等待的外设是磁盘资源,那么此进程就是深度睡眠状态
如何理解:
在内存不够用,进程过多,服务器压力过大时,可能会终止掉一些进程;
在一个进程要将大量数据写入磁盘中,在写入过程中,进程会将自己设置成深度睡眠状态,等待磁盘的写入失败或者成功的消息,防止被操作系统终止造成数据丢失。
结论:深度睡眠可以防止被操作系统终止掉;等待磁盘资源就绪,也是阻塞状态
死亡状态:资源可以立马回收
僵尸状态:在Linux下,当进程退出的时候,一般不会直接进入死亡状态,而是进入僵尸状态,僵尸状态存储着这个进程的执行结果,维护着退出信息,通过僵尸状态来告知操作系统此进程是否需要终止。
因为父进程控制着子进程,父进程不回收子进程,子进程会一直保持僵尸状态,没有用的进程还不能被释放占据着空间,这是内存泄漏!!
如果父进程提前退出,子进程还在运行,那么子进程会被一号进程领养,被领养的子进程就叫做孤儿进程:
- #include
- #include
- #include
- #include
- int main()
- {
- pid_t id = fork();
- if(id==0)
- {
- int j = 1;
- while(j++)
- {
- if(j>4)
- {
- printf("我是子进程,我已经孤儿了\n");
- sleep(1);
- }
- else
- {
- printf("我是子进程,我马上孤儿了\n");
- sleep(1);
- }
- }
- }
- else
- {
- int n = 4;
- while(n--)
- {
- printf("我是父进程,我还有%d秒退出了\n",n);
- sleep(1);
- }
- exit(0);
- }
- return 0;
- }
此时有一个问题,我们 Ctrl + C 不能结束掉孤儿进程,因为它变成了后台进程:
必须要用命令将其终止掉:
当一个进程运行时,用命令传递信号令其暂停
kill -19 pid
此时这个孤儿进程就是暂停状态
- //继续进程
- kill -18 pid
暂停状态的一种,在gdb调试中,打上断点,运行至断点处即可: