From: Collin Funk Date: Thu, 23 Oct 2025 08:11:50 +0000 (-0700) Subject: split: prefer posix_spawn to fork and execl X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ca8b92866579fe87aeb6d4e93f3bfc07457e2367;p=thirdparty%2Fcoreutils.git split: prefer posix_spawn to fork and execl * NEWS: Mention the change. * bootstrap.conf (gnulib_modules): Add posix_spawn, posix_spawnattr_setsigdefault, posix_spawn_file_actions_addclose, posix_spawn_file_actions_adddup2, and posix_spawn_file_actions_init. * src/split.c: Include spawn.h. (create): Use posix_spawn instead of fork and execl. --- diff --git a/NEWS b/NEWS index a5471f3756..ff684fb648 100644 --- a/NEWS +++ b/NEWS @@ -54,6 +54,9 @@ GNU coreutils NEWS -*- outline -*- - supports a multi-byte --delimiter character - no longer processes input indefinitely in the presence of write errors + 'split' now uses posix_spawn() to invoke the shell command specified by + --filter more efficiently. + wc -l now operates 10% faster on hosts that support AVX512 instructions. diff --git a/bootstrap.conf b/bootstrap.conf index 523c4923ac..fdab0725f1 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -213,9 +213,14 @@ gnulib_modules=" pipe-posix pipe2 posix-shell + posix_spawn posix_spawnattr_destroy posix_spawnattr_init posix_spawnattr_setflags + posix_spawnattr_setsigdefault + posix_spawn_file_actions_addclose + posix_spawn_file_actions_adddup2 + posix_spawn_file_actions_init posix_spawnp posixtm posixver diff --git a/src/split.c b/src/split.c index e58e0db54e..077e30f0d7 100644 --- a/src/split.c +++ b/src/split.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "system.h" #include "alignalloc.h" @@ -497,50 +498,69 @@ create (char const *name) } else { - int fd_pair[2]; - pid_t child_pid; - char const *shell_prog = getenv ("SHELL"); - if (shell_prog == nullptr) - shell_prog = "/bin/sh"; if (setenv ("FILE", name, 1) != 0) error (EXIT_FAILURE, errno, _("failed to set FILE environment variable")); if (verbose) fprintf (stdout, _("executing with FILE=%s\n"), quotef (name)); + + int result; + int fd_pair[2]; + pid_t child_pid; + + posix_spawnattr_t attr; + posix_spawn_file_actions_t actions; + + sigset_t set; + sigemptyset (&set); + if (default_SIGPIPE) + sigaddset (&set, SIGPIPE); + + if ( (result = posix_spawnattr_init (&attr)) + || (result = posix_spawnattr_setflags (&attr, + (POSIX_SPAWN_USEVFORK + | POSIX_SPAWN_SETSIGDEF))) + || (result = posix_spawnattr_setsigdefault (&attr, &set)) + || (result = posix_spawn_file_actions_init (&actions)) + ) + error (EXIT_FAILURE, result, _("posix_spawn initialization failed")); + if (pipe (fd_pair) != 0) error (EXIT_FAILURE, errno, _("failed to create pipe")); - child_pid = fork (); - if (child_pid == 0) - { - /* This is the child process. If an error occurs here, the - parent will eventually learn about it after doing a wait, - at which time it will emit its own error message. */ - int j; - /* We have to close any pipes that were opened during an - earlier call, otherwise this process will be holding a - write-pipe that will prevent the earlier process from - reading an EOF on the corresponding read-pipe. */ - for (j = 0; j < n_open_pipes; ++j) - if (close (open_pipes[j]) != 0) - error (EXIT_FAILURE, errno, _("closing prior pipe")); - if (close (fd_pair[1])) - error (EXIT_FAILURE, errno, _("closing output pipe")); - if (fd_pair[0] != STDIN_FILENO) - { - if (dup2 (fd_pair[0], STDIN_FILENO) != STDIN_FILENO) - error (EXIT_FAILURE, errno, _("moving input pipe")); - if (close (fd_pair[0]) != 0) - error (EXIT_FAILURE, errno, _("closing input pipe")); - } - if (default_SIGPIPE) - signal (SIGPIPE, SIG_DFL); - execl (shell_prog, last_component (shell_prog), "-c", - filter_command, (char *) nullptr); - error (EXIT_FAILURE, errno, _("failed to run command: \"%s -c %s\""), - shell_prog, filter_command); - } - if (child_pid < 0) - error (EXIT_FAILURE, errno, _("fork system call failed")); + + /* We have to close any pipes that were opened during an + earlier call, otherwise this process will be holding a + write-pipe that will prevent the earlier process from + reading an EOF on the corresponding read-pipe. */ + for (int i = 0; i < n_open_pipes; ++i) + if ((result = posix_spawn_file_actions_addclose (&actions, + open_pipes[i]))) + break; + + if ( result + || (result = posix_spawn_file_actions_addclose (&actions, fd_pair[1])) + || (fd_pair[0] != STDIN_FILENO + && ( (result = posix_spawn_file_actions_adddup2 (&actions, + fd_pair[0], + STDIN_FILENO)) + || (result = posix_spawn_file_actions_addclose (&actions, + fd_pair[0])))) + ) + error (EXIT_FAILURE, result, _("posix_spawn setup failed")); + + + char const *shell_prog = getenv ("SHELL"); + if (shell_prog == nullptr) + shell_prog = "/bin/sh"; + char const *const argv[] = { last_component (shell_prog), "-c", + filter_command, nullptr }; + + result = posix_spawn (&child_pid, shell_prog, &actions, &attr, + (char * const *) argv, environ); + if (result != 0) + error (EXIT_FAILURE, errno, _("failed to run command: \"%s -c %s\""), + shell_prog, filter_command); + if (close (fd_pair[0]) != 0) error (EXIT_FAILURE, errno, _("failed to close input pipe")); filter_pid = child_pid;