kcella has asked for the wisdom of the Perl Monks concerning the following question:

I need to know if there is any way to force atomicity for a sequence of operations. I have a program that searches for a process and shuts it down if found. The problem is that the time between finding it and killing it leaves room for a user to shutdown the process manually. Then potentially another app could come along and steal the same PID the previous process was using and my application would come along and kill the wrong process.

I need to make sure that from the moment I find the process to when I kill it, no other process has the CPU. Can this be done?

thanks,
Kevin

Replies are listed 'Best First'.
Re: atomic operations
by superfrink (Curate) on Jul 09, 2004 at 05:40 UTC
    I need to make sure that from the moment I find the process to when I kill it, no other process has the CPU. Can this be done?
    Not as far as I'm aware on say Linux for example. Imagine your process is running and the timer (runs at 100 Hz by default) goes off because your process's time is up. Now the operating system is free to assign the CPU to another process at it's will during one of these ticks (or another hardware interrupt). (Based on the schedule algorithm and they vary.)

    Now suppose the operating system were to let processes request to not be interrupted. In that case it's possible one process would keep the CPU forever. Imagine say the process has an infinite loop in it, that bug in a user program could hang the system so nothing else would get the CPU. As far as I know Linux doesn't have any way to let processes request to not be interrupted.

    It has been a while since I've looked at the scheduler code (and I hear it has changed) but I recall you could specify priority levels where all processes in a higher priority would be served first. (Note this is different than the "nice" level as I recall.) I'm not sure but you may be able to make your find-and-kill process a higher priority and it won't give up the CPU until it yeilds it by it's own choice.
    Update: ie all lower priority processes won't get the CPU unless the higher priority processes (only your one process) have yielded the CPU.

    The point is I'm not aware of what you asked for being offered on most common operating systems but I'm not authoritative on the topic.
      This is the exact problem I am trying to avoid. Since everyone seems to agree that there is no easy 100% safe way to bypass the CPU scheduler, I will just double check right before I kill the PID (as danielcid recommends) and that should be enough. I am running on a winxp system and there is very little time between finding the PID and killing it. FYI: I use Win32::ToolHelp (to find by name) and Win32::Process (to kill by PID).

      thanks, Kevin
Re: atomic operations
by SciDude (Friar) on Jul 09, 2004 at 02:54 UTC

    Assuming that you are using the "cookbook" method;

    use Config; defined $Config{sig_name} or die "No sigs?"; $i = 0; # Config prepends fake 0 signal called "ZE +RO". foreach $name (split(' ', $Config{sig_name})) { $signo{$name} = $i; $signame[$i] = $name; $i++; }

    You should be able to send the kill command to a name -or- PID. This should solve your problem (or am I missing the point of your question?)


    SciDude
    The first dog barks... all other dogs bark at the first dog.
Re: atomic operations
by haoess (Curate) on Jul 09, 2004 at 07:13 UTC

    Which operating system do you use?

    What do you know about the process you search for? If it's not its PID then there is no way to do it in an atomic operation. Tools like pgrep, pkill and killall search within the proc filesystem and can't be atomic.

    Don't worry about your same PID problem. Linux for example uses 32768 different PID values. See and change PID_MAX_DEFAULT in include/linux/threads.h if that's not enough for you.

    -- Frank

