进程(task)
一. 进程的基本概念
进程:程序的一个执行实例,正在执行的程序。
从内核上来看,进程担当分配系统资源(CPU时间,内存)的实体。
从广义上定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
二. 进程控制块PCB(task_struct)
进程控制块是用来描述进程的,在它里面存储着这个进程的相关信息,所以是每一个进程都会有一个进程控制块,并且在进程控制块中存储的信息自然也不会相同。
进程信息被放在进程控制块中,进程控制块是一个数据结构,是进程属性的集合。课本上的进程控制块被称为PCB,在Linux操作系统下的PCB叫做:task_struct。task_struct是linux内核中的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
上面说到了PCB是进程所有属性的集合。那么PCB中包含哪些属性呢?
1. 标示符:描述进程的唯一标志信息,用来区分进程,每个进程的标示符都不同,相当与人的身份证号,用来区别人与人。
2. 状态:当前进程的状态,并且保存进程退出的信息等
3. 优先级:运行时的优先级,相对与其他进程。
4. 程序计数器:即EIP,指向程序中下一条即将被执行的指令。在CPU中有一份,在内存中也有一份。
5. 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
6. 上下文数据:进程执行时处理器的寄存器中的数据。
7. I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表
8. 记账信息:包括处理器时间的总和,使用的时钟数总和,时间限制,记账号等。
9. 其他信息
三. 进程的管理
进程的组织:在操作系统中,所有运行的进程都以task_struct链表的形式存在内核中。
查看进程:进程的信息可以通过/proc系统文件查看
若是想查看PID为1的进程信息,就需要查看 /proc/1 这个文件夹
具体如下:
这个是/proc文件中的内容,包含当前系统下的所有进程的信息。可以看到包含了好多的文件夹,在每一个文件夹下就是单个进程的信息了。
这个就是当前系统下一号进程的信息了。
在linux系统下,有一个指令,ps也可以用来查看进程的信息。查询结果如下图所示,列出了当前用户所产生的进程的信息。也可以带参数查询其他进程的信息。
下面这个是top命令的作用,即时显示进程的动态,如果不退出就可以一直查看当前进程的信息,是动态的。
我们来编写一段代码,试着查看一下进程。代码如下:
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
sleep(1);
}
return 0;
}
这段代码很简单,就是让这个进程一直处在睡眠状态中。我们再重新打开一个窗口,查看当前所有进程。如果是直接使用ps指令,查到的就是当前用户的进程,所以这样时候查不到的,需要使用 ps aux指令来查询,但是用这个查询的结果太多不好找到我们想要的,那么我们就可以使用
ps aux | grep a.out | grep -v grep
grep是用来筛选的,grep -v是用来将当前行指令去除,否则查询到的结果中会有当前指令产生的进程。这样我们就可以找到我们刚才写的程序产生的进程了。
除了以上的办法,在linux中我们还有一种办法可以用来得到进程的标示符。就使用系统函数。在系统中,进程的标示符就是它的ID,通常叫PID,其父进程的ID是PPID。
获取当前进程的ID的函数为getpid(),得到父进程ID的函数为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;
}
当前进程的ID为44456,其父进程的ID为36360。
四. 进程的创建
在linux下有一个系统函数 fork(),它是用来创建进程的,并且它有一个特殊的地方,就是它的返回值有两个。在父进程中返回的值为子进程的PID,在子进程中返回0。我们就可以通过这个来区分当前的进程是子进程还是父进程。
fork之后父进程与子进程共享代码,数据不共享,数据存储在不同的位置。代码存储采用了写时拷贝的方法。在需要修改代码时,才将父进程的代码拷贝出来再进行修改。下面来看一段代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int ret = fork();
printf("proc : %d ,ret : %d\n",getpid(),ret);
sleep(1);
return 0;
}
ret用来接收返回值,getpid()用来获取当前进程的pid,父子进程谁先调用并不确定,取决于操作系统调度器。在这里,是父进程先调用。为什么呢?因为父进程的返回值应该是大于0的,而子进程的返回值为0。这样我们就可以写出这样的代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0)
perror("fork!");
else if(pid == 0)
printf("child's pid = %d\n",getpid());
else if(pid >= 0)
printf("parent's pid = %d\n",getpid());
return 0;
}
用返回值来区分父子进程,让其做自己的事情。