random...@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
-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to