Re: atomic operations
by danielcid (Scribe) on Jul 09, 2004 at 12:11 UTC
    It looks like that you are searching for a specific process by "name"
    (it wouldn't very smart to search for a specific process by PID).

    What you can do is:

    1- Find the name of the process and its PID
    2- Get the owner of this process
    3- Get the TTY of this process

    When you are going to kill it (you didn't say how much
    time later), you need to check if the Name,PID, owner and the
    TTY is still the same.. It can reduce drastically this
    "race condition" problem.

    *Remember that in most operating systems, the PID is not
    "random", so you can't just trust on that...

    []'s -DBC
Re: atomic operations
by samtregar (Abbot) on Jul 09, 2004 at 14:51 UTC
    You would need a real-time operating system to enforce this kind of restriction. As far as I know there aren't an RTOSes that support Perl. It might be possible to use something like RTLinux which offers an RTOS core and a non-real-time OS running at the same time.

    However, I think it's worth pointing out that the situation you describe is exceedingly unlikely to occur. Most operating systems don't start reusing PIDs until they reach their maximum PID, which is generally a long time even for a very fast system.

    -sam

Re: atomic operations
by bluto (Curate) on Jul 09, 2004 at 15:02 UTC
    I don't think there is any 100% safe way of doing this for an arbitrary process, but in practice it's probably fairly easy to get most of the way there. Some suggestions...

    Most systems I've used _seemed_ to avoid reusing a PID at least for a little while. If this is true and your find/kill sequence is short enough, this should get you 99.99% there.

    If you have root priviledge and the process you are trying to kill is owned by a non-root user, you could setuid to the user and then perform the kill. Of course this won't help if the same user gets the reused PID quickly enough.

      Note that on OpenBSD and Linuces that have the "pid randomization patch" applied, they could, in theory, reuse a pid as soon as the old process that used it goes away. This is because they don't cycle through pids, they just randomly pick an unused one when the old goes away. However, collisions relating to this are bound to be quite rare.
Re: atomic operations
by Anonymous Monk on Jul 09, 2004 at 15:08 UTC
    How about killing the process in question with a SIGSTOP first, so no progress is maded toward termination, then rechecking the name, if ok send SIGKILL and SIGCONT if not ok too.
Re: atomic operations
by eventualdave (Beadle) on Jul 09, 2004 at 13:14 UTC
    Is there an easy way to set the priority of a process through Perl? As the comments above said, I don't think there is a way to 100% ensure that your script won't be interrupted, but renice-ing it to a higher priority might help.

      It can help /only/ if the "time of check" and the "time
      to use" are very close. However, if this program is doing
      many things in the meanwhile (like checking other
      process, reading a file, etc), renicing is not going to
      help.

      []'s
      DBC
Re: atomic operations
by nothingmuch (Priest) on Jul 11, 2004 at 07:37 UTC
    If this is very crucial to you, have your script fork, and then use the parent as your worker and the child as the killer. Since the child will die if the parent does, the PPID will always be consistent.

    You could also be notified with SIGCHLD whenever your child exits, if you do it in reverse.

    I don't know of a way to do what you want reliably, but there are many other ways to get the same results.

    -nuffin
    zz zZ Z Z #!perl
Re: atomic operations
by BrowserUk (Patriarch) on Jul 12, 2004 at 22:00 UTC

    There may be a way to achieve a "safe kill" on windows. There is an API OpenProcess that allows (with appropriate privalege) one process to obtain a system handle to another running process.

    In my experiments, if one process (the killer) obtains a handle to another process (the killee), whilst it does not stop the latter dieing or being killed, it seems to prevent the process id for that process being re-used before the killer relingishes it's grip on the system handle returned by the API.

    You'll understand this is a bit vague. Verifying that this is true is very difficult. My attempt was:

    I started a copy of perl, found out it's pid.

    Ran the following script to obtain and hold onto a system handle to that pid.

    #! perl -slw use strict; use Win32::API::Prototype; use constant PROCESS_TERMINATE => 1; ApiLink( 'Kernel32', 'HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, D +WORD dwProcessId )' ) or die $^E; ApiLink( 'Kernel32', 'BOOL TerminateProcess( HANDLE hProcess, UINT uExitCode )' ) or die $^E; if( my $hProc = OpenProcess( PROCESS_TERMINATE, 0, $ARGV[ 0 ] ) ) { print "Got handle to process; Can you kill it?"; die 'Drat' if <STDIN> =~ m[^y]i; print "I'll try"; print TerminateProcess( $hProc, -1 ) ? 'It sleeps with da fishies!' : 'Double drat!'; } else { print "OpenProcess failed: $^E"; }

    I then killed the first copy of perl and left the second holding the handle.

    I started a third copy of perl that constantly spawned new processes (I created a tiny dummy executable int main () { return 0; } for speed), that recorded and yelled if the process id of the first process had, was reused:

    do{ my $pid = open my $in, "p:/test/dummy.exe |" or die $!; $pid{ $pid }++; printf "\r%7d : $pid ", scalar keys %pid; print " Reused\n\n" if $pid == 65992 ## The first pid } for 1 .. 1_000_000;

    I allowed this to run whilst I watch a movie. The pid was never re-used. I then killed the second process that was holding the system handle to the defunct first process. It's pid was then re-used twice within less than a minute.

    14281 : 65992 Reused 19112 : 65992 Reused 19112 : 60132 Terminating on signal SIGINT(2)

    Far from definitive proof, and if it is true, it isn't the complete picture as there still remains the task of ensuring that the pid to which you obtained the handle is the same process as it was when you obtained the PID in the first place...


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon