Skip to main content

Modules/posixmodule.c (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/posixmodule.c

This annotation covers the process management and low-level file descriptor APIs. See modules_posix_detail for the file path and stat functions.

Map

LinesSymbolRole
1-300os.fork, os.fork1Process creation
301-600os.execve, os.execl, os.execvpeReplace current process
601-900os.waitpid, os.wait, os.waitstatus_to_exitcodeWait for child process
901-1200os.pipe, os.pipe2, os.dup, os.dup2File descriptor operations
1201-1500os.read, os.write, os.closeLow-level I/O
1501-1800os.open, os.lseek, os.fstatFile descriptor access
1801-2200os.scandir, DirEntryDirectory traversal with stat caching

Reading

os.fork

// CPython: Modules/posixmodule.c:3820 os_fork_impl
static PyObject *
os_fork_impl(PyObject *module)
{
pid_t pid;
PyOS_BeforeFork();
pid = fork();
if (pid == 0) {
/* Child: reset state */
PyOS_AfterFork_Child();
} else {
PyOS_AfterFork_Parent();
}
...
return PyLong_FromPid(pid);
}

PyOS_BeforeFork acquires the import lock and other locks to prevent deadlock in the child. PyOS_AfterFork_Child resets the GIL, reinitializes random state, and flushes file buffers.

os.execve

// CPython: Modules/posixmodule.c:3920 os_execve_impl
static PyObject *
os_execve_impl(PyObject *module, path_t path, PyObject *argv, PyObject *env)
{
char **argvlist = parse_argv(argv);
char **envlist = parse_envlist(env);
...
execve(path.narrow, argvlist, envlist);
/* If we get here, execve failed */
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path.object);
return NULL;
}

execve replaces the current process image. On success it never returns.

os.scandir

// CPython: Modules/posixmodule.c:10800 ScandirIterator
typedef struct {
PyObject_HEAD
path_t path;
DIR *dirp;
PyObject *stat_cache; /* DirEntry stat cache */
} ScandirIterator;

os.scandir() returns a ScandirIterator that yields DirEntry objects. Each DirEntry caches the d_type from dirent to allow is_file()/is_dir() without a stat() call on most filesystems.

DirEntry.stat

// CPython: Modules/posixmodule.c:10620 DirEntry_stat
static PyObject *
DirEntry_stat(DirEntry *self, PyObject *args, PyObject *kwargs)
{
if (!self->stat) {
/* Call os.stat on the path */
self->stat = _PyObject_CallMethod(module, &_Py_ID(stat), "Os",
self->path, "follow_symlinks", follow_symlinks);
}
return Py_NewRef(self->stat);
}

stat is cached on the DirEntry object. The first call does a system call; subsequent calls return the cached result.

gopy notes

Process operations (fork, execve, waitpid) are needed for subprocess. In gopy these map to syscall.Fork, syscall.Exec, syscall.Wait4. os.pipe maps to syscall.Pipe. os.scandir wraps Go's os.ReadDir with a DirEntry type that caches FileInfo. os.dup2 is needed for subprocess pipe setup.