[File] [PATCH 3/4] use vfork when spawning decompressor

Denys Vlasenko dvlasenk at redhat.com
Fri May 3 10:57:00 UTC 2019


Tools such as rpmdiff and rpmbuild call libmagic from processes
with large mapped virtual sizes (gigabytes). In this case,
vfork is much faster than fork (sometimes x100 faster).

Signed-off-by: Denys Vlasenko <dvlasenk at redhat.com>
---
 src/compress.c | 35 ++++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/src/compress.c b/src/compress.c
index a2ede242..417ccc86 100644
--- a/src/compress.c
+++ b/src/compress.c
@@ -707,30 +707,47 @@ uncompressbuf(int fd, size_t bytes_max, size_t method, const unsigned char *old,
 		return makeerror(newch, n, "Cannot create pipe, %s",
 		    strerror(errno));
 	}
-	pid = fork();
+
+	/* For processes with large mapped virtual sizes, vfork
+	 * may be _much_ faster (10-100 times) than fork.
+	 */
+	pid = vfork();
 	if (pid < 0) {
-		return makeerror(newch, n, "Cannot fork, %s",
+		return makeerror(newch, n, "Cannot vfork, %s",
 		    strerror(errno));
 	}
 	if (pid == 0) {
 		/* child */
-
+		/* Note: we are after vfork, do not modify memory
+		 * in a way which confuses parent. In particular,
+		 * do not modify fdp[i][j].
+		 */
 		if (fd != -1) {
 			(void) lseek(fd, CAST(off_t, 0), SEEK_SET);
 			if (copydesc(STDIN_FILENO, fd))
-				close(fd);
+				(void) close(fd);
 		} else {
-			copydesc(STDIN_FILENO, fdp[STDIN_FILENO][0]); closep(fdp[STDIN_FILENO]);
+			if (copydesc(STDIN_FILENO, fdp[STDIN_FILENO][0]))
+				(void) close(fdp[STDIN_FILENO][0]);
+			if (fdp[STDIN_FILENO][1] > 2)
+				(void) close(fdp[STDIN_FILENO][1]);
 		}
-///FIXME: if one of the fdp[i][j] is 0, 1, or 2, this can bomb spectacularly
-		copydesc(STDOUT_FILENO, fdp[STDOUT_FILENO][1]); closep(fdp[STDOUT_FILENO]);
-		copydesc(STDERR_FILENO, fdp[STDERR_FILENO][1]); closep(fdp[STDERR_FILENO]);
+///FIXME: if one of the fdp[i][j] is 0 or 1, this can bomb spectacularly
+		if (copydesc(STDOUT_FILENO, fdp[STDOUT_FILENO][1]))
+			(void) close(fdp[STDOUT_FILENO][1]);
+		if (fdp[STDOUT_FILENO][0] > 2)
+			(void) close(fdp[STDOUT_FILENO][0]);
+
+		if (copydesc(STDERR_FILENO, fdp[STDERR_FILENO][1]))
+			(void) close(fdp[STDERR_FILENO][1]);
+		if (fdp[STDERR_FILENO][0] > 2)
+			(void) close(fdp[STDERR_FILENO][0]);
 
 		(void)execvp(compr[method].argv[0],
 		    RCAST(char *const *, RCAST(intptr_t, compr[method].argv)));
 		dprintf(STDERR_FILENO, "exec `%s' failed, %s",
 		    compr[method].argv[0], strerror(errno));
-		exit(1);
+		_exit(1); /* _exit(), not exit(), because of vfork */
 	}
 	/* parent */
 	/* Close write sides of child stdout/err pipes */
-- 
2.21.0



More information about the File mailing list