Path: csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!feeder.erje.net!1.eu.feeder.erje.net!eternal-september.org!feeder.eternal-september.org!mx02.eternal-september.org!.POSTED!not-for-mail From: Marko Rauhamaa Newsgroups: comp.lang.python Subject: Re: fork/exec & close file descriptors Date: Thu, 04 Jun 2015 00:09:15 +0300 Organization: A noiseless patient Spider Lines: 91 Message-ID: <87vbf433es.fsf@elektro.pacujo.net> References: <87pp5eksnc.fsf@universite-de-strasbourg.fr.invalid> <87eglt7u5i.fsf@elektro.pacujo.net> <87lhg1lt00.fsf@universite-de-strasbourg.fr.invalid> <87a8wh7nq6.fsf@elektro.pacujo.net> <87d21dl0jc.fsf@universite-de-strasbourg.fr.invalid> <877frl6xxj.fsf@elektro.pacujo.net> <556eee15$0$12975$c3e8da3$5496439d@news.astraweb.com> <878uc1567j.fsf@elektro.pacujo.net> <87wpzl3pnn.fsf@elektro.pacujo.net> <87oakw532a.fsf@elektro.pacujo.net> <87iob44zu9.fsf@elektro.pacujo.net> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: mx02.eternal-september.org; posting-host="b7cb1518d23ec19d482dcc9c31d30fdd"; logging-data="9296"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX190eWE7q31DM2zI0bgQ4IGh" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux) Cancel-Lock: sha1:VRgOj5NB1aqJ/xhzZpnaCDqiir0= sha1:hh2f9S2qh2KvaAGswUU5qsPN09o= Xref: csiph.com comp.lang.python:91997 random832@fastmail.us: > On Wed, Jun 3, 2015, at 10:43, Marko Rauhamaa wrote: >> However, the child process needs to be prepared for os.close() to >> block indefinitely because of an NFS problem or because SO_LINGER has >> been specified by the parent, for example. Setting the close-on-exec >> flag doesn't help there. > > Out of curiosity, does exec block in this situation? I didn't try it, but it is apparent in the source code: ======================================================================== void do_close_on_exec(struct files_struct *files) { unsigned i; struct fdtable *fdt; /* exec unshares first */ spin_lock(&files->file_lock); for (i = 0; ; i++) { unsigned long set; unsigned fd = i * BITS_PER_LONG; fdt = files_fdtable(files); if (fd >= fdt->max_fds) break; set = fdt->close_on_exec[i]; if (!set) continue; fdt->close_on_exec[i] = 0; for ( ; set ; fd++, set >>= 1) { struct file *file; if (!(set & 1)) continue; file = fdt->fd[fd]; if (!file) continue; rcu_assign_pointer(fdt->fd[fd], NULL); __put_unused_fd(files, fd); spin_unlock(&files->file_lock); filp_close(file, files); cond_resched(); spin_lock(&files->file_lock); } } spin_unlock(&files->file_lock); } int filp_close(struct file *filp, fl_owner_t id) { int retval = 0; if (!file_count(filp)) { printk(KERN_ERR "VFS: Close: file count is 0\n"); return 0; } if (filp->f_op->flush) retval = filp->f_op->flush(filp, id); if (likely(!(filp->f_mode & FMODE_PATH))) { dnotify_flush(filp, id); locks_remove_posix(filp, id); } fput(filp); return retval; } ======================================================================== Now, the kernel NFS code specifies a flush() method, which can block and even fail. Sockets don't have a flush() method. So closing a socket cannot fail. However, fput(), which decrements the reference count, may block if lingering has been specified for the socket. So I wasn't all that wrong earlier after all: whoever closes a socket last will linger. Thus, the parent (who wanted to linger) might zip through closing a socket while the unwitting child process will suffer the lingering delay before it gets to exec. However, there's this comment in inet_release(): /* [...] * If the close is due to the process exiting, we never * linger.. */ Marko