进程
进程指正在执行的程序。从内核来说,它是担当分配系统资源(CPU时间,内存)的实体。
那进程跟程序有什么区别呢?从数据和代码层面看,它们是一样的,但是进程比程序多了一个东西,那就是描述进程的PCB结构体。
那这个PCB到底有什么作用呢?
PCB
相信学过操作系统的同学都知道,操作系统作为软件和硬件的管理者,那操作系统要管理那么多的进程,而每个进程都有很多的信息,为了方便管理这些进程信息,它们被放在一个叫做进程控制块的数据结构中,我们称之为PCB。
在Linux中描述进程的结构体叫做task_struct。它是Linux内核的一种数据结构,会被装载到内存里。
task_ struct内容分类
标识符(PID) :跟这个进程相关的唯一标识符,用来区别其他进程
状态 :如果进程正在执行,那么进程处于执行状态。
优先级 :相对于其他进程的优先级。
程序计数器 :程序中即将被执行的下一条指令的地址。
内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
上下文数据:进程执行时处理器的寄存器中的数据。
I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备(如磁带驱动器)和被进程使用的文件列表。
审计信息:可包括处理器时间总和,使用的时钟数总和,时间限制,审计号等。
标识符(PID)
在Linux下可以使用top命令(相当于windows系统下的任务管理器)可以查看
也可以使用ps aux命令,可以查看所有进程,也可以使用ps aux | grep +特定要查找的进程 。(可以使用ps aux | head -1 && ps aux | grep XXX可在第一行显示对应的属性)
当然也有可以查看PID的函数getpid(),查看父进程的函数getppid()
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid: %d\n",getpid());
printf("ppid: %d\n",getppid());
return 0;
}
getpid返回当前进程标识,getppid返回父进程标识。可以通过ps aux | grep + PID,可以看到所显示的父进程PID为bash
可以通过echo $$ 查看脚本运行的当前进程的PID
使用ls /proc 命令可以查看动态目录,存放的是一些进程的信息。
通过系统调用创建进程(fork)
如何创建一个进程呢?我们可以使用fork()函数来创建
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
fork();
printf("process : %d , parent: %d\n", getpid(), getppid());
sleep(1);
return 0;
}
可以看到fork之后有两个进程,为父子关系。
而fork的有两个返回值,给父进程返回子进程的PID,给子进程返回0值。 父进程后面需要通过子进程的PID管理子进程的状态。
进程状态
可以通过ps aux | ps axj 命令查看
R(running) 运行状态:表示进程要么在运行中,要么在运行队列里。
S(sleeping) 睡眠状态:可中断睡眠状态,表示进程正在等待事件完成。
D(disk sleep) 磁盘休眠状态:不可中断睡眠状态,在这个状态的进程通常会等待IO的结束。
T(stopped) 停止状态:可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
X(dead) 死亡状态:这个状态为返回状态。
Z(zombie) 僵尸状态:当进程退出并且父进程使用wait()系统调用没有读取到子进程退出的返回代码时就会产生僵死(尸)进程,僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。(类似于警察检查死者,要知道死者的原因和状态)
注:若系统中有大量僵尸状态的进程,长时间不回收,会造成内存资源的浪费,造成==内存泄漏问题==
在下面简单的实现一个僵死进程
#include <iostream>
#include <stdlib.h>
#include <sys/types.h>
include <unistd.h>
using namespace std;
int main()
{
pid_t id = fork();
int count = 0;
if(id == -1){
cerr << "fork error " << endl;
}
if(id == 0){//chlid
while(1){
cout << "I am chlid ..." << endl;
sleep(1);
if(count++ >= 3)
exit(1); //退出进程
}
else
{
while(1){//father
cout << "I am father " << endl;
sleep(1);
}
}
return 0;
}
另起一个终端,跑一个脚本 while :; do ps aux | grep ‘myprocess’| grep -v grep ;sleep 1;echo ‘###################’;done
可以从图片上看出三秒之后,子进程变成僵死状态。
那父进程如果提前退出,子进程后退出会怎样呢?这时候子进程就称为孤儿进程了,应该由1号(操作系统)init进程领养,之后也由init进程回收。
int main()
{
pid_t id = fork();
int count = 0;
if(id == -1){
cerr << "fork error " << endl;
}
if(id == 0){//chlid
while(1){
cout << "I am chlid ..." << endl;
sleep(1);
}
else
{
while(1){//father
cout << "I am father " << endl;
sleep(1);
if(count++ >= 3)
exit(1); //退出进程
}
}
return 0;
}
跑脚本:while :; do ps axj | grep ‘myprocess’| grep -v grep ;sleep 1;echo ‘###################’;done
注:要看到父进程,需要将aux变成axj
可以看到当父进程退出之后,子进程被1号进程领养。
进程优先级
进程的优先权:CPU资源分配的先后顺序。一般来说,数字越小优先级越高。
可以使用ps -l 命令查看优先级
nice值为Linux下优先级的修正数据,nice其取值范围是-20至19。
PRI = (旧的)PRI + nice
可使用top命令进行修改(一般不建议修改)
进入top之后按“r” —> 输入进程ID —>输入nice值
总结来说,如何管理进程就是先描述再组织,先把进程描述起来(就是这些表征信息,都放在task_struct里面),然后进行组织。