O_CLOEXEC


Rev.2を表示中。最新版はこちら

O_CLOEXECでない場合、fork_childはparentのファイルIDでファイル参照でき、新規オープンしたファイルIDは3となりますが、O_CLOEXECの時、parentのファイルIDでファイル参照できません。(実際はエラーとなります。)そして新規オープンしたファイルIDは2となっています。ファイルIDの0/1/2は標準入力/標準出力/標準エラです。
[root@localhost kitamura]# cat parent.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char* argv[])
{
       int fd;
       char    fileID[4];
       char    buff[10];

       if (argc == 1) {
               fd = open("hoge1", O_RDWR);
       }
       else {
               fd = open("hoge1", O_CLOEXEC|O_RDWR);
       }
       sprintf(fileID, "%d", fd);

       printf("--parent--\n");
       memset(buff, 0, 10);
       read(fd, buff, 5);
       printf("%d:%s\n", fd, buff);

       if (!fork()) {
               execlp("./fork_child", "./fork_child", fileID,  NULL);
       }
       sleep(1);
       printf("--parent--\n");
       memset(buff, 0, 10);
       read(fd, buff, 5);
       printf("%d:%s\n", fd, buff);
       close(fd);
}

[root@localhost kitamura]# cat fork_child.c
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

void main(int argc, char *argv[])
{
       char    buff[10];

       printf("---child---\n");
       memset(buff, 0, sizeof(buff));
       read(atoi(argv[1]), buff, 5);
       printf("%d:%s\n", atoi(argv[1]), buff);

       int fd = open("hoge2", O_RDWR);
       printf("new fileID:%d\n", fd);
}

実行結果

[root@localhost kitamura]# echo 0123456789abcde > hoge1

[root@localhost kitamura]# ./parent
--parent--
3:01234
---child---
3:56789
new fileID:4
--parent--
3:abcde

[root@localhost kitamura]# ./parent O_CLOEXEC
--parent--
3:01234
---child---
3:
new fileID:3
--parent--
3:56789
forkは親プロセスのメモリ空間を共有(メモリの書き込みはCOWにて動的に割り当てられます。)し、プロセスstructの属性も、copy_process()で親プロセスの属性を共有します。ファイルIDについては、 copy_files()がコールされます。

親プロセスのcurrent->filesからdup_fd()で共有したstruct files_struct newfを取得し、それを新規プロセスのtsk->filesといたします。
static int copy_files(unsigned long clone_flags, struct task_struct *tsk)
{
       struct files_struct *oldf, *newf;
       int error = 0;

       oldf = current->files;
       if (!oldf)
               goto out;

       if (clone_flags & CLONE_FILES) {
               atomic_inc(&oldf->count);
               goto out;
       }

       newf = dup_fd(oldf, &error);
       if (!newf)
               goto out;

       tsk->files = newf;
       error = 0;
out:
       return error;
}
execは実行するfileのセグメントから、binfmtを取得し、struct linux_binfmtの掛かるローダの.load_binaryコールバックでロードされます。elfはload_elf_binary()がコールされ、そこでsetup_new_exec()/flush_old_files()をコールし、fdt->close_on_exec->fds_bits[]のセットしているビットに応じて、そのファイルをcloseします。O_CLOEXECでオープンされたファイルはそのビットがセットされます。従ってfds_bits[]がセットされていないO_CLOEXECでないファイルIDは、オープン状態となります。
static struct linux_binfmt elf_format = {
       .module         = THIS_MODULE,
       .load_binary    = load_elf_binary,
       .load_shlib     = load_elf_library,
       .core_dump      = elf_core_dump,
       .min_coredump   = ELF_EXEC_PAGESIZE,
};

void setup_new_exec(struct linux_binprm * bprm)
{
       arch_pick_mmap_layout(current->mm);

       current->sas_ss_sp = current->sas_ss_size = 0;

       if (current_euid() == current_uid() && current_egid() == current_gid())
               set_dumpable(current->mm, 1);
       else
               set_dumpable(current->mm, suid_dumpable);

       set_task_comm(current, bprm->tcomm);

       current->mm->task_size = TASK_SIZE;

       if (bprm->cred->uid != current_euid() ||
           bprm->cred->gid != current_egid()) {
               current->pdeath_signal = 0;
       } else {
               would_dump(bprm, bprm->file);
               if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)
                       set_dumpable(current->mm, suid_dumpable);
       }

       if (!get_dumpable(current->mm))
               perf_event_exit_task(current);

       current->self_exec_id++;
                       
       flush_signal_handlers(current, 0);
       flush_old_files(current->files);
}

static void flush_old_files(struct files_struct * files)
{
       long j = -1;
       struct fdtable *fdt;

       spin_lock(&files->file_lock);
       for (;;) {
               unsigned long set, i;

               j++;
               i = j * __NFDBITS;
               fdt = files_fdtable(files);
               if (i >= fdt->max_fds)
                       break;
               set = fdt->close_on_exec->fds_bits[j];
               if (!set)
                       continue;
               fdt->close_on_exec->fds_bits[j] = 0;
               spin_unlock(&files->file_lock);
               for ( ; set ; i++,set >>= 1) {
                       if (set & 1) {
                               sys_close(i);
                       }
               }
               spin_lock(&files->file_lock);

       }
       spin_unlock(&files->file_lock);
}


最終更新 2015/01/10 19:53:12 - north
(2015/01/10 19:39:14 作成)


検索

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