Unpacking the Core of Operating Systems: From System Calls to Threads

Understanding Threads: The Core Unit of Execution in a Process | by  Shadeshsaha | Medium

It’s easy to take for granted the magic that happens when you click a button, type a command, or even just open a file. But behind that seamless experience lies a complex world of operating system concepts, and diving into them can be surprisingly illuminating.

Consider the fundamental components of the structure. We’ve got system calls and library functions. You can think of the library functions as useful, system-provided tools that you can pick up and use—think printf for printing text. System calls, on the other hand, are the direct line to the operating system’s core services, the way your applications ask the kernel to do things like exit when a program is done. Like asking a librarian for a book versus asking the librarian to go get it from the archives, this is a crucial distinction. When we talk about files, the concept of a Virtual File System (VFS) is pretty neat.

It acts as a universal translator for the operating system, allowing different file systems – whether they’re on your hard drive, a USB stick, or even a network share – to speak the same language. This means your open(), read(), and write() commands don’t need to know the nitty-gritty details of how each specific storage medium works. The unnoticed hero that ensures that your system’s file management remains consistent There are also procedures. An operating system-managed dynamic entity, a process is essentially a running program. It’s more than just the code; it includes all the data and resources it needs to run. It is essential to distinguish between a dynamic process and a static program. A program is like a recipe, while a process is the actual act of cooking that recipe, with all the ingredients and the kitchen in use.

When a process needs to create another, fork() and vfork() come into play. fork() creates a near-identical copy of the parent process, each with its own memory space. vfork(), however, is a bit more efficient for certain scenarios; the child process actually runs within the parent’s address space until it calls exec() or exit(), meaning they share memory temporarily. Their approaches to resource management differ in a subtle but significant manner. We also come across concepts like zombie processes and orphans. An orphan process is one whose parent has died before it. The system, in its wisdom, assigns it to the init process (PID 1) to ensure its resources are eventually cleaned up. On the other hand, a zombie process is a little bit sadder because it has completed its execution but its parent has not yet collected its exit status, leaving its kernel resources untouched. Killing the parent process can often resolve this, turning a zombie into an orphan that init can then adopt.

It is essential to communicate between these processes. Signals, pipes—named and unnamed—semaphores, shared memory, message queues, and sockets are all present in our system. Unnamed pipes, created with pipe(), are simple, temporary, and only work between related processes. Named pipes, which are made with mkfifo(), have a longer lifespan and can be accessed by processes that aren’t related to one another, just like a dedicated channel. Threads, often described as lightweight processes, are the smallest units of execution within a process. They share the same address space, making communication between them much faster and less resource-intensive than inter-process communication. However, this shared nature also means that if one thread crashes, the entire process can go down with it, unlike with processes which are more isolated.

Understanding these fundamental concepts – from the basic system calls and file system abstractions to the intricate dance of processes, threads, and their communication mechanisms – gives us a deeper appreciation for the invisible machinery that powers our digital lives.