PID of exec

H

hendedav

Gang,

I have looked all over google groups on how to find the pid of
the exec command when using the fork/exec. I will admit that I am not
overly familiar with the workings (I understand the idea though) of
these two commands. Here is what I am working with. I have a perl
script (see below) that calls a shell script that simply counts to
thirty while pausing for one second (basically is a script that does
nothing for 30 seconds so I can see if the PID is correct). What I am
trying to accomplish is trying to find the pid of the binary/script
that is executed from the exec command. If I run the script below,
this would be a sample of what I get:


debian:/tmp# ./test.pl
pid is 6330, parent pid is 6329
debian:/tmp# ps a
PID TTY STAT TIME COMMAND
2545 tty1 Ss+ 0:00 /sbin/getty 38400 tty1
2546 tty2 Ss+ 0:00 /sbin/getty 38400 tty2
2547 tty3 Ss+ 0:00 /sbin/getty 38400 tty3
2548 tty4 Ss+ 0:00 /sbin/getty 38400 tty4
2551 tty5 Ss+ 0:00 /sbin/getty 38400 tty5
2552 tty6 Ss+ 0:00 /sbin/getty 38400 tty6
2569 pts/1 Ss 0:00 -bash
2578 pts/1 S 0:00 bash
3104 pts/0 Ss+ 0:00 -bash
6331 pts/1 S 0:00 /bin/sh ./test.sh
6346 pts/1 S 0:00 sleep 1
6347 pts/1 R+ 0:00 ps a

As you can see neither of the pids is the one from the exec command.
I also tried using the open command (which returns the pid correctly),
but I can not background the process (using an & at the end of the
command). I need to find the pid of the exec command within the
parent perl script so that I can track it. Any help would greatly be
appreciated.

Thanks,
Dave

#!/usr/bin/perl

if (defined (my $pid = fork)) {
if ($pid) { # this test runs if the fork was successful
# eliminates the zombies
local $SIG{CHLD} = "IGNORE";
print "pid is $pid\n, parent pid is $$\n";
} else { # the following line runs in the child
exec("./test.sh &");
print "child pid is: $$\n";
exit();
}
} else {
print "there was a problem executing the script\n";
}
 
P

Peter Makholm

As you can see neither of the pids is the one from the exec command.

exec() does not spawn a new process and do therefore not generate a
new process id. The exec function does never return so you 'print
"child pid is $$\n' statement is never executed (why didn't you winder
about this?).
#!/usr/bin/perl

if (defined (my $pid = fork)) {
if ($pid) { # this test runs if the fork was successful
# eliminates the zombies
local $SIG{CHLD} = "IGNORE";
print "pid is $pid\n, parent pid is $$\n";
} else { # the following line runs in the child
exec("./test.sh &");
print "child pid is: $$\n";

Try to exchange the above two lines of code and se if you don't get an
pid for the child which matches what test.sh have.
exit();
}
} else {
print "there was a problem executing the script\n";
}

//Makholm
 
H

hendedav

exec() does not spawn a new process and do therefore not generate a
new process id. The exec function does never return so you 'print
"child pid is $$\n' statement is never executed (why didn't you winder
about this?).



Try to exchange the above two lines of code and se if you don't get an
pid for the child which matches what test.sh have.


//Makholm

I switched the two lines of code as suggested, and I did get a print
out, but it contained the same PID as the "my $pid=fork" statment.
Here is the output:

