首先,我们知道,进程是一个东西,是确确实实存在的某个东西,那么,我们写程序怎么表示这些个“东西”的啊,一般就是用结构体了哦,所以,系统中也就有一个结构体表示了“进程”,即: struct task_struct { …… } 利用这个结构体,就可以描述系统中的各个进程了,当然,为了时髦一点,人们一般把这个结构体称之为“进程控制块”,或者用英语说是:PCB。而这个PCB中有哪些东西,我们暂且不管。NND,差距明显啊,人家已经想到了这点,而且设计出了里面应该包括哪些东西——这个结构体中应该包括哪些东西,还是非常头疼的——我们现在还只是考虑到这点,还没法更多地考虑里面应该包括哪些东西!
现在知道了,系统中的某个进程必定是由另外一个进程孵化出来的,而某个进程又可以孵化出其他的进程,从而子子孙孙,无穷尽也——错,系统关机,所有进程全部死了——从而构成下面一幅壮观的景象:
某个进程在运行-〉某个进程觉得需要创建一个子进程-〉调用函数开始创建
我们知道,孩子都具有爸妈的遗传基因,而某个进程创建出来的子进程要不要具有父进程的特征呢?简单点说:假如此时父进程打开了某个文件,在进行读取操作,那么子进程要不要也拥有读取该文件的能力呢?想想,好像需要,也好像不需要,看情况而定。
在Linux中,子进程则是遗传了父进程的基因。而系统提供的创建函数有两个:fork()和clone()。这两个函数的区别在于:fork是把父进程所具有的全部资源全部“复制”给了子进程,而clone则是部分复制,某些其他部分则是传递了指针,这个就好像是c++中的传值和传引用的区别。
//在我们写程序的时候注意,long是可移植的,而int则随着编译器的不同而不同
long do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
//给子进程分配进程号
long pid = alloc_pidmap();
……
//开始完成具体的创建子进程工作
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid);
//这儿,我们在写程序的时候也要注意,如果某个判断的可能性大,
//那么最好放前面,省得多执行语句
if (!IS_ERR(p)) {
……
……
} else {
free_pidmap(pid);
pid = PTR_ERR(p);
}
return pid;
}
这段代码中比较重要的有两个部分,首先是给子进程分配进程号,然后是利用具体的进程号创建子进程。具体怎么创建法的,由于在系统中,进程涉及到内存管理,文件系统等等,所以以后介绍。