module Process
def create(args)
Process.spawn method instead of Process.create where possible.
If you really to use Process.wait, then you should use the
sleep 0.1 while !Process.get_exitcode(info.process_id)
To simulate Process.wait you can use this approach:
executable file in the security context of the specified credentials.
If the 'with_logon' option is set, then the process runs the specified
before the ProcessInfo struct is returned.
process_handle and the thread_handle are automatically closed for you
If the 'close_handles' option is set to true (the default) then the
* thread_id - Thread ID.
* process_id - Process ID.
* thread_handle - The handle to the primary thread of the process.
* process_handle - The handle to the newly created process.
The ProcessInfo struct contains the following members:
automatically OR'd to the +startf_flags+ value.
is automatically set to true and the Process::STARTF_USESTDHANDLES flag is
If 'stdin', 'stdout' or 'stderr' are specified, then the +inherit+ value
an option for JRuby.
are not currently supported. Unfortunately, setting these is not currently
IO objects or file descriptors (i.e. a fileno). However, StringIO objects
Note that the 'stdin', 'stdout' and 'stderr' options can be either Ruby
* stderr
* stdout
* stdin
* startf_flags
* sw_flags
* fill_attribute
* y_count_chars
* x_count_chars
* y_size
* x_size
* y
* x
* title
* desktop
and the StartupInfo struct on MSDN for more information.
GUI or console processes. See the documentation on CreateProcess()
part of the StartupInfo struct, and are generally only meaningful for
The startup_info key takes a hash. Its keys are attributes that are
mandatory.
of 'with_logon'. If 'with_logon' is set, then the 'password' option is
The 'domain' and 'password' options are only relevent in the context
require an explicit path or extension to work.
be preferred if only one of them is set because it does not (necessarily)
error is raised. Both may be set individually, but 'command_line' should
Of these, the 'command_line' or 'app_name' must be specified or an
* password (default: nil, mandatory if with_logon)
* domain (default: nil)
* with_logon (default: nil)
* close_handles (default: true)
* environment (default: nil)
* startup_info (default: nil)
* cwd (default: Dir.pwd)
* creation_flags (default: 0)
* thread_inherit (default: false)
* process_inherit (default: false)
* inherit (default: false)
* app_name (default: nil)
* command_line (this or app_name must be present)
There are several primary keys:
returning a ProcessInfo struct. It accepts a hash as an argument.
This is a wrapper for the CreateProcess() function. It executes a process,
Process.create(key => value, ...) => ProcessInfo
def create(args) unless args.is_a?(Hash) raise TypeError, "hash keyword arguments expected" end valid_keys = %w{ app_name command_line inherit creation_flags cwd environment startup_info thread_inherit process_inherit close_handles with_logon domain password } valid_si_keys = %w{ startf_flags desktop title x y x_size y_size x_count_chars y_count_chars fill_attribute sw_flags stdin stdout stderr } # Set default values hash = { "app_name" => nil, "creation_flags" => 0, "close_handles" => true, } # Validate the keys, and convert symbols and case to lowercase strings. args.each { |key, val| key = key.to_s.downcase unless valid_keys.include?(key) raise ArgumentError, "invalid key '#{key}'" end hash[key] = val } si_hash = {} # If the startup_info key is present, validate its subkeys if hash["startup_info"] hash["startup_info"].each { |key, val| key = key.to_s.downcase unless valid_si_keys.include?(key) raise ArgumentError, "invalid startup_info key '#{key}'" end si_hash[key] = val } end # The +command_line+ key is mandatory unless the +app_name+ key # is specified. unless hash["command_line"] if hash["app_name"] hash["command_line"] = hash["app_name"] hash["app_name"] = nil else raise ArgumentError, "command_line or app_name must be specified" end end env = nil # The env string should be passed as a string of ';' separated paths. if hash["environment"] env = hash["environment"] unless env.respond_to?(:join) env = hash["environment"].split(File::PATH_SEPARATOR) end env = env.map { |e| e + 0.chr }.join("") + 0.chr env.to_wide_string! if hash["with_logon"] end # Process SECURITY_ATTRIBUTE structure process_security = nil if hash["process_inherit"] process_security = SECURITY_ATTRIBUTES.new process_security[:nLength] = 12 process_security[:bInheritHandle] = 1 end # Thread SECURITY_ATTRIBUTE structure thread_security = nil if hash["thread_inherit"] thread_security = SECURITY_ATTRIBUTES.new thread_security[:nLength] = 12 thread_security[:bInheritHandle] = 1 end # Automatically handle stdin, stdout and stderr as either IO objects # or file descriptors. This won't work for StringIO, however. It also # will not work on JRuby because of the way it handles internal file # descriptors. # %w{stdin stdout stderr}.each { |io| if si_hash[io] if si_hash[io].respond_to?(:fileno) handle = get_osfhandle(si_hash[io].fileno) else handle = get_osfhandle(si_hash[io]) end if handle == INVALID_HANDLE_VALUE ptr = FFI::MemoryPointer.new(:int) if windows_version >= 6 && get_errno(ptr) == 0 errno = ptr.read_int else errno = FFI.errno end raise SystemCallError.new("get_osfhandle", errno) end # Most implementations of Ruby on Windows create inheritable # handles by default, but some do not. RF bug #26988. bool = SetHandleInformation( handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT ) raise SystemCallError.new("SetHandleInformation", FFI.errno) unless bool si_hash[io] = handle si_hash["startf_flags"] ||= 0 si_hash["startf_flags"] |= STARTF_USESTDHANDLES hash["inherit"] = true end } procinfo = PROCESS_INFORMATION.new startinfo = STARTUPINFO.new unless si_hash.empty? startinfo[:cb] = startinfo.size startinfo[:lpDesktop] = si_hash["desktop"] if si_hash["desktop"] startinfo[:lpTitle] = si_hash["title"] if si_hash["title"] startinfo[:dwX] = si_hash["x"] if si_hash["x"] startinfo[:dwY] = si_hash["y"] if si_hash["y"] startinfo[:dwXSize] = si_hash["x_size"] if si_hash["x_size"] startinfo[:dwYSize] = si_hash["y_size"] if si_hash["y_size"] startinfo[:dwXCountChars] = si_hash["x_count_chars"] if si_hash["x_count_chars"] startinfo[:dwYCountChars] = si_hash["y_count_chars"] if si_hash["y_count_chars"] startinfo[:dwFillAttribute] = si_hash["fill_attribute"] if si_hash["fill_attribute"] startinfo[:dwFlags] = si_hash["startf_flags"] if si_hash["startf_flags"] startinfo[:wShowWindow] = si_hash["sw_flags"] if si_hash["sw_flags"] startinfo[:cbReserved2] = 0 startinfo[:hStdInput] = si_hash["stdin"] if si_hash["stdin"] startinfo[:hStdOutput] = si_hash["stdout"] if si_hash["stdout"] startinfo[:hStdError] = si_hash["stderr"] if si_hash["stderr"] end app = nil cmd = nil # Convert strings to wide character strings if present if hash["app_name"] app = hash["app_name"].to_wide_string end if hash["command_line"] cmd = hash["command_line"].to_wide_string end if hash["cwd"] cwd = hash["cwd"].to_wide_string end if hash["with_logon"] logon = hash["with_logon"].to_wide_string if hash["password"] passwd = hash["password"].to_wide_string else raise ArgumentError, "password must be specified if with_logon is used" end if hash["domain"] domain = hash["domain"].to_wide_string end hash["creation_flags"] |= CREATE_UNICODE_ENVIRONMENT bool = CreateProcessWithLogonW( logon, # User domain, # Domain passwd, # Password LOGON_WITH_PROFILE, # Logon flags app, # App name cmd, # Command line hash["creation_flags"], # Creation flags env, # Environment cwd, # Working directory startinfo, # Startup Info procinfo # Process Info ) unless bool raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno) end else inherit = hash["inherit"] ? 1 : 0 bool = CreateProcessW( app, # App name cmd, # Command line process_security, # Process attributes thread_security, # Thread attributes inherit, # Inherit handles? hash["creation_flags"], # Creation flags env, # Environment cwd, # Working directory startinfo, # Startup Info procinfo # Process Info ) unless bool raise SystemCallError.new("CreateProcess", FFI.errno) end end # Automatically close the process and thread handles in the # PROCESS_INFORMATION struct unless explicitly told not to. if hash["close_handles"] CloseHandle(procinfo[:hProcess]) CloseHandle(procinfo[:hThread]) end ProcessInfo.new( procinfo[:hProcess], procinfo[:hThread], procinfo[:dwProcessId], procinfo[:dwThreadId] ) end
def get_affinity(int = Process.pid)
(0..mask).to_a.map{ |n| mask[n] }
mask = Process.get_affinity.first
this approach:
indicating the flag value of each processor, you can use something like
If you want to convert a decimal bit vector into an array of 0's and 1's
Process.get_affinity # => [[9], [15]]
# System has 4 processors, current process only allowed on 1 and 4.
Process.get_affinity # => [[15], [15]], where '15' is 1 + 2 + 4 + 8
# System has 4 processors, current process is allowed to run on all.
Example:
system.
which each bit represents the processors that are configured into a
process is allowed to run on. A system affinity mask is a bit vector in
A process affinity mask is a bit vector indicating the processors that a
containing the system affinity mask. Both are decimal values.
array, with the first containing the process affinity mask, and the second
current process if no pid is provided. The return value is a two element
Returns the process and system affinity mask for the given +pid+, or the
def get_affinity(int = Process.pid) pmask = FFI::MemoryPointer.new(:ulong) smask = FFI::MemoryPointer.new(:ulong) if int == Process.pid unless GetProcessAffinityMask(GetCurrentProcess(), pmask, smask) raise SystemCallError, FFI.errno, "GetProcessAffinityMask" end else begin handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0 , int) if handle == 0 raise SystemCallError, FFI.errno, "OpenProcess" end unless GetProcessAffinityMask(handle, pmask, smask) raise SystemCallError, FFI.errno, "GetProcessAffinityMask" end ensure CloseHandle(handle) end end [pmask.read_ulong, smask.read_ulong] end
def get_exitcode(pid)
when stdin, stdout or stderr are set to custom values.
the process isn't recognized as a child process (ECHILD). This happens for example
is still running. The usual way of calling Process.wait doesn't work when
This method is very handy for finding out if a process started with Process.create
is still running. Note that the process doesn't have to be a child process.
Returns the exitcode of the process with given +pid+ or nil if the process
def get_exitcode(pid) handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid) if handle == INVALID_HANDLE_VALUE raise SystemCallError.new("OpenProcess", FFI.errno) end begin buf = FFI::MemoryPointer.new(:ulong, 1) unless GetExitCodeProcess(handle, buf) raise SystemCallError.new("GetExitCodeProcess", FFI.errno) end ensure CloseHandle(handle) end exitcode = buf.read_int if exitcode == STILL_ACTIVE nil else exitcode end end
def get_heap_info(handle)
def get_heap_info(handle) hash = Hash.new { |h, k| h[k] = [] } hl = HEAPLIST32.new hl[:dwSize] = hl.size if Heap32ListFirst(handle, hl) while Heap32ListNext(handle, hl) he = HEAPENTRY32.new he[:dwSize] = he.size if Heap32First(he, Process.pid, hl[:th32HeapID]) hash[he[:th32ProcessID]] << HeapSnapInfo.new(he[:dwAddress], he[:dwBlockSize], he[:dwFlags], he[:th32ProcessID], he[:th32HeapID]) else if FFI.errno == ERROR_NO_MORE_FILES break else raise SystemCallError.new("Heap32First", FFI.errno) end end hash[he[:th32ProcessID]] << HeapSnapInfo.new(he[:dwAddress], he[:dwBlockSize], he[:dwFlags], he[:th32ProcessID], he[:th32HeapID]) while Heap32Next(he) end end hash end
def get_module_info(handle)
def get_module_info(handle) hash = Hash.new { |h, k| h[k] = [] } me = MODULEENTRY32.new me[:dwSize] = me.size if Module32First(handle, me) hash[me[:th32ProcessID]] << ModuleSnapInfo.new( me[:th32ProcessID], me[:modBaseAddr].to_i, me[:modBaseSize], me[:hModule], me[:szModule].to_s, me[:szExePath].to_s ) else if FFI.errno == ERROR_NO_MORE_FILES return hash else raise SystemCallError.new("Module32First", FFI.errno) end end while Module32Next(handle, me) hash[me[:th32ProcessID]] << ModuleSnapInfo.new( me[:th32ProcessID], me[:modBaseAddr].to_i, me[:modBaseSize], me[:hModule], me[:szModule].to_s, me[:szExePath].to_s ) end hash end
def get_process_info(handle)
def get_process_info(handle) hash = Hash.new { |h, k| h[k] = [] } pe = PROCESSENTRY32.new pe[:dwSize] = pe.size if Process32First(handle, pe) hash[pe[:th32ProcessID]] = ProcessSnapInfo.new( pe[:th32ProcessID], pe[:cntThreads], pe[:th32ParentProcessID], pe[:pcPriClassBase], pe[:dwFlags], pe[:szExeFile].to_s ) else if FFI.errno == ERROR_NO_MORE_FILES return hash else raise SystemCallError.new("Process32First", FFI.errno) end end while Process32Next(handle, pe) hash[pe[:th32ProcessID]] = ProcessSnapInfo.new( pe[:th32ProcessID], pe[:cntThreads], pe[:th32ParentProcessID], pe[:pcPriClassBase], pe[:dwFlags], pe[:szExeFile].to_s ) end hash end
def get_thread_info(handle, pid = nil)
def get_thread_info(handle, pid = nil) lpte = THREADENTRY32.new lpte[:dwSize] = lpte.size hash = Hash.new { |h, k| h[k] = [] } if Thread32First(handle, lpte) hash[lpte[:th32OwnerProcessID]] << ThreadSnapInfo.new(lpte[:th32ThreadID], lpte[:th32OwnerProcessID], lpte[:tpBasePri]) else if FFI.errno == ERROR_NO_MORE_FILES return hash else raise SystemCallError.new("Thread32First", FFI.errno) end end hash[lpte[:th32OwnerProcessID]] << ThreadSnapInfo.new(lpte[:th32ThreadID], lpte[:th32OwnerProcessID], lpte[:tpBasePri]) while Thread32Next(handle, lpte) hash end
def getpriority(kind, int)
32768 => Process::ABOVE_NORMAL_PRIORITY_CLASS
16384 => Process::BELOW_NORMAL_PRIORITY_CLASS
256 => Process::REALTIME_PRIORITY_CLASS
128 => Process::HIGH_PRIORITY_CLASS
64 => Process::IDLE_PRIORITY_CLASS
32 => Process::NORMAL_PRIORITY_CLASS
Possible return values are:
information, so it is effectively always Process::PRIO_PROCESS.
You can only retrieve process information, not process group or user
The +kind+ parameter is ignored but required for API compatibility.
correspond to higher priority classes.
the default implementation, lower return values do not necessarily
Retrieves the priority class for the specified process id +int+. Unlike
def getpriority(kind, int) raise TypeError, kind unless kind.is_a?(Integer) # Match spec raise TypeError, int unless int.is_a?(Integer) # Match spec int = Process.pid if int == 0 # Match spec handle = OpenProcess(PROCESS_QUERY_INFORMATION, 0, int) if handle == 0 raise SystemCallError, FFI.errno, "OpenProcess" end begin priority = GetPriorityClass(handle) if priority == 0 raise SystemCallError, FFI.errno, "GetPriorityClass" end ensure CloseHandle(handle) end priority end
def getrlimit(resource)
once within the same program would fail.
be associated with the job. In other words, trying to call it more than
it unavailable within the same process again since it would no longer
at the end of the block, while marking it for closure, would also make
to close a job handle. This is necessary because simply calling it
NOTE: Both the getrlimit and setrlimit method use an at_exit handler
--
Process.getrlimit(Process::RLIMIT_VMEM) # => [0, 0]
Example:
If [0,0] is returned then it means no limit has been set.
same.
there is no separate hard and soft limit. The values will always be the
While a two element array is returned in order to comply with the spec,
approximately 4TB (or 4GB if not NTFS).
constant is hard coded to the maximum file size on an NTFS filesystem,
refers to the per process user time limit. The Process::RLIMIT_FSIZE
all refer to the Process memory limit. The Process::RLIMIT_CPU constant
The Process:RLIMIT_AS, Process::RLIMIT_RSS and Process::VMEM constants
Process::RLIMIT_VMEM
Process::RLIMIT_RSS
Process::RLIMIT_AS
Process::RLIMIT_FSIZE
Process::RLIMIT_CPU
of flags are supported.
Gets the resource limit of the current process. Only a limited number
def getrlimit(resource) if resource == RLIMIT_FSIZE if volume_type == "NTFS" return ((1024**4) * 4) - (1024 * 64) # ~ 4TB else return (1024**3) * 4 # 4 GB end end handle = nil in_job = Process.job? # Put the current process in a job if it's not already in one if in_job && defined?(@win32_process_job_name) handle = OpenJobObjectA(JOB_OBJECT_QUERY, 1, @win32_process_job_name) raise SystemCallError, FFI.errno, "OpenJobObject" if handle == 0 else @win32_process_job_name = "ruby_" + Process.pid.to_s handle = CreateJobObjectA(nil, @win32_process_job_name) raise SystemCallError, FFI.errno, "CreateJobObject" if handle == 0 end begin unless in_job unless AssignProcessToJobObject(handle, GetCurrentProcess()) raise Error, get_last_error end end ptr = JOBJECT_EXTENDED_LIMIT_INFORMATION.new val = nil # Set the LimitFlags member of the struct case resource when RLIMIT_CPU ptr[:BasicLimitInformation][:LimitFlags] = JOB_OBJECT_LIMIT_PROCESS_TIME when RLIMIT_AS, RLIMIT_VMEM, RLIMIT_RSS ptr[:BasicLimitInformation][:LimitFlags] = JOB_OBJECT_LIMIT_PROCESS_MEMORY else raise ArgumentError, "unsupported resource type: '#{resource}'" end bool = QueryInformationJobObject( handle, JobObjectExtendedLimitInformation, ptr, ptr.size, nil ) unless bool raise SystemCallError, FFI.errno, "QueryInformationJobObject" end case resource when Process::RLIMIT_CPU val = ptr[:BasicLimitInformation][:PerProcessUserTimeLimit][:QuadPart] when RLIMIT_AS, RLIMIT_VMEM, RLIMIT_RSS val = ptr[:ProcessMemoryLimit] end ensure at_exit { CloseHandle(handle) if handle } end [val, val] end
def job?
def job? pbool = FFI::MemoryPointer.new(:int) IsProcessInJob(GetCurrentProcess(), nil, pbool) pbool.read_int == 1 ? true : false end
def kill(signal, *pids)
Process.kill(1, 12345, :exit_proc => 'ExitProcess', :module => 'kernel32')
Example:
block until the process is actually signaled.
you may specify Process::INFINITE, and your code will
signaled and instead returns immediately. Alternatively,
then the process does not wait if the process is not
actually die. The default is 5ms. If you specify 0 here
:wait_time => The time, in milliseconds, to wait for the process to
The default is 'kernel32'.
:dll_module => The name of the .dll (or .exe) that contains :exit_proc.
is used. The default is 'ExitProcess'.
:exit_proc => The name of the exit function called when signal 1 or 4-8
Possible options for signals 1 and 4-8.
behaves.
allow finer control over how that process is killed and how your program
When using signals 1 or 4-8 you may specify additional options that
same as the default implementation.
chance to do internal cleanup before being killed. Signal 0 behaves the
Signals 1 and 4-8 kill the process more nicely, giving the process a
process harshly, given that process no chance to do any internal cleanup.
a ctrl-c or ctrl-break event, respectively. Signal 9 terminates the
Internally, signals 2 and 3 will generate a console control event, using
behavior.
it kills processes, but this version also gives you finer control over
implementation of Process.kill. The differences mainly reside in the way
Kill a given process with a specific signal. This overrides the default
def kill(signal, *pids) # Match the spec, require at least 2 arguments if pids.length == 0 raise ArgumentError, "wrong number of arguments (1 for at least 2)" end # Match the spec, signal may not be less than zero if numeric if signal.is_a?(Numeric) && signal < 0 # EINVAL raise SystemCallError.new(22) end # Match the spec, signal must be a numeric, string or symbol unless signal.is_a?(String) || signal.is_a?(Numeric) || signal.is_a?(Symbol) raise ArgumentError, "bad signal type #{signal.class}" end # Match the spec, making an exception for BRK/SIGBRK, if the signal name is invalid. # Older versions of JRuby did not include KILL, so we've made an explicit exception # for that here, too. if signal.is_a?(String) || signal.is_a?(Symbol) signal = signal.to_s.sub("SIG", "") unless Signal.list.keys.include?(signal) || %w{KILL BRK}.include?(signal) raise ArgumentError, "unsupported name '#{signal}'" end end # If the last argument is a hash, pop it and assume it's a hash of options if pids.last.is_a?(Hash) hash = pids.pop opts = {} valid = %w{exit_proc dll_module wait_time} hash.each { |k, v| k = k.to_s.downcase unless valid.include?(k) raise ArgumentError, "invalid option '#{k}'" end opts[k] = v } exit_proc = opts["exit_proc"] || "ExitProcess" dll_module = opts["dll_module"] || "kernel32" wait_time = opts["wait_time"] || 5 else wait_time = 5 exit_proc = "ExitProcess" dll_module = "kernel32" end count = 0 pids.each { |pid| raise TypeError unless pid.is_a?(Numeric) # Match spec, pid must be a number raise SystemCallError.new(22) if pid < 0 # Match spec, EINVAL if pid less than zero sigint = [Signal.list["INT"], "INT", "SIGINT", :INT, :SIGINT, 2] # Match the spec if pid == 0 && !sigint.include?(signal) raise SystemCallError.new(22) end if signal == 0 access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ elsif signal == 9 access = PROCESS_TERMINATE else access = PROCESS_ALL_ACCESS end begin handle = OpenProcess(access, 0, pid) if signal != 0 && handle == 0 raise SystemCallError, FFI.errno, "OpenProcess" end case signal when 0 if handle != 0 count += 1 else if FFI.errno == ERROR_ACCESS_DENIED count += 1 else raise SystemCallError.new(3) # ESRCH end end when Signal.list["INT"], "INT", "SIGINT", :INT, :SIGINT, 2 if GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid) count += 1 else raise SystemCallError.new("GenerateConsoleCtrlEvent", FFI.errno) end when Signal.list["BRK"], "BRK", "SIGBRK", :BRK, :SIGBRK, 3 if GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid) count += 1 else raise SystemCallError.new("GenerateConsoleCtrlEvent", FFI.errno) end when Signal.list["KILL"], "KILL", "SIGKILL", :KILL, :SIGKILL, 9 if TerminateProcess(handle, pid) count += 1 else raise SystemCallError.new("TerminateProcess", FFI.errno) end else thread_id = FFI::MemoryPointer.new(:ulong) mod = GetModuleHandle(dll_module) if mod == 0 raise SystemCallError.new("GetModuleHandle: '#{dll_module}'", FFI.errno) end proc_addr = GetProcAddress(mod, exit_proc) if proc_addr == 0 raise SystemCallError.new("GetProcAddress: '#{exit_proc}'", FFI.errno) end thread = CreateRemoteThread(handle, nil, 0, proc_addr, nil, 0, thread_id) if thread > 0 WaitForSingleObject(thread, wait_time) count += 1 else raise SystemCallError.new("CreateRemoteThread", FFI.errno) end end ensure CloseHandle(handle) if handle end } count end
def setpriority(kind, int, int_priority)
* Process::ABOVE_NORMAL_PRIORITY_CLASS
* Process::BELOW_NORMAL_PRIORITY_CLASS
* Process::REALTIME_PRIORITY_CLASS
* Process::HIGH_PRIORITY_CLASS
* Process::IDLE_PRIORITY_CLASS
* Process::NORMAL_PRIORITY_CLASS
Possible +int_priority+ values are:
information, so it is effectively always Process::PRIO_PROCESS.
You can only retrieve process information, not process group or user
The +kind+ parameter is ignored but present for API compatibility.
Sets the priority class for the specified process id +int+.
def setpriority(kind, int, int_priority) raise TypeError unless kind.is_a?(Integer) # Match spec raise TypeError unless int.is_a?(Integer) # Match spec raise TypeError unless int_priority.is_a?(Integer) # Match spec int = Process.pid if int == 0 # Match spec handle = OpenProcess(PROCESS_SET_INFORMATION, 0 , int) if handle == 0 raise SystemCallError, FFI.errno, "OpenProcess" end begin unless SetPriorityClass(handle, int_priority) raise SystemCallError, FFI.errno, "SetPriorityClass" end ensure CloseHandle(handle) end 0 # Match the spec end
def setrlimit(resource, current_limit, max_limit = nil)
the interpreter. Consider this method experimental.
WARNING: Exceeding the limit you set with this method could segfault
Process.getrlimit(Process::RLIMIT_VMEM) # => [4096, 4096]
Process.setrlimit(Process::RLIMIT_VMEM, 1024 * 4) # => nil
Example:
It is always set to the current_limit value.
The +max_limit+ parameter is provided for interface compatibility only.
refers to the per process user time limit.
all refer to the Process memory limit. The Process::RLIMIT_CPU constant
The Process:RLIMIT_AS, Process::RLIMIT_RSS and Process::VMEM constants
Process::RLIMIT_VMEM
Process::RLIMIT_RSS
Process::RLIMIT_AS
Process::RLIMIT_CPU
of flags are supported.
Sets the resource limit of the current process. Only a limited number
def setrlimit(resource, current_limit, max_limit = nil) max_limit = current_limit handle = nil in_job = Process.job? unless [RLIMIT_AS, RLIMIT_VMEM, RLIMIT_RSS, RLIMIT_CPU].include?(resource) raise ArgumentError, "unsupported resource type: '#{resource}'" end # Put the current process in a job if it's not already in one if in_job && defined? @win32_process_job_name handle = OpenJobObjectA(JOB_OBJECT_SET_ATTRIBUTES, 1, @win32_process_job_name) raise SystemCallError, FFI.errno, "OpenJobObject" if handle == 0 else @win32_process_job_name = "ruby_" + Process.pid.to_s handle = CreateJobObjectA(nil, @win32_process_job_name) raise SystemCallError, FFI.errno, "CreateJobObject" if handle == 0 end begin unless in_job unless AssignProcessToJobObject(handle, GetCurrentProcess()) raise SystemCallError, FFI.errno, "AssignProcessToJobObject" end end ptr = JOBJECT_EXTENDED_LIMIT_INFORMATION.new # Set the LimitFlags and relevant members of the struct if resource == RLIMIT_CPU ptr[:BasicLimitInformation][:LimitFlags] = JOB_OBJECT_LIMIT_PROCESS_TIME ptr[:BasicLimitInformation][:PerProcessUserTimeLimit][:QuadPart] = max_limit else ptr[:BasicLimitInformation][:LimitFlags] = JOB_OBJECT_LIMIT_PROCESS_MEMORY ptr[:ProcessMemoryLimit] = max_limit end bool = SetInformationJobObject( handle, JobObjectExtendedLimitInformation, ptr, ptr.size ) unless bool raise SystemCallError, FFI.errno, "SetInformationJobObject" end ensure at_exit { CloseHandle(handle) if handle } end end
def snapshot(info_type = "thread")
p Process.snapshot(:process).keys
# Show pids of all running processes
p Process.snapshot(:heap)[Process.pid]
# Get heap info for just the current process
p Process.snapshot(:module)[Process.pid]
# Get module info for just the current process
}
p v
puts "PID: #{pid}"
Process.snapshot.each{ |pid, v|
# Get all thread info
Example:
to you to filter by pid if you wish.
If no argument is provided, then :thread is assumed. Note that it is up
:process => ProcessSnapInfo[:process_id, :threads, :parent_process_id, :priority, :flags, :path]
:module => ModuleSnapInfo[:process_id, :address, :module_size, :handle, :name, :path]
:heap => HeapSnapInfo[:address, :block_size, :flags, :process_id, :heap_id]
:thread => ThreadSnapInfo[:thread_id, :process_id, :base_priority]
type of information they each return is as follows:
+info_type+ parameter. The possible values for +info_type+, and the
that key. The type of information in that array depends on the
with the pid as the key, and an array of information as the value of
Returns a list of process information structs in the form of a hash,
def snapshot(info_type = "thread") case info_type.to_s.downcase when "thread" flag = TH32CS_SNAPTHREAD when "heap" flag = TH32CS_SNAPHEAPLIST when "module" flag = TH32CS_SNAPMODULE when "process" flag = TH32CS_SNAPPROCESS else raise ArgumentError, "info_type '#{info_type}' unsupported" end begin handle = CreateToolhelp32Snapshot(flag, Process.pid) if handle == INVALID_HANDLE_VALUE raise SystemCallError.new("CreateToolhelp32Snapshot", FFI.errno) end case info_type.to_s.downcase when "thread" array = get_thread_info(handle) when "heap" array = get_heap_info(handle) when "module" array = get_module_info(handle) when "process" array = get_process_info(handle) end array ensure CloseHandle(handle) if handle end end
def uid(sid = false)
The Process.uid method in core Ruby always returns 0 on MS Windows.
--
numeric id is returned (the default).
If +sid+ is set to true, then a binary sid is returned. Otherwise, a
RID of the SID associated with the owner of the process.
Returns the uid of the current process. Specifically, it returns the
def uid(sid = false) token = FFI::MemoryPointer.new(:ulong) raise TypeError unless sid.is_a?(TrueClass) || sid.is_a?(FalseClass) unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token) raise SystemCallError, FFI.errno, "OpenProcessToken" end token = token.read_ulong rlength = FFI::MemoryPointer.new(:ulong) tuser = 0.chr * 512 bool = GetTokenInformation( token, TokenUser, tuser, tuser.size, rlength ) unless bool raise SystemCallError, FFI.errno, "GetTokenInformation" end string_sid = tuser[FFI.type_size(:pointer) * 2, (rlength.read_ulong - FFI.type_size(:pointer) * 2)] if sid string_sid else psid = FFI::MemoryPointer.new(:uintptr_t) unless ConvertSidToStringSidA(string_sid, psid) raise SystemCallError, FFI.errno, "ConvertSidToStringSid" end psid.read_pointer.read_string.split("-").last.to_i end end
def volume_type
def volume_type buf = FFI::MemoryPointer.new(:char, 32) bool = GetVolumeInformationA(nil, nil, 0, nil, nil, nil, buf, buf.size) bool ? buf.read_string : nil end
def windows_version
def windows_version ver = OSVERSIONINFO.new ver[:dwOSVersionInfoSize] = ver.size unless GetVersionExA(ver) raise SystemCallError.new("GetVersionEx", FFI.errno) end ver[:dwMajorVersion] end