CLONE_FS标志对子进程文件系统的影响
争论
这两天在研究沙盒的时候,跟同事争论了一下如下场景: 使用clone加上CLONE_FS创建子进程,父进程使用chroot,是否会同时对子进程的文件系统产生影响. 根据man手册对于CLONE_FS的描述,使用CLONE_FS创建子进程,子进程或者父进程任意一个调用chroot, chdir或者umask, 都会对另一进程产生影响. 具体怎么影响需要看一下内核的源码.
系统调用在内核中的实现
Linux的系统调用在定义在include/linux/syscalls.h中,例如clone系统调用,在syscalls.h中的定义为
1 |
|
内核中系统调用的实现,一般会使用SYSCALL_DEFINEn宏进行封装,其中的n为系统调用的参数个数;使用该宏时,第一个参数为系统调用的名字,仍以clone为例,对应的SYSCALL_DEFINEn宏的实现为:
1 |
|
CLONE_FS标志在clone()中的使用
clone()系统调用的实现在kernel/fork.c中,该文件中还实现了fork()和vfork(),从该文件中可以确定,fork、vfork和clone在内核中调用的都是_do_fork()函数,只是传递的参数不同。
函数copy_fs()对CLONE_FS标志进行了检查:
1 | static int copy_fs(unsigned long clone_flags, struct task_struct *tsk) |
可以看到如果设置了CLONE_FS标志,则将当前进程的fs->users加1,否则调用copy_fs_struct(),并将子进程的fs结构指向其返回值:
1 | struct fs_struct *copy_fs_struct(struct fs_struct *old) |
该函数实现非常简单,就是创建新的fs_struct结构体,并把当前进程fs结构中的值,然后返回新的fs结构体。
通过这两个函数,就可以得出结论:
通过
clone创建子进程,未指定CLONE_FS时,子进程拥有自己的fs结构,其初始值与父进程的fs结构相同;双方通过chroot、chdir修改fs结构时,均不影响另一个进程。指定
CLONE_FS时,父子进程结构体中fs字段指向同一块内存区域,所以任何一方使用chroot、chdir等修改fs结构时,也会对应一个进程生效。
进程的fork
进程的fork,关键在于进程结构体的复制,而这个过程是在dup_task_struct()中实现的,从fork、vfork、clone开始,调用顺序为:clone/fork/vfork -> _do_fork -> copy_process -> dup_task_struct
该函数的实现简单来说分为如下步骤:
- 为新的
task_struct结构体分配内存 - 分配栈空间
- 将当前进程的
task_struct结构体各个字段赋值给子进程的task_struct(使用*dst = *src) - 将新的栈空间赋值给子进程的
task_struct->stack。
为什么子进程的task_struct结构体被分配了新的内存,却还能在设置了CLONE_FS后与父进程共享task_struct->fs?
因为task_struct中,fs的数据类型是struct fs_struct *,所以两个fs实际上指向同一块内存区域。