[PATCH] Core: prefer ioctl(FIOCLEX) over fcntl(FD_CLOEXEC)
Ben Noordhuis
info at bnoordhuis.nl
Thu Oct 25 13:46:47 UTC 2012
Use ioctl(FIOCLEX) to set the close-on-exec flag on platforms where it makes
sense: Linux, Darwin, the BSDs, *not* Solaris.
ioctl(FIOCLEX) is generally slightly faster than fcntl(FD_CLOEXEC), on the
order of 3-4%.
Ad-hoc benchmark:
#ifndef __linux__
#include <sys/filio.h>
#endif
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int fd = open("/dev/null", O_RDWR);
int n = 1e8;
if (argc < 2 || atoi(argv[1]) == 0)
while (n--)
fcntl(fd, F_SETFD, FD_CLOEXEC);
else
while (n--)
ioctl(fd, FIOCLEX);
return 0;
}
Results, median-of-10 style:
$ time tmp/cloexec 0 # FD_CLOEXEC
real 0m5.558s
user 0m2.388s
sys 0m3.152s
$ time tmp/cloexec 1 # FIOCLEX
real 0m5.373s
user 0m1.548s
sys 0m3.808s
The difference is small but consistent.
Caveat emptor: This change slows down the close-on-exec operation by about 4-5%
when a 32 bits nginx talks to a 64 bits kernel. The 32/64 bits conversion layer
has to translate the 32 bits ioctl() system call to its 64 bits counterpart,
which unfortunately is a slow operation.
---
auto/unix | 21 +++++++++++++++++++++
src/core/ngx_cycle.c | 10 ++++------
src/os/unix/ngx_process.c | 10 ++++------
src/os/unix/ngx_socket.h | 19 +++++++++++++++++++
4 files changed, 48 insertions(+), 12 deletions(-)
diff --git a/auto/unix b/auto/unix
index b0a0e4c..9784a1f 100755
--- a/auto/unix
+++ b/auto/unix
@@ -720,6 +720,27 @@ ngx_feature_test="int i = FIONBIO; printf(\"%d\", i)"
. auto/feature
+ngx_feature="ioctl(FIOCLEX)"
+ngx_feature_name="NGX_HAVE_FIOCLEX"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/ioctl.h>
+ #include <stdio.h>
+ $NGX_INCLUDE_SYS_FILIO_H"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="#ifdef __sun
+ /* With some version of SunOS, FIOCLEX simply doesn't work.
+ * With other versions it's roughly 3x times slower than
+ * fcntl(FD_CLOEXEC).
+ */
+ fail
+ #else
+ int i = FIOCLEX, k = FIONCLEX; printf(\"%d %d\", i, k);
+ #endif
+ "
+. auto/feature
+
+
ngx_feature="struct tm.tm_gmtoff"
ngx_feature_name="NGX_HAVE_GMTOFF"
ngx_feature_run=no
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index f153729..ba6b3c0 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -383,10 +383,9 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
}
#if !(NGX_WIN32)
- if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) {
+ if (ngx_cloexec(file[i].fd) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
- "fcntl(FD_CLOEXEC) \"%s\" failed",
- file[i].name.data);
+ ngx_cloexec_n " \"%s\" failed", file[i].name.data);
goto failed;
}
#endif
@@ -1219,10 +1218,9 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user)
}
}
- if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
+ if (ngx_cloexec(fd) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
- "fcntl(FD_CLOEXEC) \"%s\" failed",
- file[i].name.data);
+ ngx_cloexec_n " \"%s\" failed", file[i].name.data);
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c
index 4ef3582..4969f06 100644
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -157,18 +157,16 @@ ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
return NGX_INVALID_PID;
}
- if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {
+ if (ngx_cloexec(ngx_processes[s].channel[0]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
- name);
+ ngx_cloexec_n " failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
- if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
+ if (ngx_cloexec(ngx_processes[s].channel[1]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
- "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
- name);
+ ngx_cloexec_n " failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
}
diff --git a/src/os/unix/ngx_socket.h b/src/os/unix/ngx_socket.h
index fcc5153..4263114 100644
--- a/src/os/unix/ngx_socket.h
+++ b/src/os/unix/ngx_socket.h
@@ -20,6 +20,25 @@ typedef int ngx_socket_t;
#define ngx_socket_n "socket()"
+#if (NGX_HAVE_FIOCLEX)
+
+#define ngx_cloexec(fd) ioctl(fd, FIOCLEX)
+#define ngx_nocloexec(fd) ioctl(fd, FIONCLEX)
+
+#define ngx_cloexec_n "ioctl(FIOCLEX)"
+#define ngx_nocloexec_n "ioctl(FIONCLEX)"
+
+#else
+
+#define ngx_cloexec(fd) fcntl(fd, F_SETFD, FD_CLOEXEC)
+#define ngx_nocloexec(fd) fcntl(fd, F_SETFD, 0)
+
+#define ngx_cloexec_n "fcntl(FD_CLOEXEC)"
+#define ngx_nocloexec_n "fcntl(!FD_CLOEXEC)"
+
+#endif
+
+
#if (NGX_HAVE_FIONBIO)
int ngx_nonblocking(ngx_socket_t s);
--
1.7.9.5
More information about the nginx-devel
mailing list