debian:/tmp# ./test.pl
child pid is: 6498
fork pid is 6498, parent pid is 6497
debian:/tmp# ps au
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2545 0.0 0.2 1576 492 tty1 Ss+ Oct24 0:00 /sbin/
getty 38400 tty1
root 2546 0.0 0.2 1576 492 tty2 Ss+ Oct24 0:00 /sbin/
getty 38400 tty2
root 2547 0.0 0.2 1576 488 tty3 Ss+ Oct24 0:00 /sbin/
getty 38400 tty3
root 2548 0.0 0.2 1580 496 tty4 Ss+ Oct24 0:00 /sbin/
getty 38400 tty4
root 2551 0.0 0.2 1580 496 tty5 Ss+ Oct24 0:00 /sbin/
getty 38400 tty5
root 2552 0.0 0.2 1580 496 tty6 Ss+ Oct24 0:00 /sbin/
getty 38400 tty6
hendedav 2569 0.0 0.8 3552 1912 pts/1 Ss Oct24 0:00 -bash
root 2578 0.0 0.7 3044 1656 pts/1 S Oct24 0:00 bash
hendedav 3104 0.0 0.8 3560 1924 pts/0 Ss+ Oct24 0:00 -bash
root 6499 0.0 0.5 2704 1136 pts/1 S 13:45 0:00 /bin/
sh ./test.sh
root 6503 0.0 0.2 1876 460 pts/1 S 13:45 0:00 sleep 1
root 6504 0.0 0.3 2584 880 pts/1 R+ 13:45 0:00 ps au

I am trying to get the 6499 pid from the list. That is the actual pid
of the process that is being executed in the exec statment. Maybe I
didn't say things right in the first post, sorry. :)

Dave
 
P

Peter Makholm

I switched the two lines of code as suggested, and I did get a print
out, but it contained the same PID as the "my $pid=fork" statment.
Here is the output:

Yes, of course. I wasn't actually reading what you wrote, just
noticed that you tried to write the pid out after the exec. 'use
warnings' should have warned you about that.

But ok, another (and better educated) guess about you problem:

'perldoc -f exec' says:

If there is more than one argument in LIST, or if LIST is an
array with more than one value, calls execvp(3) with the argu-
ments in LIST. If there is only one scalar argument or an
array with one element in it, the argument is checked for shell
metacharacters, and if there are any, the entire argument is
passed to the system's command shell for parsing (this is
"/bin/sh -c" on Unix platforms, but varies on other platforms).
If there are no shell metacharacters in the argument, it is
split into words and passed directly to "execvp", which is more
efficient. Examples:

exec('test.sh &') contains a shell metacharacter, namely '&', so what
exactly happens is

exec('/bin/sh', '-c', 'test.sh &');

You process (6498) executes /bin/sh which tries to run test.sh in the
background and then exists. Running in the background means forking
another process.

The solution is not to pass the argument to the system shell:

exec('test.sh');

And you're allready (kind of) running the process in the background
after you have fork'ed.

//Makholm
 
H

hendedav

Yes, of course. I wasn't actually reading what you wrote, just
noticed that you tried to write the pid out after the exec. 'use
warnings' should have warned you about that.

But ok, another (and better educated) guess about you problem:

'perldoc -f exec' says:

If there is more than one argument in LIST, or if LIST is an
array with more than one value, calls execvp(3) with the argu-
ments in LIST. If there is only one scalar argument or an
array with one element in it, the argument is checked for shell
metacharacters, and if there are any, the entire argument is
passed to the system's command shell for parsing (this is
"/bin/sh -c" on Unix platforms, but varies on other platforms).
If there are no shell metacharacters in the argument, it is
split into words and passed directly to "execvp", which is more
efficient. Examples:

exec('test.sh &') contains a shell metacharacter, namely '&', so what
exactly happens is

exec('/bin/sh', '-c', 'test.sh &');

You process (6498) executes /bin/sh which tries to run test.sh in the
background and then exists. Running in the background means forking
another process.

The solution is not to pass the argument to the system shell:

exec('test.sh');

And you're allready (kind of) running the process in the background
after you have fork'ed.

//Makholm


That makes sense. I have changed the code in the perl script and it
returns the correct pid!!! I read another post that said after
running the exec statment, it is a highly recommended to use an
"exit();" statement. Does this sound correct?
 
H

hendedav

That makes sense. I have changed the code in the perl script and it
returns the correct pid!!! I read another post that said after
running the exec statment, it is a highly recommended to use an
"exit();" statement. Does this sound correct?


I have run into another problem. This code has been modified
(slightly) and inserted into a cgi script

if (defined (my $pid = fork)) {
if ($pid) {
local $SIG{CHLD} = "IGNORE";
print "Content-type: text/xml\n\n\n";
print "<info pid=\"". $pid ."\" date=\"".
scalar(localtime) ."\" />";
} else {
exec("/tmp/test.sh");
exit();
}
} else {
print "Content-type: text/xml\n\n\n";
print "<error>\n";
print "<message>The script couldn't be started.</message>\n";
print "</error>\n";
}

Now the webbrowser will not get a reply until the test.sh script has
finished executing. Any ideas?

Dave
 
H

hendedav

At 2007-10-25 03:16PM, "(e-mail address removed)" wrote:
[...]
Now the webbrowser will not get a reply until the test.sh script has
finished executing. Any ideas?

Your test.sh script should emit non-parsed headers:
http://www.oreilly.com/openbook/cgi/ch03_08.html

which means your perl script should probably be named "nph-whatever.pl"
(depending on your web server).

This is now off-topic for this group.


I renamed the script and implemented the header suggested on that
link, but it still is not working. Can you suggest a group to post in
for this problem?

Thanks,
Dave
 
G

Gary E. Ansok

I have run into another problem. This code has been modified
(slightly) and inserted into a cgi script

if (defined (my $pid = fork)) {
if ($pid) {
local $SIG{CHLD} = "IGNORE";
print "Content-type: text/xml\n\n\n";
print "<info pid=\"". $pid ."\" date=\"".
scalar(localtime) ."\" />";
} else {
exec("/tmp/test.sh");
exit();
}
} else {
print "Content-type: text/xml\n\n\n";
print "<error>\n";
print "<message>The script couldn't be started.</message>\n";
print "</error>\n";
}

Now the webbrowser will not get a reply until the test.sh script has
finished executing. Any ideas?

One possibility is that the server is waiting for the output to be
completed before sending it off to the browser. The server won't
see end-of-file on the read end of the internal connection (between
the server and the cgi script) until all the processes have closed
the write end.

In this case, the process running /tmp/test.sh still has its standard
output set to that connection, so the server still needs to wait in
case test.sh writes more data.

The solution is to redirect standard output to a more suitable place
(or discard it by using /dev/null):
} else { open STDOUT, '>', '/dev/null';
exec("/tmp/test.sh");
exit();
}

(Normally, we'd check to see if the open succeeded, but in this case
we presumably don't care as long as the existing channel gets closed.)

I would also close or re-direct STDIN and STDERR, unless you have
a reason not to.

Gary
 
H

hendedav

One possibility is that the server is waiting for the output to be
completed before sending it off to the browser. The server won't
see end-of-file on the read end of the internal connection (between
the server and the cgi script) until all the processes have closed
the write end.

In this case, the process running /tmp/test.sh still has its standard
output set to that connection, so the server still needs to wait in
case test.sh writes more data.

The solution is to redirect standard output to a more suitable place
(or discard it by using /dev/null):


open STDOUT, '>', '/dev/null';


(Normally, we'd check to see if the open succeeded, but in this case
we presumably don't care as long as the existing channel gets closed.)

I would also close or re-direct STDIN and STDERR, unless you have
a reason not to.

Gary


Thanks for the help Gary. I inserted the redirect for STDOUT and also
tried STDIN (not sure how to with STDERR if the arrows are any
indication). I have also tried in on the commandline. here is what I
have tried:

} else {
open STDOUT, '>', '/dev/null';
open STDIN, '<', '/dev/null';
#open STDERR, '?', '/dev/null';
exec("/tmp/test.sh </dev/null >>/dev/null 2>&1");
}

Still no luck. Any other ideas or corrections in the code to try?

Dave
 
N

nolo contendere

Thanks for the help Gary. I inserted the redirect for STDOUT and also
tried STDIN (not sure how to with STDERR if the arrows are any
indication). I have also tried in on the commandline. here is what I
have tried:

} else {

open STDOUT, '>', '/dev/null';
open STDIN, '<', '/dev/null';
#open STDERR, '?', '/dev/null';
exec("/tmp/test.sh </dev/null >>/dev/null 2>&1");

}

Still no luck. Any other ideas or corrections in the code to try?

Dave


maybe autoflush?
$|++;
 
H

hendedav

On Oct 25, 4:12 pm, (e-mail address removed) (Gary E. Ansok) wrote:
maybe autoflush?
$|++;


