win32-process(0.6.0) Process::kill 方法發送POSIX信號的問題

這兩天在寫一個工具,今天下午被win32-process(0.6.0)包的Process::kill方法浪費了2小時。

 

trap("INT") do
  puts "got signal INT"
end

puts "Sup"

gets

Process.kill("INT", Process.pid);
 

這段代碼舉個例子,目的很簡單,就是想接收和發送一個POSIX信號,驗證執行上正確的結果。這個我本是準備用在fork子進程後,父向子通知消息用的,但是發現很簡單的道理,可子進程就是不能進trap。後來發現問題是在於win32-process(0.6.0)包的Process::kill方法問。驗證一下,上面的代碼只要在首行加上:

 

require 'win32/process'
 

就立刻玩完,win32-process(0.6.0)包修改後的kill方法無法正確投遞“SIGINT”中斷。可憐的是,一方面爲了在Windows平臺上使用fork方法使用win32-process(0.6.0)包,而另一方面它的kill又有這個問題。不知道各位看客有什麼高見,分享下。

 

另:今天休息,家裏沒*nix環境無法再實驗了,等將來跨平臺Review代碼時再試吧。呵呵,看來Ruby還是和*nix親。

 

// 11.30 18.10 添加 ////

 

剛google到一個,和我有同樣的疑問,看來問題不是那麼好解決。

http://stackoverflow.com/questions/140111/sending-an-arbitrary-signal-in-windows

 

// 11.30 18.11 添加 ////

 

看了一下win32-process(0.6.0)包process.rb的源代碼,發現問題是在183行:

 

if GenerateConsoleCtrlEvent
(CTRL_C_EVENT, pid)
    killed_pids.push(pid)
end

 

其中GenerateConsoleCtrlEvent函數始終失敗,返回0值。通過kill方法上的註釋和MSDN(http://msdn.microsoft.com/en-us/library/ms683155(VS.85).aspx )裏的說明看到,該函數成功處理CTRL_C_EVENT消息的必要條件是,必須要向進程標識中dwProcessGroupId 參數包含CREATE_NEW_PROCESS_GROUP(0x00000200)flag的進程發送消息才能夠成功。爲了驗證,臨時在process.rb中添加了個函數fork2:

 

   def fork2
      last_arg = ARGV.last
      
      # Look for the 'child#xxx' tag
      if last_arg =~ /child#\d+/
         @i += 1
         num = last_arg.split('#').last.to_i
         if num == @i
            if block_given?
               status = 0
               begin
                  yield
               rescue Exception
                  status = -1 # Any non-zero result is failure
               ensure
                  return status
               end
            end
            return nil
         else
            return false
         end
      end
   
      # Tag the command with the word 'child#xxx' to distinguish it
      # from the calling process.
      cmd = 'ruby -I "' + $LOAD_PATH.join(File::PATH_SEPARATOR) << '" "'
      cmd << File.expand_path($PROGRAM_NAME) << '" ' << ARGV.join(' ')
      cmd << ' child#' << @child_pids.length.to_s
      
      startinfo = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
      startinfo = startinfo.pack('LLLLLLLLLLLLSSLLLL')
      procinfo  = [0,0,0,0].pack('LLLL')
      
      rv = CreateProcess(0, cmd, 0, 0, 1, CREATE_NEW_PROCESS_GROUP
, 0, 0, startinfo, procinfo)







      
      if rv == 0
         raise Error, get_last_error
      end
      
      pid = procinfo[8,4].unpack('L').first
      @child_pids.push(pid)
      
      pid 
   end

 

上面的代碼就是複製的fork代碼,只是其中紅色代碼行中的CreateProcess函數的第六個dwCreationFlags(DWORD)參數將CREATE_NEW_PROCESS_GROUP置位。之後執行測試代碼:

 

require 'win32/process'

Signal.trap('INT') do
	File.open("c:/c.txt", "w") { |f| f.write("ok") }
end

pid = Process::fork2
 do
	trap(2) { File.open("c:/b.txt", "w") { |f| f.write("ok") }; exit! }

	while 1
		sleep(2)
	end
end

exit! if pid < 1

puts "Pause any key to send 'INT' sinal to child #{pid}"

gets

Process.kill(2, pid);

Process.kill('INT', Process.pid);


gets

puts "parent proc end."
 

發現對於fork2出的子進程GenerateConsoleCtrlEvent函數執行成功,即兩個kill都執行成功。但是發現測試代碼中的trap代碼還是不能被執行。很是奇怪~

 

看了上面的google到的貼子,感覺可能是Windows沒有很好的對POSIX信號支持造成的。再研究研究,大家有什麼看法?期待中~~

 

// 2009.03.07 13:40 添加 ////


作者:lzy.je
出處:http://lzy.iteye.com
本文版權歸作者所有,只允許以摘要和完整全文兩種形式轉載,不允許對文字進行裁剪。未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章