The audit subsystem of the Linux kernel can be very useful to figure out what processes are becoming zombie processes. I just had the following situation:
server ~ # ps -ef --forest
[...]
root 16385 1 0 17:04 ? 00:00:00 /usr/sbin/apache2 -k start
root 16388 16385 0 17:04 ? 00:00:00 \_ /usr/bin/perl -T -CSDAL /usr/lib/iserv/apache_user
root 16389 16385 0 17:04 ? 00:00:00 \_ /usr/bin/perl -T -CSDAL /usr/lib/iserv/apache_user
www-data 16415 16385 0 17:04 ? 00:00:00 \_ /usr/sbin/apache2 -k start
www-data 18254 16415 0 17:23 ? 00:00:00 | \_ [sh] <defunct>
www-data 18347 16415 0 17:23 ? 00:00:00 | \_ [sh] <defunct>
www-data 22966 16415 0 18:18 ? 00:00:00 | \_ [sh] <defunct>
www-data 16583 16385 0 17:05 ? 00:00:01 \_ /usr/sbin/apache2 -k start
www-data 18306 16583 0 17:23 ? 00:00:00 | \_ [sh] <defunct>
www-data 18344 16583 0 17:23 ? 00:00:00 | \_ [sh] <defunct>
www-data 17561 16385 0 17:12 ? 00:00:00 \_ /usr/sbin/apache2 -k start
www-data 22983 17561 0 18:18 ? 00:00:00 | \_ [sh] <defunct>
www-data 18318 16385 0 17:23 ? 00:00:00 \_ /usr/sbin/apache2 -k start
www-data 19725 16385 0 17:43 ? 00:00:01 \_ /usr/sbin/apache2 -k start
www-data 22638 16385 0 18:13 ? 00:00:00 \_ /usr/sbin/apache2 -k start
www-data 22659 16385 0 18:14 ? 00:00:00 \_ /usr/sbin/apache2 -k start
www-data 25102 16385 0 18:41 ? 00:00:00 \_ /usr/sbin/apache2 -k start
www-data 25175 16385 0 18:42 ? 00:00:00 \_ /usr/sbin/apache2 -k start
www-data 25272 16385 0 18:44 ? 00:00:00 \_ /usr/sbin/apache2 -k start
The cause for these zombie processes is most probably a PHP script, but as these Apache child processes are processing lots of HTTP requests and lots of different PHP scripts, it's very hard to figure out which one could be responsible. Linux has also already deallocated important information of these zombie processes, so we don't even have /proc/<pid>/cmdline
to figure out which script or -c
command /bin/sh
may have been running:
server ~ # cat /proc/18254/cmdline
server ~ #
To figure it out, I've installed auditd
: https://linux-audit.com/configuring-and-auditing-linux-systems-with-audit-daemon/
I set up the following audit rules:
auditctl -a always,exit -F arch=b32 -S execve -F path=/bin/dash
auditctl -a always,exit -F arch=b64 -S execve -F path=/bin/dash
These rules audit all process creations of the /bin/dash
binary. /bin/sh
doesn't work here, because it's a symlink and audit apparently only sees the target file name:
server ~ # ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Nov 8 2014 /bin/sh -> dash*
A simple test should now produce audit logs in /var/log/audit/audit.log
(I've taken the liberty and added a lot of line breaks to improve the readability):
server ~ # sh -c 'echo test'
test
server ~ # tail -f /var/log/audit/audit.log
[...]
type=SYSCALL msg=audit(1488219335.976:43871): arch=40000003 syscall=11 \
success=yes exit=0 a0=ffdca3ec a1=f7760e58 a2=ffdd399c a3=ffdca068 items=2 \
ppid=27771 pid=27800 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 \
fsgid=0 tty=pts7 ses=7532 comm="sh" exe="/bin/dash" key=(null)
type=EXECVE msg=audit(1488219335.976:43871): argc=3 a0="sh" a1="-c" \
a2=6563686F2074657374
type=CWD msg=audit(1488219335.976:43871): \
cwd="/var/lib/iserv/remote-support/iserv-martin.von.wittich"
type=PATH msg=audit(1488219335.976:43871): item=0 name="/bin/sh" inode=10403900 \
dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL
type=PATH msg=audit(1488219335.976:43871): item=1 name=(null) inode=5345368 \
dev=08:01 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL
type=PROCTITLE msg=audit(1488219335.976:43871): \
proctitle=7368002D63006563686F2074657374
Lots of the information is encoded, but ausearch
can translate it with -i
:
server ~ # ausearch -i -x /bin/dash | tail
[...]
----
type=PROCTITLE msg=audit(27.02.2017 19:15:35.976:43871) : proctitle=sh
type=PATH msg=audit(27.02.2017 19:15:35.976:43871) : item=1 name=(null) \
inode=5345368 dev=08:01 mode=file,755 ouid=root ogid=root rdev=00:00 \
nametype=NORMAL
type=PATH msg=audit(27.02.2017 19:15:35.976:43871) : item=0 name=/bin/sh \
inode=10403900 dev=08:01 mode=file,755 ouid=root ogid=root rdev=00:00 \
nametype=NORMAL
type=CWD msg=audit(27.02.2017 19:15:35.976:43871) : \
cwd=/var/lib/iserv/remote-support/iserv-martin.von.wittich
type=EXECVE msg=audit(27.02.2017 19:15:35.976:43871) : argc=3 a0=sh a1=-c \
a2=echo test
type=SYSCALL msg=audit(27.02.2017 19:15:35.976:43871) : arch=i386 \
syscall=execve success=yes exit=0 a0=0xffdca3ec a1=0xf7760e58 a2=0xffdd399c \
a3=0xffdca068 items=2 ppid=27771 pid=27800 auid=root uid=root gid=root \
euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts7 \
ses=7532 comm=sh exe=/bin/dash key=(null)
----
If you don't want to restrict the ausearch
filtering to /bin/dash
, you can also use ausearch -i -m ALL
to translate the complete log. Another good filter would be ausearch -i -p <PID of a zombie process>
, in this case ausearch -i -p 27800
.
Just leave these rules in place until new zombie processes show up, and then search for the process creation of a zombie PID:
ausearch -i -p <PID>
This should be very helpful to identify the root cause of the zombie processes. In my case it was a PHP script that used proc_open
to spawn a Perl script without closing the handle with proc_close
.
From the wikipedia page titled: Process identifier:
There are two tasks with specially distinguished process IDs: swapper
or sched has process ID 0 and is responsible for paging, and is
actually part of the kernel rather than a normal user-mode process.
Process ID 1 is usually the init process primarily responsible for
starting and shutting down the system. Originally, process ID 1 was
not specifically reserved for init by any technical measures: it
simply had this ID as a natural consequence of being the first process
invoked by the kernel. More recent Unix systems typically have
additional kernel components visible as 'processes', in which case PID
1 is actively reserved for the init process to maintain consistency
with older systems.
You can see the evidence of this if you look at the parent PIDs (PPID) of init
and kthreadd
:
$ ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jun24 ? 00:00:02 /sbin/init
root 2 0 0 Jun24 ? 00:00:00 [kthreadd]
kthreadd
is the kernel thread daemon. All kthreads are forked from this thread. You can see evidence of this if you look at other processes using ps
and seeing who their PPID is:
$ ps -eaf
root 3 2 0 Jun24 ? 00:00:57 [ksoftirqd/0]
root 4 2 0 Jun24 ? 00:01:19 [migration/0]
root 5 2 0 Jun24 ? 00:00:00 [watchdog/0]
root 15 2 0 Jun24 ? 00:01:28 [events/0]
root 19 2 0 Jun24 ? 00:00:00 [cpuset]
root 20 2 0 Jun24 ? 00:00:00 [khelper]
Notice they're all 2
.
Best Answer
netstat
There's a process there, your userid just isn't privy to seeing what it is. This is a layer of protection provided by
lsof
that's keeping you from seeing this. Simply re-run the command but prefix it using thesudo
command instead.There's even a warning about this in the output of
lsof
at the top.Example
ss
If you're not having any luck with
netstat
perhapsss
will do. You'll still need to usesudo
, and the output can be a little bit more cryptic.Example
Process ID still not there?
There are instances where there simply isn't a PID associated to the TCP port in use. You can read about NFS, in @derobert's answer, which is one of them. There are others. I have instances where I'm using ssh tunnels to connect back to services such as IMAP. These are showing up without a process ID too.
In any case you can use a more verbose form of
netstat
which might shed additional light on what process is ultimately using a TCP port.Example
If you notice the output includes INODES so we could back track into the process using this info.
Which will show you a file which might lead you to a process.
References