struct task_struct
来实现
进程控制块是进程存在的唯一标志,pid是一个整型,它属于是进程的身份证号码,由于pid是由整型表示的,所以pid的范围也就是一个整型的大小范围
首先会将原来的进程(父进程)PCB复制一块出来,并且给他申请一个pid,来唯一标识子进程,接下来将整个进程的实体复制一份给子进程
复制出来的子进程与父进程是相同的,唯一不同的是fork()
的返回值不同,父进程fork()
返回子进程的pid,子进程fork()
返回0
#include
#include
#include
#include
#include
int main()
{
char *s = NULL;
int n = 0;
pid_t id = fork();//pid_t 相当于 int
assert(id != -1);
if(id == 0)
{
s = "child";
n = 3;
}
else
{
s = "parent";
n = 7;
}
int i = 0;
for(;i<n;i++)
{
printf("s = %s pid = %d\n",s,getpid());
sleep(1);
}
exit(0);
}
fork()
执行之后,子进程会从fork()
执行到的地方开始执行,而不是从新执行一遍程序
在物理内存的管理中,我们会将物理内存划分为一个一个的页面,一个页面有4k或者8k大小
而这个是一个进程的逻辑页面,也同样是4k或者8k一个页面大小,即使页面使用只有一部分但是还会分给他一个完整的页面
我们通过页表来描述,逻辑页与物理页之间的联系,根据上表进程复制,会把4号页面的内容复制给7号一份,10号页面的内容复制给14号一份以此类推,但是假如我们2号3号页面我们不去进行修改,我们将其复制,子进程物理页面上与父进程物理页面上存放的内容是一模一样的,这样不光复制开销大,也占用了内存空间
继而我们引入了写时拷贝技术,假如我们的0号页面与1号页面会发生修改,我们就将这两个页面进行复制,而后面的2号3号页面不会进行修改,那么我们父子进程共享这两个页面
我们的32位系统,4g内存的使用情况,上面的1g是有内核占用的
进程中使用的是逻辑地址,即两个程序会使用相同的地址(逻辑),而会映射到不同的物理地址上
我们修改这一块的代码
printf("s = %s pid = %d %x\n",s,getpid(),&n);
来打印 n 的地址,运行程序
我们发现父进程与子进程打印的n地址都是一样,我们知道父子进程的n值不一样,为什么地址会相同呢,这就是进程使用逻辑地址造成的,他们虽然逻辑地址相同,但是在物理上映射的地址是不同的
僵死进程的产生:
我们的程序退出时,无论是return 0
还是exit(0)
都会有一个退出码,退出码会存放在进程的PCB中,希望父进程能够获得这个退出码,来观察子进程的退出状态
子进程结束,进程实体会消失,但是PCB会暂时保留,等待父进程获取PCB的退出码,否则会一直存在,所以当父进程先于子进程结束,PCB等待不到父进程的获取,继而会进入僵死状态
if(id == 0)
{
s = "child";
n = 3;
}
else
{
s = "parent";
n = 7;
}
将这一段修改,让子进程先与父进程结束
子进程结束,父进程没有获得子进程的退出码,变成僵死进程
wait()
方法获取子进程的退出码僵死进程的影响:当不断有僵死进程产生,会一直消耗PCB编号,以及占用一定的内存空间,会对系统软硬件资源进行损耗
父进程先于子进程结束,子进程会变成孤儿进程,孤儿进程会被init
进程进行收养,获取它的退出码
if(id == 0)
{
s = "child";
n = 7;
}
else
{
s = "parent";
n = 3;
}
int i = 0;
for(;i<n;i++)
{
printf("s = %s pid = %d ppid = %d\n",s,getpid(),getppid());
sleep(1);
}
我们这次这样修改代码,让父进程先结束,子进程后结束并显示他们的父进程pid
子进程被init
收养
另外一种方法就是使用wait()
方法来调用等待获取子进程退出码
wait()
会将父进程阻塞住,等待子进程结束获取到退出码,进程才会继续执行
#include
#include
#include
#include
#include
int main()
{
char *s = NULL;
int n = 0;
pid_t id = fork();
assert(id != -1);
if(id == 0)
{
s = "child";
n = 3;
}
else
{
s = "parent";
n = 7;
int val = 0;
wait(&val);//接收子进程的退出码
if(WIFEXITED(val))//是否正常结束 获取退出码
{
printf("val = %d\n",WEXITSTATUS(val));
}
}
int i = 0;
for(;i<n;i++)
{
printf("s = %s pid = %d ppid = %d\n",s,getpid(),getppid());
sleep(1);
}
exit(3);
}