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(&current->mm->mm_users) > 1)
                       return -EINVAL;
       }

       return 0;
}
forkはflag=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDでclone()をコールする事で実装されています。


最終更新 2016/12/06 13:05:55 - north
(2016/12/06 12:51:33 作成)


検索

アクセス数
3575562
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。