fork
linux可以通过fork()创建一个新的进程。
例如通过shell命令运行ELF的时候,shell程序就会调用fork()创建一个子进程。
pid_t fork(void);
- fork函数进入到内核中会先创建新进程对应的内核数据结构(PCB进程控制块什么的)
- 给新进程分配对应的地址空间
- 将父进程的所有段的数据和栈堆等数据都map到进程的地址空间中。
- fork()返回:fork会返回两次,在父进程中返回时返回值为子进程的PID, 在子进程中返回0
//打印三个A和三个B
int main()
{
int i;
for(i=0;i<2;i++)
{
if(fork()==0)
{
printf("A\n");
}
else
{
printf("B\n");
}
}
}
fork的写时复制(copy-on-write)
因为fork是把当前进程复制一份作为子进程,而子进程内存空间与父进程内存空间映射的物理内存是同一块。
只有当子进程向内存空间中写入数据时,此内存块对应的物理内存会copy一份然后重新映射会对应的内存地址空间中。这样做的好处是可以节省系统开销,子进程如果只是访问数据时不会分配新的物理内存的,只有当需要写入数据时才会。
fork后调用exec
一般创建子进程,fork从内核后返回到子进程中会接着调用exec,exec会将子进程的所有内存空间覆盖掉,包括.text,.data,.bss段和所有的堆栈等内存。然后就可以映射新进程对应的ELF到内存空间中。
clone
clone实际是linux用来创建新线程的,但是其用法很多,不仅可以创建线程还可以创建子进程,甚至可以创建兄弟进程。
- fn是对应函数的指针,即对应新线程的入口函数。
- child_stack为线程使用的堆栈指针
- arg为传递给子线程的参数
- flags为各种标志,可以利用此标志创建子进程
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
clone创建进程
clone允许新进程继承父进程的哪些资源(共享),如果全部共享就意味着相当于一个线程。
- 如果flags指定CLONE_NEWNS,则对应创建爱的是一个新进程,新进程拥有自己的内存空间
- 如果flags指定CLONE_VM | CLONE_PID就表示创建的是一个线程,其与进程共享所有的内存空间,没有自己的内存空间。