There's only one reason you ever get zombies: you were supposed to wait on a child process after it died, and you didn't. Given you're calling waitpid it means you either haven't gotten to that part of your code, or it's sitting there waiting on one pid that isn't ready.
If you truly don't care what your child processes return, just set $SIG{CHLD} = "IGNORE". If you do care, then you should setup an actual signal handler, $SIG{CHLD} = sub { my $pid = wait; $exits{$pid} = $?; }, or something similar.
Waiting on a specific pid is usually only done when you know that specific process has died.
Also, foreach my $pid (@pids) { ... }, you don't need a C-style for loop to iterate over an array.