That seemed to have worked. Here is the adjusted code:

} else {
$|++;
open STDOUT, '>', '/dev/null';
open STDIN, '>', '/dev/null';
open STDERR, '>', '/dev/null';
exec("/tmp/test.sh");
exit();
}


Does this look okay or do I need to add anything else?

Thanks,
Dave
 
G

Gary E. Ansok

That seemed to have worked. Here is the adjusted code:

} else {
$|++;
open STDOUT, '>', '/dev/null';
open STDIN, '>', '/dev/null';
open STDERR, '>', '/dev/null';
exec("/tmp/test.sh");
exit();
}

STDIN should be opened with '<' rather than '>'. This won't matter
unless /tmp/test.sh or something it calls tries to read from standard
input, but if it does, better to get end-of-file instead of a
runtime error.

Gar
 
H

hendedav

That seemed to have worked. Here is the adjusted code:
} else {
$|++;
open STDOUT, '>', '/dev/null';
open STDIN, '>', '/dev/null';
open STDERR, '>', '/dev/null';
exec("/tmp/test.sh");
exit();
}

STDIN should be opened with '<' rather than '>'. This won't matter
unless /tmp/test.sh or something it calls tries to read from standard
input, but if it does, better to get end-of-file instead of a
runtime error.

Gar[/QUOTE]


Thanks Gary. Is the STDERR pointed the correct way? Also, this
script seems to be generating zombies. Anyone have ideas on how to
clear that up? I was hoping that the "local $SIG{CHLD} = "IGNORE";"
line would do the trick (as it stated in the perldoc's), but I guess
not.

Thanks,
Dave
 
J

J. Gleixner

Thanks Gary. Is the STDERR pointed the correct way?

Well, does the program possibly read or does it
possibly write? You should know the difference
between '<', and '>', long before you ever use
fork(), IMHO.
 
X

xhoster

Also, this
script seems to be generating zombies. Anyone have ideas on how to
clear that up?

You could double-fork.
I was hoping that the "local $SIG{CHLD} = "IGNORE";"
line would do the trick (as it stated in the perldoc's), but I guess
not.

What if the "local" expires before the child does? Then IGNORE is no
longer in effect and you leave zombies. Did the docs say to set $SIG{CHLD}
to IGNORE with local? That seems like a manifestly bizarre thing to do.
The effects of $SIG{CHLD} are inherently global and pretending it can be
localized is going to get you no where fast.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
H

hendedav

Well, does the program possibly read or does it
possibly write? You should know the difference
between '<', and '>', long before you ever use
fork(), IMHO.

Thanks for the reply J, but I was unfamiliar with the usage in perl.
Redirecting in bash uses the same arrow (eg 2>&1). Plus, you run out
of arrows after STDIN and STDOUT, so I had no idea what to use for
STDERR. So, I figured maybe they all used the same arrow. Oh well...

Dave
 
H

hendedav

You could double-fork.


What if the "local" expires before the child does? Then IGNORE is no
longer in effect and you leave zombies. Did the docs say to set $SIG{CHLD}
to IGNORE with local? That seems like a manifestly bizarre thing to do.
The effects of $SIG{CHLD} are inherently global and pretending it can be
localized is going to get you no where fast.

Xho

--
--------------------http://NewsReader.Com/--------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.


No I don't recall the doc's saying to use local, but while working
with another part of the project, I was searching for answers and come
upon that as a solution. Anyhow, I removed the 'local' portion, but I
am still receiving zombies. I will post the entire script now for
review:


#!/usr/bin/perl -w

use strict;
use CGI qw:)all);
use CGI::Carp qw(fatalsToBrowser);

