[1] https://github.com/torvalds/linux/blob/3e732ebf7316ac83e8562...
[1] https://github.com/torvalds/linux/blob/3e732ebf7316ac83e8562...
$ touch empty
$ chmod a+x empty
$ ./empty # Works
$ valgrind -q ./empty # Works
$ timeout 10s ./empty # Works
$ /usr/bin/time ./empty # Works
$ perl -e 'exec("./empty") or die' # Works
$ python -c 'import subprocess; subprocess.check_call("./empty", shell=True)' # Works
$ python -c 'import subprocess; subprocess.check_call("./empty")' # Fails
...
OSError: [Errno 8] Exec format error
$ ruby -e 'exec "./empty"' # Fails
-e:1:in `exec': Exec format error - ./empty (Errno::ENOEXEC)
from -e:1
$ strace ./empty # Fails
execve("./empty", ["./empty"], 0x7fff6639f3a0 /* 84 vars */) = -1 ENOEXEC (Exec format error)
strace: exec: Exec format error
+++ exited with 1 +++
It can be quite fun to track down why a program executes successfully and then later fails to execute, with no changes made to the program in between. zsh: Exec format error: empty-file
(last command returned 127.)
(127 is the exit status for other failures, so I've used it here. The second line is intended to be part of my PS1, the first zsh's normal reporting. The string used here is what I get from perror() for that code.)> If the execl() function fails due to an error equivalent to the [ENOEXEC] error defined in the System Interfaces volume of POSIX.1-2017, the shell shall execute a command equivalent to having a shell invoked with the pathname resulting from the search as its first operand, with any remaining arguments passed to the new shell, except that the value of "$0" in the new shell may be set to the command name. If the executable file is not a text file, the shell may bypass this command execution. In this case, it shall write an error message, and shall return an exit status of 126.
[1] https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V... [2] https://unix.stackexchange.com/a/373229
Does POSIX define what a "text file" is?
https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1...
An empty file actually contains zero of everything in the universe.
$ strace -c ./empty
strace: exec: Erreur de format pour exec()
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0,00 0,000000 0 1 1 execve
------ ----------- ----------- --------- --------- ----------------
100,00 0,000000 0 1 1 total
$ strace -c /bin/true
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
54,05 0,000060 15 4 mprotect
24,32 0,000027 27 1 set_tid_address
10,81 0,000012 12 1 set_robust_list
8,11 0,000009 9 1 munmap
2,70 0,000003 3 1 prlimit64
0,00 0,000000 0 1 read
0,00 0,000000 0 2 close
0,00 0,000000 0 8 mmap
0,00 0,000000 0 1 brk
0,00 0,000000 0 4 pread64
0,00 0,000000 0 1 1 access
0,00 0,000000 0 1 execve
0,00 0,000000 0 2 1 arch_prctl
0,00 0,000000 0 2 openat
0,00 0,000000 0 2 newfstatat
------ ----------- ----------- --------- --------- ----------------
100,00 0,000111 3 32 2 total
the multiple execution seems to be much slower with the empty executable than with /bin/true using the multitime tool https://tratt.net/laurie/src/multitime/ $ multitime -n 10 ./empty
===> multitime results
1: ./empty
Mean Std.Dev. Min Median Max
real 0.006 0.000 0.005 0.006 0.006
user 0.001 0.001 0.000 0.002 0.002
sys 0.000 0.001 0.000 0.000 0.002
$ multitime -n 10 /bin/true
===> multitime results
1: /bin/true
Mean Std.Dev. Min Median Max
real 0.002 0.000 0.001 0.002 0.003
user 0.001 0.000 0.001 0.001 0.001
sys 0.000 0.000 0.000 0.000 0.000That's because it reports an error and does not actually execute the file.
>strace: exec: Erreur de format pour exec()
`strace` uses one of the exec()-style functions that don't automatically invoke a shell here. And if it did, it would invoke an entire shell (/bin/sh, typically), which might have more overhead.
----
You need to be very careful when measuring this, because what happens is that a shell executes the shebangless empty file.
If you try launching it from e.g. a C program using execlp(), it will have to first start that shell.
If you try launching it from e.g. bash, it might directly run that in-process or at least after a fork() or similar, skipping some of the shell setup.
So the overhead might depend on context.