对操作系统而言,进程是一个指令执行流及其执行环境的统称,这里的执行环境是一个系统资源的集合,所有涉及到的资源在Linux中被抽象成各种数据:进程控制块、虚存空间、文件系统,文件I/O、信号处理函数等。因此,对操作系统来说,创建一个进程的过程就是这些数据对象的创建过程。
Linux内核通常把进程也叫做任务,一般来说,进程具有以下四个特点,且缺一不可:
(1)一段代码块供其执行
(2)专用系统堆栈空间
(3)进程控制块
(4)独立的存储空间
我们通常将缺少第四条的称为线程,线程是进程基础上进一步抽象的产物。线程的创建、管理以及销毁等过程我们将在后续的文章中进行说明。在Linux系统中,进程的创建主要有由操作系统创建,和由父进程创建两种方式。严格来说,Linux系统中的用户进程不能直接被创建出来,只能通过特定的API从某个进程中复制出来,复制进程的API包括三种:fork、vfork、clone。他们作为系统调用,分别调用了sys_fork、sys_vfork、sys_clone,最终他们都调用了do_fork函数,三者的差别在于相应的准备工作和参数的传递不同。
系统调用 | 描述 |
fork | fork创造的子进程是父进程的完整副本,复制了父亲进程的资源(父进程的进程地址空间),包括内存的task_struct内容,父子进程运行的先后顺序是随机的 |
vfork | vfork的特点是创建的子进程与父进程共享数据段,而且由vfork()创建的子进程将先于父进程运行 |
clone | 一般Linux中创建线程一般使用POSIX提供的pthread库,但实际上linux也提供了创建线程的系统调用,也就是clone,此外,clone还具有其他特殊功能 |
fork 创建的子进程复制了父亲进程的资源(copy-on-write技术),包括内存的内容task_struct内容(即父子进程的pid不同)。当父进程使用系统调用fork()创建一个子进程时,子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。
vfork创建的子进程共享父进程的空间(包括虚拟空间与物理空间),同时vfork返回后,直接使用父进程的内存和数据,也就是说若子进程修改或破坏了父进程的数据结构,则会造成返回失败或者引起系统崩溃等,因此在调用vfork时,若发生子进程修改了父进程数据结构的话,则不能调用exit进行返回操作。
clone()可以说是fork的升级版,它不仅可以创建进程或者线程,甚至可以有选择的继承父进程的内存、将创建出来的进程变成父进程的兄弟进程等等。clone的原型为:
int clone(int (fn)(void ), void*child_stack, int flags, void *arg);
fn是函数指针;child_stack是子进程分配系统堆栈空间,flags为子进程将从父进程继承哪些资源,arg则是传给子进程的参数。flag标志常见的选项和含义如下表所示:
标志 | 含义 |
CLONE_PARENT | 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子” |
CLONE_FS | 子进程与父进程共享相同的文件系统,包括root、当前目录、umask |
CLONE_FILES | 子进程与父进程共享相同的文件描述符(file descriptor)表 |
CLONE_NEWNS | 在新的namespace启动子进程,namespace描述了进程的文件hierarchy |
CLONE_SIGHAND | 子进程与父进程共享相同的信号处理(signal handler)表 |
CLONE_PTRACE | 若父进程被trace,子进程也被trace |
CLONE_VFORK | 父进程被挂起,直至子进程释放虚拟内存资源 |
CLONE_VM | 子进程与父进程运行于相同的内存空间 |
CLONE_PID | 子进程在创建时PID与父进程一致 |
CLONE_THREAD | Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群 |
通过上面的分析,我们不难发现,在执行fork时clone_flag= SIGCHLD;在vfork时clone_flag= CLONE_VM | CLONE_VFORK | SIGCHLD;而在clone中,clone_flag一般由用户给出。
以上为学习Linux进程以及进程管理时的一点心得,其中也参考了一些大神的技术博客,在此表示感谢,文章内容若有错误或不足,还请各位大神指正,谢谢!
文章若有侵权行为,请及时联系作者。谢谢!