Process information and resources

os · Advanced · 9 min

Sizing worker pools, writing PID files and spawning commands correctly all rely on these calls.

Every process has an ID and a parent: os.getpid() and os.getppid(). Daemons write the PID to a file so other tools can signal them, and log lines often include it to tell workers apart.

CPU counts are subtle:

  • os.cpu_count() reports the CPUs in the machine.
  • os.process_cpu_count() (new in 3.13) reports the CPUs this process may actually use, which is smaller when the process is pinned with CPU affinity or containers restrict it. Prefer it when sizing a worker pool. Both may return None.
  • Python 3.13 also adds the -X cpu_count=N option and PYTHON_CPU_COUNT variable to override both values.

On Unix, os.getuid() returns the user ID (0 for root), os.getloadavg() the load averages, and os.nice(n) lowers the process priority.

To run another program, don't use `os.system`: it passes a string to the shell, which invites injection bugs when arguments contain user input, and returns an encoded wait status rather than a plain exit code. os.popen has the same shell problem. Use subprocess.run([...]) with a list of arguments instead; it gives you the exit code, output capture and timeouts.

os.kill(pid, signal.SIGTERM) sends a signal to another process; os.kill(pid, 0) merely checks the process exists and you may signal it.

Example

import os
import subprocess
import sys

print(os.getpid() > 0)                     # True
n = os.process_cpu_count() or 1            # new in 3.13
print(n <= (os.cpu_count() or n))          # True
workers = max(1, n - 1)

# os.system: shell string, returns a wait status
status = os.system("exit 3")
print(os.waitstatus_to_exitcode(status))   # 3

# subprocess: argument list, real return code, no shell parsing
r = subprocess.run([sys.executable, "-c", "print('hi')"],
                   capture_output=True, text=True)
print(r.returncode, r.stdout.strip())      # 0 hi

Remember

  • os.getpid/os.getppid identify the process and its parent
  • Size pools with os.process_cpu_count() (3.13), which respects affinity limits
  • Prefer subprocess.run([...]) over os.system and os.popen

Watch out for

  • Building an os.system command string from user input; a value like x; rm -rf ~ becomes shell code.

Check yourself

  1. A container limits a process to 2 of the host's 32 CPUs via affinity. Which call reports 2?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/os.html#os.process_cpu_count