if (defined param('runnow')) {
if (defined (my $pid = fork)) {
if ($pid) {
$SIG{CHLD} = "IGNORE";
print "Content-type: text/xml", "\n\n";
print "<info pid=\"". $pid ."\" date=
\"".scalar(localtime)."\" />";
} else {
$|++;
open STDOUT, '>', '/dev/null';
open STDIN, '<', '/dev/null';
open STDERR, '>', '/dev/null';
$_ = param('runnow');
exec("/tmp/test.sh");
exit();
}
} else {
print "Content-type: text/xml", "\n\n";
print "<error>\n";
print "<message>The script couldn't be started.</message>\n";
print "</error>\n";
}

} elsif (defined param('pollJob')) {
my $pid = param('pollJob');
my $retval = `ps h -p $pid 2>&1`;

print "Content-type: text/plain", "\n\n";
if ($retval eq "") {
print "Restore job completed...";
} else {
open(REJ, "/tmp/rej$pid");
chomp ($retval=<REJ>);
close(REJ);
if ($retval ne 'Processing select data...') {
print "$retval";
} else {
my $dir = readlink "/tmp/rej$pid.data";
$dir = "/mnt" . substr($dir, rindex($dir,"/"), -4);
my $data = '';
if (! open(FILES, "/tmp/rej$pid.data")) { exit 0; }
while (<FILES>) {
s/^\./$dir/;
if (-e $_) {$data .= "$_<br />";} else {last;}
}
close(FILES);
print $data;
}
}
}


The top if statement only gets executed once (to start a process which
is monitored by the pollJob section), then the pollJob gets called
between 2-3 seconds until the job is done. This job is only running
for 30 seconds (because of the test.sh script), but I accumulate
several many zombies. Any ideas?

Thanks
Dave
 
A

A. Sinan Unur

(e-mail address removed) wrote in @v3g2000hsg.googlegroups.com:
Thanks for the reply J, but I was unfamiliar with the usage in perl.
Redirecting in bash uses the same arrow (eg 2>&1). Plus, you run out
of arrows after STDIN and STDOUT, so I had no idea what to use for
STDERR. So, I figured maybe they all used the same arrow. Oh well...

This is a WTF in so many ways.

1) 2 > &1 means redirect the stderr output to where stdout points.

2) The pointy edge of the angle bracket is the target. So:

open FILEHANDLE, '<', 'source'

means you want to use FILEHANDLE to read from 'source'. On the other
hand,

open ANOTHERHANDLE, '>', 'target'

means you want to write to 'target' the output you send to
ANOTHERHANDLE.

STDERR is an output stream just as STDOUT is. STDIN is an input stream.
I would think the suffixes OUT and IN would give that away. Now, I
think, it is common sense (I have not questioned this in the last 20
years) that STDERR is the stream where you *output* error messages so,
again, the pointy edge of the angle bracket points away from the STDERR
and to the target of that stream.

"I was running out of arrows so I used the same one for all of them".
This is not a grocery store: The logic of "they had run out of apples so
I bought pears" cannot be applied.

I think I am scarred for life (again).

Sinan
 
X

xhoster

No I don't recall the doc's saying to use local, but while working
with another part of the project, I was searching for answers and come
upon that as a solution. Anyhow, I removed the 'local' portion, but I
am still receiving zombies. I will post the entire script now for
review:

First, are the zombies something to worry about? If they go away by
soon enough so they don't clog up the kernel anyway, don't worry about
them.


.....
The top if statement only gets executed once (to start a process which
is monitored by the pollJob section), then the pollJob gets called
between 2-3 seconds until the job is done. This job is only running
for 30 seconds (because of the test.sh script), but I accumulate
several many zombies. Any ideas?

Since the code you show only forks once and is only run once, it couldn't
give rise to many zombies. The zombies must be coming from someplace else.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
H

hendedav

First, are the zombies something to worry about? If they go away by
soon enough so they don't clog up the kernel anyway, don't worry about
them.

....




Since the code you show only forks once and is only run once, it couldn't
give rise to many zombies. The zombies must be coming from someplace else.

Xho

--
--------------------http://NewsReader.Com/--------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.


The reason that I know that they come from this script is because it
is the only file with its name shown in the ps listing with a
"<defunct>" statement beside each occurance. I end up with about 4 or
5 zombies (that I have paid attention to) for just the 30 seconds of
the test.sh script, but what if the script runs for minutes or hours?
The script shown isn't run just once, it is run every 2-3 seconds
until the test.sh script (which will eventually be replaced with
another script that performs an actual job) is completed.

Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,533
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top