O_CLOEXEC
Rev.1を表示中。最新版はこちら。
O_CLOEXECでない場合、fork_childはparentのファイルIDでファイル参照でき、新規オープンしたファイルIDは3となりますが、O_CLOEXECの時、parentのファイルIDでファイル参照できません。(実際はエラーとなります。)そして新規オープンしたファイルIDは2となっています。ファイルIDの0/1/2は標準入力/標準出力/標準エラです。
[root@localhost lkm]# 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 lkm]# 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:56789forkは親プロセスのメモリ空間を共有(メモリの書き込みは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);
}






