unshareシステムコール
CLONE_FILESのclone()による子プロセス作成は、親プロセスのcurrent->filesを複写でなく共有する事で実装され、従って、子プロセス作成後に、プロセスで作成したファイルも、一方のプロセスで参照可能です。CLONE_FILESのunshareシステムコールは新規にファイル領域を取得し、それに共有しているファイルを複写してのファイルとなり、unshare前のファイルは共有されますが、unshare後のファイルは共有されません。
[root@localhost c]# cat parent.c #define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <string.h> static int child(void *arg) { char buff[6]; int ret; sleep(1); memset(buff, 0, 6); ret = read(254, buff, 5); printf("child1:%2d:%s\n", ret, buff); memset(buff, 0, 6); ret = read(253, buff, 5); printf("child2:%2d:%s\n", ret, buff); } void main(int argc, char *argv[]) { char stack[64]; int pfd1[2], pfd2[2]; pipe2(pfd1, 0); dup3(pfd1[0], 254, 0); write(pfd1[1], "01234", 5); clone(child, stack + 64, CLONE_FILES, NULL); if (!strcmp(argv[1], "unshare")) { unshare(CLONE_FILES); } pipe2(pfd2, 0); dup3(pfd2[0], 253, 0); write(pfd2[1], "abcde", 5); sleep(1); }
[root@localhost c]# ./parent.out no_unshare child1: 5:01234 child2: 5:abcde [root@localhost c]# ./parent.out unshare child1: 5:01234 <- unshare前のファイルは継承されます。 child2:-1: <- unshare後のファイルは継承されません。CLONE_FILESならunshare_fd()で新たにfiles structureを所得し、そこに共有参照しているcurrent->filesを複写し、put_files_struct()で複写後共有参照してたcurrent->filesで参照カウンタ=1の自身のプロセスしか参照していないファイルは削除されます。
SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) { struct fs_struct *fs, *new_fs = NULL; struct files_struct *fd, *new_fd = NULL; struct nsproxy *new_nsproxy = NULL; int do_sysvsem = 0; int err; err = check_unshare_flags(unshare_flags); if (err) goto bad_unshare_out; if (unshare_flags & CLONE_NEWNS) unshare_flags |= CLONE_FS; if (unshare_flags & (CLONE_NEWIPC|CLONE_SYSVSEM)) do_sysvsem = 1; err = unshare_fs(unshare_flags, &new_fs); if (err) goto bad_unshare_out; err = unshare_fd(unshare_flags, &new_fd); if (err) goto bad_unshare_cleanup_fs; err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, new_fs); if (err) goto bad_unshare_cleanup_fd; if (new_fs || new_fd || do_sysvsem || new_nsproxy) { if (do_sysvsem) { exit_sem(current); } if (new_nsproxy) { switch_task_namespaces(current, new_nsproxy); new_nsproxy = NULL; } task_lock(current); if (new_fs) { fs = current->fs; spin_lock(&fs->lock); current->fs = new_fs; if (--fs->users) new_fs = NULL; else new_fs = fs; spin_unlock(&fs->lock); } if (new_fd) { fd = current->files; current->files = new_fd; new_fd = fd; } task_unlock(current); } if (new_nsproxy) put_nsproxy(new_nsproxy); bad_unshare_cleanup_fd: if (new_fd) put_files_struct(new_fd); bad_unshare_cleanup_fs: if (new_fs) free_fs_struct(new_fs); bad_unshare_out: return err; } static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp) { struct files_struct *fd = current->files; int error = 0; if ((unshare_flags & CLONE_FILES) && (fd && atomic_read(&fd->count) > 1)) { *new_fdp = dup_fd(fd, &error); if (!*new_fdp) return error; } return 0; } void put_files_struct(struct files_struct *files) { struct fdtable *fdt; if (atomic_dec_and_test(&files->count)) { close_files(files); rcu_read_lock(); fdt = files_fdtable(files); if (fdt != &files->fdtab) kmem_cache_free(files_cachep, files); free_fdtable(fdt); rcu_read_unlock(); } }
追記
unshareの実装されているオブジェクトです。static int check_unshare_flags(unsigned long unshare_flags) { if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND| CLONE_VM|CLONE_FILES|CLONE_SYSVSEM| CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET)) return -EINVAL; if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) { if (atomic_read(¤t->mm->mm_users) > 1) return -EINVAL; } return 0; }forkはflag=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDでclone()をコールする事で実装されています。