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()をコールする事で実装されています。





