进程将会由父进程 fork 而来, 从而避免创建所有子系统 ( 内存, 文件等) 的开销.
当父进程调用 fork 创建进程的时候,子进程将各个子系统为父进程创建的数据结构也全部拷贝了一份,甚至连程序代码也是拷贝过来的。按理说,如果不进行特殊的处理,父进程和子进程都按相同的程序代码进行下去,这样就没有意义了。
所以,我们往往会这样处理:对于 fork 系统调用的返回值,如果当前进程是子进程,就返回 0;如果当前进程是父进程,就返回子进程的进程号。这样首先在返回值这里就有了一个区分,然后通过 if-else 语句判断,如果是父进程,还接着做原来应该做的事情;如果是子进程,需要请求另一个系统调用execve来执行另一个程序,这个时候,子进程和父进程就彻底分道扬镳了,也即产生了一个分支(fork)了。
所以可知, 在操作系统启动的时候, 会有一个所有用户进程的 “祖宗进程”.
父进程可以调用它,将子进程的进程号作为参数传给它,这样父进程就知道子进程运行完了没有,成功与否。
每个进程都有独立的进程内存空间,互相之间不干扰.
对于进程的内存空间来讲,放程序代码的这部分,我们称为代码段(Code Segment)。
放进程运行中产生数据的这部分,我们称为数据段(Data Segment)。其中局部变量的部分,在当前函数执行的时候起作用,当进入另一个函数时,这个变量就释放了;也有动态分配的,会较长时间保存,指明才销毁的,这部分称为堆(Heap)。
只有进程要去使用部分内存的时候,才会使用内存管理的系统调用来登记,说自己马上就要用了,希望分配一部分内存给它,但是这还不代表真的就对应到了物理内存。只有真的写入数据的时候,发现没有对应物理内存,才会触发一个中断,现分配物理内存。
当分配的内存数量比较小的时候,使用 brk
,会和原来的堆的数据连在一起。当分配的内存数量比较大的时候,使用 mmap,会重新划分一块区域。
对于文件的操作,下面这六个系统调用是最重要的: