Skip to content

Commit e6dad5f

Browse files
committed
First commit
0 parents  commit e6dad5f

4 files changed

Lines changed: 273 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/__pycache__

linux_rlimit.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# -*- coding: utf-8 -*-
2+
3+
#
4+
# Roland Pihlakas, 2018
5+
# roland@simplify.ee
6+
#
7+
8+
9+
# Set memory limits for current process, to specified 'size' in bytes
10+
def set_mem_limits(data_size, address_space_size):
11+
12+
13+
import resource
14+
15+
16+
rlimits = [
17+
(resource.RLIMIT_DATA, (int(data_size), int(data_size))) if data_size else None,
18+
(resource.RLIMIT_AS, (int(address_space_size), int(address_space_size))) if address_space_size else None, # The maximum area (in bytes) of address space which may be taken by the process. # includes memory that is from shared libraries - https://stackoverflow.com/questions/7880784/what-is-rss-and-vsz-in-linux-memory-management
19+
20+
# (resource.RLIMIT_RSS, (int(mem_limit), int(mem_limit))), # The maximum resident set size that should be made available to the process. # Ignored in Linux 2.4.30 and higher - https://superuser.com/questions/239796/limits-conf-to-set-memory-limits
21+
22+
# (resource.RLIMIT_STACK, (int(8 * 1024 * 1024), int(8 * 1024 * 1024))),
23+
# (resource.RLIMIT_MEMLOCK, (int(64 * 1024), int(64 * 1024))), # The maximum address space which may be locked in memory. # 64kB is the default soft and hard limit
24+
# (resource.RLIMIT_MSGQUEUE, (0, 0)), # The number of bytes that can be allocated for POSIX message queues.)
25+
# (resource.RLIMIT_SIGPENDING, (0, 0)),
26+
# (resource.RLIMIT_NICE, (10, 10)), # -20 is the highest priority and 19 is the lowest priority
27+
# (resource.RLIMIT_SBSIZE, (0, 0)), # The maximum size (in bytes) of socket buffer usage for this user. This limits the amount of network memory, and hence the amount of mbufs, that this user may hold at any time. # Availability: FreeBSD 9 or later.
28+
# (resource.RLIMIT_CPU, (cpu_timelimit, cpu_timelimit)),
29+
# (resource.RLIMIT_NPROC, (0, 0)), # disable subprocess creation
30+
31+
# (resource.RLIMIT_FSIZE, (0, 0)), # disable file writing
32+
# (resource.RLIMIT_NOFILE, (16, 16)), # The maximum number of open file descriptors for the current process. # 1024 is the default soft limit, so lets make it a hard limit
33+
# (resource.RLIMIT_CORE, (0, 0)), # The maximum size (in bytes) of a core file that the current process can create.
34+
]
35+
36+
37+
for rlimit in rlimits:
38+
if rlimit: # not None
39+
try:
40+
resource.setrlimit(rlimit[0], rlimit[1])
41+
except Exception as msg:
42+
print(msg)
43+
pass
44+
45+
46+
#/ def set_mem_limits(data_size, address_space_size):

main.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# -*- coding: utf-8 -*-
2+
3+
4+
5+
6+
if __name__ == "__main__": # subprocesses are automatically included in the aggregated accounting of the limits set to the main process
7+
8+
if (1 == 1): # prevent system hangs
9+
10+
try:
11+
12+
import os
13+
14+
if os.name == 'nt':
15+
16+
from windows_jobobject import set_mem_commit_limit
17+
18+
mem_limit = 4 * 1024 * 1024 * 1024
19+
min_free_swap = 8 * 1024 * 1024 * 1024
20+
21+
set_mem_commit_limit(os.getpid(), mem_limit, min_free_swap)
22+
23+
else: #/ if os.name == 'nt':
24+
25+
from linux_rlimit import set_mem_limits
26+
27+
data_size_limit = 512 * 1024 * 1024
28+
address_space_size_limit = 1024 * 1024 * 1024
29+
30+
set_mem_limits(data_size_limit, address_space_size_limit)
31+
32+
#/ if os.name == 'nt':
33+
34+
except Exception as msg:
35+
print(msg)
36+
pass
37+
38+
#/ if (1 == 1):
39+
40+
#/ if __name__ == "__main__":
41+
42+
43+
44+
45+
46+
# unleash your dragons here
47+
48+
print("hello")
49+
50+
51+
52+

windows_jobobject.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# -*- coding: utf-8 -*-
2+
3+
#
4+
# Roland Pihlakas, 2017
5+
# roland@simplify.ee
6+
#
7+
8+
9+
# https://stackoverflow.com/questions/16779497/how-to-set-memory-limit-for-thread-or-process-in-python
10+
11+
import ctypes
12+
import os
13+
import psutil
14+
15+
16+
17+
PROCESS_SET_QUOTA = 0x100
18+
PROCESS_TERMINATE = 0x1
19+
JobObjectExtendedLimitInformation = 9
20+
21+
22+
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms684147(v=vs.85).aspx
23+
24+
JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100
25+
JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200
26+
27+
JOB_OBJECT_LIMIT_WORKINGSET = 0x00000001
28+
29+
JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x00000040 # Preserves any job time limits you previously set. As long as this flag is set, you can establish a per-job time limit once, then alter other limits in subsequent calls. This flag cannot be used with JOB_OBJECT_LIMIT_JOB_TIME.
30+
JOB_OBJECT_LIMIT_JOB_TIME = 0x00000004
31+
JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002
32+
33+
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000
34+
35+
36+
# https://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
37+
JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x00000008
38+
JOB_OBJECT_LIMIT_AFFINITY = 0x00000010
39+
JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
40+
JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400
41+
JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200
42+
JOB_OBJECT_LIMIT_JOB_TIME = 0x00000004
43+
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000
44+
JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x00000040
45+
JOB_OBJECT_LIMIT_PRIORITY_CLASS = 0x00000020
46+
JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100
47+
JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002
48+
JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 0x00000080
49+
JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000
50+
JOB_OBJECT_LIMIT_WORKINGSET = 0x00000001
51+
52+
53+
class IO_COUNTERS(ctypes.Structure):
54+
_fields_ = [('ReadOperationCount', ctypes.c_uint64),
55+
('WriteOperationCount', ctypes.c_uint64),
56+
('OtherOperationCount', ctypes.c_uint64),
57+
('ReadTransferCount', ctypes.c_uint64),
58+
('WriteTransferCount', ctypes.c_uint64),
59+
('OtherTransferCount', ctypes.c_uint64)]
60+
61+
62+
class JOBOBJECT_BASIC_LIMIT_INFORMATION(ctypes.Structure):
63+
_fields_ = [('PerProcessUserTimeLimit', ctypes.c_int64),
64+
('PerJobUserTimeLimit', ctypes.c_int64),
65+
('LimitFlags', ctypes.c_uint32),
66+
('MinimumWorkingSetSize', ctypes.c_void_p),
67+
('MaximumWorkingSetSize', ctypes.c_void_p),
68+
('ActiveProcessLimit', ctypes.c_uint32),
69+
('Affinity', ctypes.c_void_p),
70+
('PriorityClass', ctypes.c_uint32),
71+
('SchedulingClass', ctypes.c_uint32)]
72+
73+
74+
class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(ctypes.Structure):
75+
_fields_ = [('BasicLimitInformation', JOBOBJECT_BASIC_LIMIT_INFORMATION),
76+
('IoInfo', IO_COUNTERS),
77+
('ProcessMemoryLimit', ctypes.c_void_p),
78+
('JobMemoryLimit', ctypes.c_void_p),
79+
('PeakProcessMemoryUsed', ctypes.c_void_p),
80+
('PeakJobMemoryUsed', ctypes.c_void_p)]
81+
82+
## https://msdn.microsoft.com/en-us/library/windows/desktop/hh448384(v=vs.85).aspx
83+
#class JOBOBJECT_CPU_RATE_CONTROL_INFORMATION(ctypes.Structure):
84+
# _fields_ = [('BasicLimitInformation', JOBOBJECT_BASIC_LIMIT_INFORMATION),
85+
# ('IoInfo', IO_COUNTERS),
86+
# ('ProcessMemoryLimit', ctypes.c_void_p),
87+
# ('JobMemoryLimit', ctypes.c_void_p),
88+
# ('PeakProcessMemoryUsed', ctypes.c_void_p),
89+
# ('PeakJobMemoryUsed', ctypes.c_void_p)]
90+
91+
92+
93+
# Set memory limit for process with specfied 'pid', to specified 'size' in bytes
94+
def set_mem_commit_limit(pid, size, min_free_swap = None, retry_count = 10):
95+
96+
for i in range(0, retry_count): # assigning to job fails sometimes
97+
try:
98+
if (set_mem_commit_limit_worker(pid, size, min_free_swap)):
99+
break
100+
except Exception as msg: # possibly already assigned to job object? (this can be done only once)
101+
print(msg)
102+
pass
103+
104+
#/ def set_mem_commit_limit(pid, size, min_free_swap, retry_count):
105+
106+
107+
def set_mem_commit_limit_worker(pid, size, min_free_swap = None):
108+
109+
110+
if min_free_swap:
111+
swap_status = psutil.swap_memory()
112+
max_commit_limit = swap_status.free - min_free_swap
113+
size = min(size, max_commit_limit) # NB!
114+
115+
116+
job_info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION()
117+
out_size = ctypes.c_uint32()
118+
119+
120+
# child processes inherit the job object so it is not necessary to check for the parent
121+
main_pid = os.getpid() # if __name__ == '__main__' else os.getppid()
122+
123+
124+
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms682409(v=vs.85).aspx
125+
# If the object existed before the function call, the function returns a handle to the existing job object and GetLastError returns ERROR_ALREADY_EXISTS.
126+
job = ctypes.windll.kernel32.CreateJobObjectW(None, None) # NB! CreateJobObjectW not CreateJobObjectA
127+
# assert job != 0
128+
129+
success = ctypes.windll.kernel32.QueryInformationJobObject(
130+
job,
131+
JobObjectExtendedLimitInformation,
132+
ctypes.POINTER(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)(job_info),
133+
ctypes.sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION),
134+
ctypes.POINTER(ctypes.c_uint32)(out_size)
135+
)
136+
# assert success
137+
138+
139+
#job_info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PROCESS_MEMORY | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
140+
#job_info.ProcessMemoryLimit = size
141+
142+
job_info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_JOB_MEMORY | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
143+
job_info.JobMemoryLimit = size
144+
145+
# job_info.MaximumWorkingSetSize = size
146+
147+
148+
success = ctypes.windll.kernel32.SetInformationJobObject(
149+
job,
150+
JobObjectExtendedLimitInformation,
151+
ctypes.POINTER(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)(job_info),
152+
ctypes.sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)
153+
)
154+
# assert success
155+
156+
process = ctypes.windll.kernel32.OpenProcess(
157+
PROCESS_SET_QUOTA | PROCESS_TERMINATE,
158+
False, pid
159+
)
160+
# assert process != 0
161+
162+
success_assign = ctypes.windll.kernel32.AssignProcessToJobObject(job, process)
163+
# assert success
164+
165+
#if __name__ != '__main__': # NB! due to JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag
166+
# success = ctypes.windll.kernel32.CloseHandle(job)
167+
# # assert success
168+
169+
success = ctypes.windll.kernel32.CloseHandle(process)
170+
# assert success
171+
172+
return success_assign
173+
174+
#/ def set_mem_commit_limit(pid, size, min_free_swap):

0 commit comments

Comments
 (0)