I recently needed a program to test and explore some Linux NFS client behavior (namely, our recent NFS issue ). Because this behavior depended on what user-level operations the kernel saw, I needed to be very specific about what system calls my test setup made, in what order, and so on. I also wanted something that I could rapidly put together and easily revise and alter for experiments, to see just what sequence of (system call) operations were necessary to cause our issues. In a way the obvious language to write this in would be C, but instead I immediately turned to Python.
Beyond the speed of writing things in Python, the obvious advantage of Python here is that the
os
module provides more or less direct access to all of the system calls I wanted (ultimately mixed with the
fcntl
module in order to get
flock()
). Although Python normally works with file objects , which are abstracted, the
os
module gives you almost raw access to Unix file descriptors and the common operations on them, which map closely to system calls.
That latter bit is important, and leads to the subtle thing. Although the
os
module's documentation doesn't quite promise it directly, the operations it exposes translate almost completely directly to Unix system calls, and CPython's interpreter runtime doesn't alter them or add others intermixed into them (well, not others related to the files and so on that you're working on; it may do operations like request more memory, although probably not for simple test code). This means that you can write a fair amount of code using the
os
module (and
fcntl
, and a few others) that deal with raw Unix file descriptors (fds) and be pretty confident that Python is doing exactly what you asked it to and nothing else.
This is something you get with C, of course, but it's not something you can always say about other language runtimes. For test programs like what I needed, it can be a quite handy sort of behavior. I already knew CPython behaved like this from previous work, which is why I was willing to immediately turn to it for my test program here.
(If you're sufficiently cautious, you'll want to verify the behavior with a system call tracer, such as
strace
on Linux. If you do, it becomes very useful that the CPython runtime makes relatively few system calls that you didn't ask it to make, so it's easy to find and follow the system calls produced by your test code. Again, some language runtime environments are different here; they may have a churn of their own system calls that are used to maintain background activities, which clutter up
strace
output and so on.)