Re: How to get a pid
--- quoth Ted Zeng [EMAIL PROTECTED]: Originally, my perl script launches Eggplant as a child process (use ``, not an independent process.) Actually ``, aka qx(), does spawn an independant process. Try running: perl -e'`sleep 6000`;' and then doing a `ps` to see. But the semantics of qx() mean that the forking, execing, and capturing of output are hidden from the programmer. Same goes for using system(). Because of the semantics of qx() and system(), there's no way to capture the pid of the spawned process-- that's all handled behind the scenes. If you don't trust the children to return properly, then you need to use fork() and exec() explicitly with something like the following: #!/usr/bin/env perl print $$: noone in here but us chickens\n; $pid = fork(); print $$: I got $pid\n; if ($pid == 0) { print $$: child is execing\n; exec 'sleep', 10; } else { print $$: parent is waiting\n; sleep 5; print $$: kids these days! (killing $pid)\n; kill 9, $pid; } __END__ You can watch this in another shell with a ps. You can also use elements of %SIG for the first argument to kill() but `sleep` so happens to ignore everything. Do note that the interleaving of the process scheduling after calling fork() is unpredictable, even if it appears reliable over a few runs. Even the order in which processes return from a call to fork() is unpredictable. Unfortunately with this approach, capturing the output of the child is harder. And of course you'll want to verify that the child is running too long before killing it in your version. Live well, ~wren __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Re: How to get a pid
Hi wren, Thanks. I know better now. I will try to fix the problem within my time limit. Best wishes, Ted zeng On 8/2/06 1:18 AM, wren ng thornton [EMAIL PROTECTED] wrote: --- quoth Ted Zeng [EMAIL PROTECTED]: Originally, my perl script launches Eggplant as a child process (use ``, not an independent process.) Actually ``, aka qx(), does spawn an independant process. Try running: perl -e'`sleep 6000`;' and then doing a `ps` to see. But the semantics of qx() mean that the forking, execing, and capturing of output are hidden from the programmer. Same goes for using system(). Because of the semantics of qx() and system(), there's no way to capture the pid of the spawned process-- that's all handled behind the scenes. If you don't trust the children to return properly, then you need to use fork() and exec() explicitly with something like the following: #!/usr/bin/env perl print $$: noone in here but us chickens\n; $pid = fork(); print $$: I got $pid\n; if ($pid == 0) { print $$: child is execing\n; exec 'sleep', 10; } else { print $$: parent is waiting\n; sleep 5; print $$: kids these days! (killing $pid)\n; kill 9, $pid; } __END__ You can watch this in another shell with a ps. You can also use elements of %SIG for the first argument to kill() but `sleep` so happens to ignore everything. Do note that the interleaving of the process scheduling after calling fork() is unpredictable, even if it appears reliable over a few runs. Even the order in which processes return from a call to fork() is unpredictable. Unfortunately with this approach, capturing the output of the child is harder. And of course you'll want to verify that the child is running too long before killing it in your version. Live well, ~wren __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Re: How to get a pid
One way of seeing that the grandfather process can terminate grandchild processes is to have the child process catch the signal and kill, in turn, its own child processes, as part of its clean-up code before it dies. This means that you have to use a signal that can be recovered from when killing the child process.
Re: How to get a pid
Thanks. This is a pretty good lecture on OS's process. You are right. The problem is the Grandchild could not be trusted. And the child is stuck with the grandchild due to the way it launches The grandchild. But since the parent knows child's pid, it could use this information To find out the grandchild's pid (as Packy proposed). I think that solves The problem pretty well. Ted Zeng Adobe Systems Inc. On 7/31/06 6:06 PM, wren ng thornton [EMAIL PROTECTED] wrote: --- quoth Packy Anderson: --- quoth Ted Zeng: I don't know how the OS generate the pid for a process. That is why I don't feel comfortable with what I did. The way I understand it, the OS assigns the next highest available pid when creating a process. It' s never specifically documented because it's one of those implementation dependent things, but canonically speaking it's the next available pid which is either a first fit or next fit depending on which is easier to implement. Due to the nature of the lifecycles of processes this generally means that in practice child=ppid+1 but in actuality there is no guarantee that the two pids are related to one another. This general observation can be undone by wrapping of pids past the max pid, by some other process jumping in to spawn a child before your process' request is fulfilled, by other processes dying off and revealing a better next available pid, etc etc. Whenever a process makes a syscall to fork() an exact copy of the current process is made (including all open files and some other complexities), with the only difference being the pid. The other difference is that once the call to fork() returns the parent will receive the child's pid as a return value and the child will receive 0 (an invalid pid). If for for some reason the fork fails (e.g. no available pids), then the parent process will receive -1 (also an invalid pid). Generally it's up to the parent to keep track of their children and there's not much recourse to recovering them if you loose them. However the OS does keep track of such things (to deal with auto-reaping zombies and the like) and so there're often ways to recover that info. E.g. in C the waitpid() function has an option to wait for any child to die; I don't know of a Perl implementation if this call, but I wouldn't be surprised if it's out there, and if it isn't anyone with a bit of XS knowledge should be able to bang one out quickly. There may be other ways depending on the specific OS. I've only loosely been following this thread, but it sounds like the problem isn't the parent (the Perl script) keeping track of it's children, but rather keeping track of grandchildren. Since POSIX makes no province for keeping track of grandchildren, it's really something the child should keep track of and then report to the parent. That is, every process should clean up after themselves and so every parent can consider their child to be one process ignoring any other processes they may spin off. Of course it sounds like the child is an unknown (and not particularly trusted). Is there not any way for the Perl script to call the necessary grandchildren directly? Live well, ~wren __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Re: How to get a pid
I don't know how the OS generate the pid for a process. That is why I don't feel comfortable with what I did. I am not sure if there is a race situation here. Ted zeng On 7/28/06 4:21 PM, Joel Rees [EMAIL PROTECTED] wrote: On 2006/07/29, at 4:12, Ted Zeng wrote: ... I have used pid+1 for quite a few days now and it seems to work without any problem. But I still feel it is not the right thing to do. Have you ever heard of race?
Re: How to get a pid
On Jul 31, 2006, at 5:35 PM, Ted Zeng wrote: I don't know how the OS generate the pid for a process. That is why I don't feel comfortable with what I did. The way I understand it, the OS assigns the next highest available pid when creating a process. However, pids wrap around when they reach the maximum defined pid. So, if your process' pid is near the max, your child could get a lower pid. What you want to do is find the pid of your child's child. There's probably a system call you can make to find these things, but I don't know it. What I do know is how to use ps -j or ps -l. I've whipped up the following script to demonstrate the technique. Note: the script you're waiting on may have spawned off children of it's own, so it'd be a good idea to find them and kill them first. This, of course, may let the script spawn off even more processes... start demo script #!/usr/bin/perl use POSIX qw(:sys_wait_h); # need this to get non-blocking waitchild $|=1; # autoflush on unless (defined($kid_pid = fork())) { die Cannot fork: $!; } if ($kid_pid == 0) { # we're running as the child process execNightshade(); } else { print Watching child $kid_pid...\n; my $count=0; my $tConstant = 1; ## I'm impatient do { print Sleeping ... ; sleep 10; $count++; $kid = waitpid($kid_pid, WNOHANG); print kid: $kid_pid, waitpid: $kid\n; } until ($kid 0 or $count $tConstant); if ($count $tConstant ) { recursive_kill($kid_pid); } } sub execNightshade { # since we don't know what execEggplant does beside exec-ing shell # scripts with ``, let's make something up... my $count = join q{ }, 1 .. 10; # make a string '1 2 3 4 5 6 7 8 9 10' # this shell script prints numbers from 1 to 100, sleeping 10 # seconds between each number... my $output = `for i in $count; do echo \$i; sleep 10; done`; # what good is output if we don't use it? print $output\n; } sub recursive_kill { my($pid) = @_; while (my $child = child_pid($pid)) { # kill all its children, grandchildren, etc... recursive_kill($child); } # then kill the process itself print Killing $pid\n; kill 9, $pid; } sub child_pid { my($parent) = @_; # we want to know the process id of the child that's been forked # by our own child. since OS X is a unix, we can use ps to find # it. the following highly non-portable code should work on OS X # and _maybe_ some other unices, but definately won't work on # WinAnything or other, stranger OSes... my $grandkid_pid; open my $ps, ps -l |; while ($ps) { # unless the first field is numeric, we're looking at the # column headings. let's discard it. next unless /^ \s* \d+ /msx; # split /PATTERN/,EXPR,LIMIT # # If EXPR is omitted, splits the $_ string. If PATTERN is # also omitted, splits on whitespace (after skipping any # leading whitespace). # # As a special case, specifying a PATTERN of space (' ') # will split on white space just as split with no # arguments does. my($uid, $pid, $ppid, $other_fields) = split q{ }, $_, 4; # if the process' parent pid isn't our kid's pid, we're # not interested in it next unless ($ppid == $parent); # congratulations! you're a parent! return $pid; } return; # if there's no child, just return } end demo script Sample output: $ pid_test.pl Watching child 7718... Sleeping ... kid: 7718, waitpid: 0 Sleeping ... kid: 7718, waitpid: 0 Killing 7724 Killing 7729 Killing 7733 Killing 7736 Killing 7739 Killing 7742 Killing 7745 Killing 7748 Killing 7751 1 2 3 4 5 6 7 8 9 10 Killing 7719 Killing 7718 $ What's happening is the script is killing the 'sleep', and then before the subroutine can return and kill off the for loop, it's sleeping again. Fortunately, it disposes of the sleeps fairly quickly, and the child process actually returns its data before it gets killed. -- Packy Anderson [EMAIL PROTECTED] Catapultam habeo. Nisi pecuniam omnem mihi dabis, ad caput tuum saxum immane mittam.
Re: How to get a pid
--- quoth Packy Anderson: --- quoth Ted Zeng: I don't know how the OS generate the pid for a process. That is why I don't feel comfortable with what I did. The way I understand it, the OS assigns the next highest available pid when creating a process. It' s never specifically documented because it's one of those implementation dependent things, but canonically speaking it's the next available pid which is either a first fit or next fit depending on which is easier to implement. Due to the nature of the lifecycles of processes this generally means that in practice child=ppid+1 but in actuality there is no guarantee that the two pids are related to one another. This general observation can be undone by wrapping of pids past the max pid, by some other process jumping in to spawn a child before your process' request is fulfilled, by other processes dying off and revealing a better next available pid, etc etc. Whenever a process makes a syscall to fork() an exact copy of the current process is made (including all open files and some other complexities), with the only difference being the pid. The other difference is that once the call to fork() returns the parent will receive the child's pid as a return value and the child will receive 0 (an invalid pid). If for for some reason the fork fails (e.g. no available pids), then the parent process will receive -1 (also an invalid pid). Generally it's up to the parent to keep track of their children and there's not much recourse to recovering them if you loose them. However the OS does keep track of such things (to deal with auto-reaping zombies and the like) and so there're often ways to recover that info. E.g. in C the waitpid() function has an option to wait for any child to die; I don't know of a Perl implementation if this call, but I wouldn't be surprised if it's out there, and if it isn't anyone with a bit of XS knowledge should be able to bang one out quickly. There may be other ways depending on the specific OS. I've only loosely been following this thread, but it sounds like the problem isn't the parent (the Perl script) keeping track of it's children, but rather keeping track of grandchildren. Since POSIX makes no province for keeping track of grandchildren, it's really something the child should keep track of and then report to the parent. That is, every process should clean up after themselves and so every parent can consider their child to be one process ignoring any other processes they may spin off. Of course it sounds like the child is an unknown (and not particularly trusted). Is there not any way for the Perl script to call the necessary grandchildren directly? Live well, ~wren __ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Re: How to get a pid
I could not figure out how to get the tool process's id. But by observing, I notice that its pid is 'always' equals to child process's pid+1. PIDs can wrap, so there is no guarantee of this at all. Why do you call waitpid with -1, when you know the pid you want to wait on ($kidpid). The reason for zombies (ie, (perl)) is normally that the process has died, but no one has requested the termination status (with waidpid or its cousins). I admit to having no idea what execEggplant does. But it sounds like it is running another subprocess. Really, it needs to wait on that sub process and return when it is complete. You could use killall to find the process by name: killall -s -d Preview although that is always a bit risky if the process name is not known and unique. Otherwise you have problems. Peter. -- Check out Interarchy 8.1.1, just released, now with Amazon S3 support. http://www.stairways.com/ http://download.stairways.com/
RE: How to get a pid
It would have been helpful to see the contents of execEggplant. Is it really using exec? If so, the child's pid should be the same as the thing that exec'd it. Sorry. I should be more clear. My code uses ` ` (backward qoute?), which is the same as system(), I think. So it is not exec(), open(). execEggplant is a command that executes eggplant with a Eggplant script path as parameter. I also pipe the error to output. $execEggplant = runscript an_eggplant_script_path 2$1 ## just from my memory. I don't have the script on hand right now. The reason I doesn't use exec is because I want to catch the output from Eggplant (it write its log to err). If I use exec(), then I wouldn't be able to see the output from that process. I have no experience with open() and took a look at it, and it doesn't seem to work for my case. I have used pid+1 for quite a few days now and it seems to work without any problem. But I still feel it is not the right thing to do. ted If you use open(), the return value of open will be the pid of the pipe opened. If you use system(), you may be out of luck; I don't know. ...or is it the case that execEggplant exec's some script which then, itself runs Eggplant? In that case, you will have to roll your own IPC. -- rjbs
Re: How to get a pid
On 2006/07/29, at 4:12, Ted Zeng wrote: ... I have used pid+1 for quite a few days now and it seems to work without any problem. But I still feel it is not the right thing to do. Have you ever heard of race?
Re: How to get a pid
* Ted Zeng [EMAIL PROTECTED] [2006-07-25T13:17:13] I could not figure out how to get the tool process's id. But by observing, I notice that its pid is 'always' equals to child process's pid+1. It would have been helpful to see the contents of execEggplant. Is it really using exec? If so, the child's pid should be the same as the thing that exec'd it. If you use open(), the return value of open will be the pid of the pipe opened. If you use system(), you may be out of luck; I don't know. ...or is it the case that execEggplant exec's some script which then, itself runs Eggplant? In that case, you will have to roll your own IPC. -- rjbs signature.asc Description: Digital signature
How to get a pid
Hi, I have the following problem. I run a perl script that set up an environment, then Run a tool and wait for its return. But the tool hangs from time to time. I have to kill it when it hangs. To do this, I use Fork. I fork a child process that will run the tool. In the parent process, I get the child process's pid and if it runs longer than a set time limit, the parent will kill the child. But I found out that while the child dies, it still hangs around with the tool. (it has (perl) when I do 'ps -xc' ). If I kill the tool process, then thing is ok. I could not figure out how to get the tool process's id. But by observing, I notice that its pid is 'always' equals to child process's pid+1. So, I let the parent process kills 'child pid+1. It seems to work so far. I have to admit I don't feel comfortable with doing this, though it works so far. Any suggestion on how I could figure out the tool's pid? Regards, Ted Zeng Adobe Systems Inc. Here is the part of the script that does fork: if(!defined( $kidpid = fork() )){ die Canot fork: $!; }elsif ($kidpid == 0) { ## Execute the Eggplant Scripts. execEggplant(); } else { my $count=0; my $tConstant = 120; ## 20 min. do { sleep 10; $count ++; $kid = waitpid(-1, WNOHANG); } until ($kid 0 or $count $tConstant); if($count $tConstant ){ { $kidpid = $kidpid + 1; ## get the Eggplant PID ## The child process is still running. Log and kill it. my $ret = `kill $kidpid`; LogBoth(...20 min. passed... We terminate the Eggplant process: $ret); exit(0); ## no need to archive now. The child process should do the archive } ## } }