在学习进程之前我们需要理解和区分两个概念:程序和进程。
程序是完成特定任务的一系列指令集合。
举个栗子,下面这段代码就是一个程序
#include
#include
int main()
{
while(1)
{
printf("hello world\n");
sleep(1);
}
return 0;
}
进程是资源分配的最小单位,每一个进程都有自己独立的地址空间与执行状态。像Linux这样的多任务操作系统能够让许多程序同时运行,每一个运行着的程序就构成了一个进程。
我们另开一个窗口,使用ps命令就可以查看刚才运行的程序以及它所构成的进程
进程的静态描述由三部分组成:PCB、有关程序段和该程序段对其进行操作的数据结构集。
task_ struct内容分类
进程最知名的属性就是进程号(processID,PID)和它父进程号(parent processID,PPID)。
通过调用getpid()
获取进程id,调用getppid()
获取父进程id,举个栗子:
#include
#include
int main()
{
printf("子进程pid:%d\n",getpid());
printf("父进程ppid:%d\n",getppid());
return 0;
}
运行结果:
我们可以使用ps,top等命令查看系统进程,也可以通过下述操作查看
PID常见的用法之一就是创建唯一的文件或目录名。
另一种的用途是把PID写入日志文件做为日志消息的一部分。
对于Linux系统,我们运行man fork
命令来认识fork
掌握了函数的用法后,我们通过实例来掌握并理解fork函数和进程创建
#include
#include
int main()
{
printf("未调用fork打印一遍\n");
fork();
printf("调用fork打印两遍,父进程ID:%d , 进程ID:%d \n",getppid(), getpid());
sleep(1);
return 0;
}
通过上述代码我们调用fork函数创建进程,可以发现fork()语句之后printf语句被执行了两次
我们可以更加形象的理解进程间的关系:
bash进程(id:23920)——爷爷
第一个输出进程(id:31987),与bash进程为父子关系——儿子
第二个输出进程(id:31988),与第一个输出进程为父子关系——孙子
博客1.2节中介绍了进程的数据结构由PCB+代码段+数据段,而Linux中的PCB是由task_struct代替的,创建一个进程在Linux中,我们目前可以认为需要三部分构成
fork系统调用之后,父子进程将交替执行。
首先问题的本质是:两次返回,是在各自的进程空间中返回的。
在Q1中我们了解到子进程和父进程各有自己的内存空间 (fork函数:代码段、数据段、堆栈段、PCB进程控制块的复制)。父进程调用fork函数,子进程也会调用,且返回值都会存入到自己的数据段中,我们通过一个程序来深刻理解两次返回。
#include
#include
int main()
{
int ret = fork();
if(ret < 0)
{
printf("进程创建失败!\n");
}
else if(0 == ret)
{
printf("我是儿子(id:%d),fork返回值:%d\n",getpid(),ret);
}
else
{
printf("我是父亲(id:%d),fork返回值:%d\n", getpid(),ret);
}
sleep(1);
return 0;
}