T Theory — conceptual understanding
P Practical — reasoning & tradeoffs
C Coding — write a working C program
Contents
| 1 | Unix System Basics |
| 2 | File Descriptors |
| 3 | Standard I/O vs Unbuffered I/O |
| 4 | File Offsets and `lseek` |
| 5 | File Types |
| 6 | File Permissions |
| 7 | `umask` and File Creation |
| 8 | Hard Links and Symbolic Links |
| 9 | Directories |
| 10 | Process Creation with `fork()` |
| 11 | Parent and Child Processes |
| 12 | `wait()` and Process Termination |
| 13 | `exec` Family |
| 14 | Process Environment |
| 15 | Exit Handlers (`atexit`) |
| 16 | Signals Introduction |
| 17 | Signal Handling |
| 18 | Signal Masks |
| 19 | Pipes |
| 20 | Redirection with `dup` and `dup2` |
| 21 | Combining `fork`, `pipe`, and `exec` |
| 22 | FIFOs (Named Pipes) |
| 23 | Process Groups and Sessions |
| 24 | Daemon Processes |
| 25 | Nonblocking I/O and `fcntl()` |
| 26 | Threads Introduction |
| 27 | Thread Synchronization — Mutex |
| 28 | Real UID, Effective UID, and `setuid` |
| 29 | `stat()` and `struct stat` Deep Dive |
| 30 | `errno`, `perror`, and `strerror` |
| 31 | `exit()`, `_exit()`, and `_Exit()` |
| 32 | Orphan Processes and `SIGCHLD` |
| 33 | Controlling Terminal |
| 34 | `getcwd()` and Working Directory |
| 35 | System Logging with `syslog` |
| 36 | Advisory File Locking |
| 37 | Detached Threads |
| 38 | Condition Variables |
| 39 | Memory Management and Memory Leaks |
| 40 | `O_APPEND` and Atomic Writes |
| 41 | Absolute vs Relative Paths and `realpath` |
| 42 | Safe Temporary Files with `mkstemp` |
| 43 | `SIGALRM`, `alarm()`, and `sigsuspend()` |
| 44 | `fread` / `fwrite` vs `read` / `write` |
| 45 | `open()` Flags Deep Dive |
| 46 | `waitpid()` vs `wait()` |
| 47 | Mini Shell Implementation |
| 48 | `mmap()` — Memory-Mapped Files |
| 49 | `alarm()` and Interval Timers |
| 50 | `popen()` and `pclose()` |
| 51 | POSIX Shared Memory |
| 52 | POSIX Semaphores |
| 53 | Thread-Specific Data |
| 54 | Read-Write Locks |
| 55 | `getrlimit()` and `setrlimit()` |
| 56 | `sysconf()` and `pathconf()` |
| 57 | `sigqueue()` and Real-Time Signals |
| 58 | Process Times with `times()` and `clock_gettime()` |
| 59 | Filesystem Statistics with `statvfs()` |
| 60 | Scatter-Gather I/O with `readv()` and `writev()` |
| 61 | `truncate()` and `ftruncate()` |
| 62 | `execve()` Internals and Environment Manipulation |
| 63 | `dup()` and `dup2()` Deep Dive |
| 64 | System Data Files: Password, Group, and Login |
| 65 | `chmod()`, `chown()`, and Permission Modification |
| 66 | Socket Basics and Unix Domain Sockets |
| 1. |
T |
Explain the role of the operating system kernel in Unix. What is the boundary between kernel space and user space, and why does that boundary exist? |
| 2. |
T |
What is the difference between a system call and a standard C library function? Why do library functions like printf sometimes make system calls internally, and sometimes do not? |
| 3. |
P |
A programmer writes a loop calling strlen() one million times on the same string. Another programmer argues this is "making one million system calls." Is that correct? Explain the distinction. |
| 4. |
P |
A process runs as an ordinary user and tries to open /etc/shadow for reading. The kernel denies the request. At which layer does this enforcement happen — library, kernel, or both? Explain. |
| 5. |
C |
Write a C program that prints: the process ID, the parent process ID, the real user ID, and the real group ID. Use getpid(), getppid(), getuid(), and getgid(). |
| 6. |
C |
Write a C program that intentionally calls a system call with an invalid argument (for example, read(-1, buf, 10)), checks the return value, and prints a descriptive error message using perror(). |
| 1. |
T |
What is a file descriptor? Describe the three kernel data structures that back an open file descriptor: the per-process file descriptor table, the system-wide open-file table, and the inode table. What does each track? |
| 2. |
T |
What are file descriptors 0, 1, and 2? Where are they conventionally opened, and what happens if a process closes descriptor 0 and then calls open()? |
| 3. |
P |
A process opens the same file twice with two separate calls to open(). It then writes 5 bytes through the first descriptor and 5 bytes through the second. Do the two writes go to the same offset or different offsets? Explain using the kernel data structure model. |
| 4. |
P |
A process calls fork() after opening a file. Both parent and child write to the file using the inherited descriptor. Explain whether the file offset is shared or independent, and what race condition this can create. |
| 5. |
C |
Write a C program that opens a file named input.txt, reads its entire content in a loop using read() with a fixed buffer, and writes each chunk to standard output using write(). Handle short reads and short writes correctly. |
| 6. |
C |
Write a C program that opens the same file twice, writes the string "first\n" through the first descriptor and "second\n" through the second, then closes both and prints the file contents. Observe and explain the resulting file content. |
| 1. |
T |
Explain the three kinds of buffering used by the standard I/O library: fully buffered, line buffered, and unbuffered. Which streams default to which kind, and why? |
| 2. |
T |
What is the difference between the FILE * abstraction provided by standard I/O and the integer file descriptor provided by the kernel? How does fileno() relate them? |
| 3. |
P |
A program writes to standard output using printf() and then immediately calls write(STDOUT_FILENO, ...). Under what conditions can the write() output appear before the printf() output in the terminal, even though printf() was called first? |
| 4. |
P |
A program opens a file with fopen() and writes data with fwrite(). The program then crashes (e.g., via abort()). Is the data guaranteed to be in the file? Explain what fflush() and fclose() do and why the crash matters. |
| 5. |
C |
Write a program that copies one file to another using only open(), read(), write(), and close() — no standard I/O functions. Accept source and destination filenames as command-line arguments. Use a buffer of 4096 bytes and handle all error cases. |
| 6. |
C |
Write a program that opens a file with fopen(), writes several lines with fprintf(), explicitly calls fflush(), and then uses write() on fileno() of the same stream to append a final line. Explain the expected result. |
| 1. |
T |
What is a file offset? How does the kernel maintain it, and how is it updated after read() and write() calls? Who owns the offset — the file, the open-file table entry, or the descriptor? |
| 2. |
T |
What is a sparse file? How can lseek() create one, and what do the "holes" contain when read back? |
| 3. |
P |
A program opens a file containing 100 bytes and calls lseek(fd, 0, SEEK_END) followed immediately by write(fd, buf, 10). Where in the file do the 10 bytes land? Now suppose the file was empty — what happens? |
| 4. |
P |
Two processes independently open the same file and each calls lseek(fd, 0, SEEK_END) followed by write(). Is this a safe way to append to a file from multiple processes? Explain the race condition and how O_APPEND addresses it. |
| 5. |
C |
Write a program that opens a file given as a command-line argument, uses lseek(fd, 0, SEEK_END) to find and print the file size in bytes, then closes the file. Do not use stat(). |
| 6. |
C |
Write a program that creates a sparse file: open a new file, seek 1 MB ahead with lseek(), write a single byte, and close. Print the reported file size. Then open the file and read the byte at offset 0 — print its value and explain where it came from. |
| 1. |
T |
Describe all seven Unix file types: regular file, directory, symbolic link, character special, block special, FIFO (named pipe), and socket. Give one real-world example of each. |
| 2. |
T |
How does the kernel store the file type? Which field in struct stat contains it, and what macros are used to test the type? |
| 3. |
P |
A program calls stat() on a symbolic link. Does it get information about the link itself or about the target file? What call should be used to get information about the link itself? What happens if the target does not exist? |
| 4. |
P |
Why can you not easily determine whether a file is a compiled executable (ELF binary) by looking at the file type returned by stat()? What would you need to look at instead? |
| 5. |
C |
Write a program that receives a pathname as a command-line argument and prints what type of file it is: regular, directory, symbolic link, character device, block device, FIFO, socket, or unknown. Use lstat() and the S_IS* macros. |
| 6. |
C |
Write a program that walks through all entries in a directory given as an argument and for each entry prints its name and file type. Use opendir(), readdir(), and lstat(). |
| 1. |
T |
Explain the Unix permission model: owner, group, and others; read, write, and execute bits. How does the kernel decide which set of bits to apply when a process accesses a file? |
| 2. |
T |
What are the setuid, setgid, and sticky bits? Explain the purpose of each and give a practical example of when setuid on an executable is legitimately used. |
| 3. |
P |
What does read permission mean on a file versus read permission on a directory? What does execute permission mean on a file versus execute permission on a directory? Can you list a directory's contents without execute permission on it? |
| 4. |
P |
A file is owned by user alice with permissions rwx------. User bob belongs to the same primary group as alice. Can bob read the file? Explain exactly how the kernel walks the permission check sequence. |
| 5. |
C |
Write a program that receives a filename as a command-line argument, calls stat(), and prints the permission bits in human-readable form, for example: |
| |
Owner: read write execute
Group: read execute
Others: (none)
|
| 6. |
C |
Write a program that calls access() to check whether the calling process can read, write, and execute a file given on the command line, printing the result for each permission. Explain why access() uses real IDs rather than effective IDs. |
| 1. |
T |
What is umask? How does it interact with the mode argument passed to open() or creat()? Write the formula that gives the final permission bits. |
| 2. |
T |
Is umask inherited across fork() and exec()? What does this imply for child processes and daemon processes? |
| 3. |
P |
A program creates a file with mode 0666 and the current umask is 0027. What are the final permission bits? Show the bitwise calculation and express the result in symbolic form (e.g., rw-r-----). |
| 4. |
P |
A daemon wants to ensure that every file it creates has exactly the permissions it specifies, regardless of the user's shell umask. What one call should the daemon make at startup, and what value should it use? |
| 5. |
C |
Write a program that saves the current umask, sets it to 0, creates a file named full.txt with mode 0666, restores the original umask, creates a second file named masked.txt with mode 0666, and prints the actual permissions of both files using stat(). |
| 6. |
C |
Write a program that accepts a umask value (in octal) and a desired mode (in octal) as command-line arguments, applies the umask, creates a file, and prints the resulting permission bits. Demonstrate that umask only removes bits, never adds them. |
| 1. |
T |
Explain the difference between a hard link and a symbolic link at the inode level. What does each link contain, and what does the link count in the inode represent? |
| 2. |
T |
Why are hard links to directories normally forbidden for regular users? What specific filesystem consistency problem would they cause? |
| 3. |
P |
A file has two hard links named a and b. A program opens a for writing and writes new data. It then removes a with unlink(). Can the program still write to the open file descriptor? What happens to the data? When is the inode actually freed? |
| 4. |
P |
A symbolic link points to a file that is later deleted. What happens when a process tries to open() the symbolic link? What does lstat() return for the link in this situation? |
| 5. |
C |
Write a program that takes two command-line arguments — a source file and a link name — and creates both a hard link (using link()) and a symbolic link (using symlink()) to the source. Print the link count of the source file before and after the hard link using stat(). |
| 6. |
C |
Write a program that reads and prints the target of a symbolic link using readlink(). If the argument is not a symbolic link, print an appropriate message. Do not follow the link. |
| 1. |
T |
What is a directory in Unix at the filesystem level? What does it store internally, and what is the minimum link count of a newly created empty directory and why? |
| 2. |
T |
Explain the difference between . and .. in a directory. How does .. in the root directory behave? |
| 3. |
P |
Explain the difference between opendir() / readdir() / closedir() and the lower-level open() / getdents(). Why should most programs prefer the POSIX DIR * interface? |
| 4. |
P |
A program lists a directory with readdir() and deletes every file whose name ends with .tmp. Is it safe to call unlink() while the DIR * is still open? Are there any ordering guarantees on entries returned by readdir()? |
| 5. |
C |
Write a program that lists all entries in the current directory using opendir() and readdir(), printing each entry's name and whether it is a regular file, directory, or other. Use lstat() for type checking. |
| 6. |
C |
Write a program that counts the total number of regular files and subdirectories (non-recursively) in a directory given as a command-line argument, and prints both counts. |
| 1. |
T |
Describe exactly what happens when a process calls fork(). What is copied, what is shared, and what does each process receive as the return value? |
| 2. |
T |
What is copy-on-write (COW)? How does it improve fork() performance, and under what condition does the kernel actually copy a page? |
| 3. |
P |
After fork(), both parent and child have the same open file descriptors. The child closes one of those descriptors. Does this affect the parent's ability to use the same descriptor? Explain using the kernel file descriptor model. |
| 4. |
P |
A process has data buffered in a FILE * stream and calls fork() without calling fflush() first. Explain what can happen when both parent and child eventually call fclose() on their copy of the stream. |
| 5. |
C |
Write a program that calls fork(). The child prints "child PID=<pid> PPID=<ppid>" and the parent prints "parent PID=<pid> child_PID=<cpid>". Use getpid() and getppid(). The parent must wait for the child before exiting. |
| 6. |
C |
Write a program that forks five child processes in a loop. Each child prints its loop index and its PID, then exits. The parent waits for all five children and prints each one's exit status. |
| 1. |
T |
Explain the parent-child relationship in Unix process management. What resources does a child inherit from its parent at fork() time? List at least six. |
| 2. |
T |
What is an orphan process? What happens to it, and which process adopts it? What is the purpose of this adoption mechanism? |
| 3. |
P |
A parent forks a child and immediately calls exit() without calling wait(). The child is still running. What is the child's new parent? How can the child find out what happened? |
| 4. |
P |
After fork(), if both parent and child use printf() and the output is redirected to a file, the output can be duplicated. Explain why this happens and how to prevent it. |
| 5. |
C |
Write a program where the parent exits immediately after fork(). The child sleeps for 2 seconds and then calls getppid() to print its new parent's PID. Run it and confirm the child is adopted by init (or systemd). |
| 6. |
C |
Write a program that forks a child. The child prints the values of a variable that was set before the fork, modifies it, and prints the new value. The parent then prints the same variable to show that its copy was not affected. |
| 1. |
T |
Why must a parent process call wait() or waitpid() after a child exits? What is a zombie process, and what kernel resources does it continue to occupy? |
| 2. |
T |
What macros are used to inspect the termination status returned by wait()? Describe WIFEXITED, WEXITSTATUS, WIFSIGNALED, and WTERMSIG. |
| 3. |
P |
A parent process creates 10 children and never calls wait(). The parent then enters an infinite loop. What do the 10 exited children become? What is the system-level impact of many zombie processes? |
| 4. |
P |
How can a parent avoid zombie processes without blocking in wait()? Describe two approaches: using SIGCHLD with waitpid(-1, ..., WNOHANG) and the double-fork technique. |
| 5. |
C |
Write a program that forks a child that exits with status 42. The parent calls wait(), checks WIFEXITED, and prints the exit status. Also handle the case where the child was killed by a signal. |
| 6. |
C |
Write a program that forks a child that calls abort() to terminate abnormally. The parent uses wait() and the WIFSIGNALED / WTERMSIG macros to detect and print the signal that caused termination. |
| 1. |
T |
What happens when a process calls one of the exec functions? What is replaced, and what is preserved? List at least four attributes that survive an exec. |
| 2. |
T |
Explain the difference between execl, execv, execle, execve, execlp, and execvp. What does the l vs v, the e, and the p suffix each indicate? |
| 3. |
P |
After a successful exec, does the original program's code still run? Why does exec never return on success, and what does it return on failure? |
| 4. |
P |
A process has several file descriptors open when it calls exec. Which descriptors remain open in the new program by default? How can you ensure a descriptor is closed automatically across exec? |
| 5. |
C |
Write a program that forks a child. The child uses execl() to run /bin/ls -l in the current directory. The parent waits for the child and prints its exit status. |
| 6. |
C |
Write a program that builds an argv array at runtime (for example, {"echo", "hello", "world", NULL}) and calls execvp(). Demonstrate passing a custom environment using execve(). |
| 1. |
T |
What are environment variables? How are they stored in a process's address space, and how does the kernel pass them to a new program at exec time? |
| 2. |
T |
What is the environ global variable? How does it differ from getenv()? What are the dangers of modifying the environment with putenv() vs setenv()? |
| 3. |
P |
A parent sets an environment variable with setenv() and then forks. The child modifies the variable. Does the parent's environment change? Now reverse: the parent calls setenv() after fork(). Does the child's environment change? |
| 4. |
P |
A program calls getenv("PATH") and stores the returned pointer, then later calls putenv("PATH=/usr/bin"). Is the stored pointer still valid? Explain why getenv returns a pointer into the environment rather than a copy. |
| 5. |
C |
Write a program that prints all environment variables by iterating through the environ array, printing each NAME=VALUE pair. Count and print the total number of variables. |
| 6. |
C |
Write a program that reads the HOME and PATH environment variables using getenv(), adds a new variable MY_VAR=hello using setenv(), and then forks. In the child, print all three variables. Confirm the parent's environment is unchanged after the fork. |
| 1. |
T |
What is a signal in Unix? Explain the lifecycle of a signal: generation, delivery, and disposition. What are the three possible dispositions for any signal? |
| 2. |
T |
List six common signals, their default actions, and typical causes: SIGINT, SIGTERM, SIGKILL, SIGSEGV, SIGCHLD, and SIGPIPE. |
| 3. |
P |
What is the difference between ignoring a signal and setting its disposition to the default action? Can SIGKILL and SIGSTOP be caught or ignored? Why not? |
| 4. |
P |
A process sends itself a signal using raise(). The signal is not blocked. At what point is the signal delivered — immediately during raise(), or at some later time? |
| 5. |
C |
Write a program that installs a handler for SIGINT using sigaction(). The handler prints "Caught SIGINT, press again to quit" the first time. On the second SIGINT, restore the default disposition and re-raise the signal. |
| 6. |
C |
Write a program that uses kill() to send SIGUSR1 to itself. Install a handler that prints "SIGUSR1 received". Show that the handler is called synchronously relative to the kill() call. |
| 1. |
T |
What is an anonymous (unnamed) pipe? Describe its kernel implementation: capacity, buffering, and the behavior when the read or write end is closed. |
| 2. |
T |
What are the atomicity guarantees of write() on a pipe? What is PIPE_BUF, and what happens when you write more than PIPE_BUF bytes in a single call? |
| 3. |
P |
Why must unused pipe ends be closed in both parent and child after fork()? Give two concrete failure scenarios: one for not closing the unused read end in the writer, and one for not closing the unused write end in the reader. |
| 4. |
P |
A program creates a pipe and then calls fork(). The parent writes to the pipe continuously; the child reads. The parent eventually calls close() on the write end. What does read() in the child return after all data has been consumed? |
| 5. |
C |
Write a program that creates a pipe and forks. The child writes the string "hello from child\n" to the write end and exits. The parent closes the write end, reads all data from the read end in a loop, and prints it. |
| 6. |
C |
Write a program that creates two pipes to establish bidirectional communication between parent and child. The parent sends a number to the child; the child doubles it and sends it back; the parent prints the result. |
| 1. |
T |
What does dup() do? What is the relationship between the original and the duplicated descriptor in terms of the open-file table entry, flags, and offset? |
| 2. |
T |
What does dup2(oldfd, newfd) do that dup() cannot do directly? What happens if newfd is already open when dup2() is called? What happens if oldfd == newfd? |
| 3. |
P |
A shell implements program > output.txt as follows: open the file, call dup2(fd, STDOUT_FILENO), close fd, then exec the program. Trace each step and explain why the program writes to the file without knowing it. |
| 4. |
P |
After dup2(fd, 1), the original fd is still open. A program closes fd but forgets to set it to -1 and later accidentally calls close(fd) again. What can go wrong if a new open() has reused fd in the meantime? |
| 5. |
C |
Write a program that redirects standard output to output.txt using dup2(). After redirection, call printf("This goes to the file\n") and fflush(stdout). Verify by reading the file back and printing its content to stderr. |
| 6. |
C |
Write a program that saves standard output to a temporary descriptor using dup(), redirects stdout to a file, writes a message, and then restores the original stdout using dup2() and writes a second message to the terminal. |
| 1. |
T |
Explain how a Unix shell connects two commands with a pipe, for example ls | wc -l. Describe each step: pipe creation, fork, descriptor manipulation, exec. Be precise about which process does what. |
| 2. |
T |
What is a pipeline deadlock? Describe the conditions under which two processes communicating through a pipe can deadlock, and how to avoid it. |
| 3. |
P |
In implementing cmd1 | cmd2, a common mistake is failing to close the pipe's write end in the cmd2 process. What symptom does this cause at runtime, and why? |
| 4. |
P |
In a three-command pipeline A | B | C, how many pipes and how many fork() calls does the shell need? Sketch the descriptor layout for process B. |
| 5. |
C |
Write a C program that implements the pipeline ls | wc -l without using system() or popen(). Use fork(), pipe(), dup2(), and execl(). The parent should wait for both children. |
| 6. |
C |
Write a program that implements cat file.txt | grep error > result.txt. Use two forks, one pipe, and dup2() for both the pipe connection and the output redirection. The parent waits for all children. |
| 1. |
T |
What is a FIFO (named pipe)? How does it differ from an anonymous pipe in terms of creation, lifetime, and which processes can use it? |
| 2. |
T |
What are the blocking semantics when opening a FIFO? What happens when a process opens a FIFO for reading before any writer has opened it? How does O_NONBLOCK change this? |
| 3. |
P |
Two unrelated processes (no common ancestor) need to communicate. Why can they not use an anonymous pipe? How does a FIFO solve this problem? What filesystem namespace requirement does a FIFO impose? |
| 4. |
P |
A writer opens a FIFO and writes data. The reader has not yet opened the FIFO. The writer then closes the FIFO before the reader opens it. What data, if any, does the reader see when it eventually opens the FIFO? |
| 5. |
C |
Write a program fifo_writer.c that creates a FIFO named /tmp/myfifo using mkfifo() (if it does not already exist), opens it for writing, writes the string "hello from writer\n", and closes it. |
| 6. |
C |
Write a companion program fifo_reader.c that opens /tmp/myfifo for reading, reads and prints all data, and closes it. Demonstrate that the two programs can be run in separate terminals. |
| 1. |
T |
What is a process group? What is a session? What is a session leader, and what is a controlling terminal? Draw the hierarchy: session → process groups → processes. |
| 2. |
T |
What system calls create a new session and a new process group? What restrictions apply — for example, why can a process group leader not call setsid()? |
| 3. |
P |
When the user types Ctrl+C in a terminal, which processes receive SIGINT? Explain the role of the foreground process group and how the kernel determines which processes to signal. |
| 4. |
P |
A shell runs prog1 & prog2. Both are in the same session. What are their process group IDs? What happens to prog1 if the user closes the terminal (SIGHUP)? |
| 5. |
C |
Write a program that prints: its PID, parent PID, process group ID (getpgrp()), and session ID (getsid(0)). Then call setpgid(0, 0) to create a new process group and print the updated process group ID. |
| 6. |
C |
Write a program that forks a child. In the child, call setsid() to create a new session, and print the new session ID to confirm it equals the child's PID. The parent prints the child's PID and waits. |
| 1. |
T |
What is a daemon process? List at least five well-known Unix daemons and what they do. What distinguishes a daemon from an ordinary background process? |
| 2. |
T |
Describe the standard sequence of steps used to create a daemon: the two fork() calls (or one fork plus setsid()), chdir("/"), umask(0), closing file descriptors, and redirecting 0/1/2 to /dev/null. Explain the purpose of each step. |
| 3. |
P |
Why does a daemon call fork() and have the parent exit before calling setsid()? What would go wrong if the process group leader called setsid() directly? |
| 4. |
P |
Why does a daemon close all open file descriptors at startup and redirect stdin, stdout, and stderr to /dev/null? What problems can arise if it does not? |
| 5. |
C |
Write a simplified daemon that: forks (parent exits), calls setsid(), changes directory to /, sets umask(0), closes file descriptors 0–2 and reopens them to /dev/null, then writes a timestamped message to /tmp/daemon.log every 5 seconds. Include a SIGTERM handler that exits cleanly. |
| 6. |
C |
Write a program that checks whether it is already running as a daemon (no controlling terminal) using isatty(STDIN_FILENO). If not, daemonize itself using the steps above and print a confirmation to the log file. |
| 1. |
T |
What is nonblocking I/O? How does it differ from blocking I/O in the behavior of read() and write()? What error is returned when an operation would block? |
| 2. |
T |
What is fcntl()? Describe four common uses: duplicating a descriptor, getting/setting descriptor flags (FD_CLOEXEC), getting/setting file status flags (O_NONBLOCK), and advisory record locking. |
| 3. |
P |
A program sets a socket to nonblocking mode and calls read(). No data is available. What does read() return, and what value does errno have? How should the program respond? |
| 4. |
P |
A program opens a file and wants to ensure the descriptor is closed when the process calls exec(). Show the fcntl() calls needed to get the current flags, add FD_CLOEXEC, and set the new flags — without accidentally clearing other flags. |
| 5. |
C |
Write a program that opens a file for writing, uses fcntl() to retrieve the current file status flags, adds O_APPEND to them, sets the new flags, and then writes two lines. Verify by checking the file content. |
| 6. |
C |
Write a program that sets standard input to nonblocking mode using fcntl(). Attempt a read() call. If no data is available (EAGAIN/EWOULDBLOCK), print "No data available" and exit. Otherwise print what was read. |
| 1. |
T |
What is a thread? How does a thread differ from a process in terms of what is shared and what is private? List at least five things shared among threads in the same process and three things that are private per thread. |
| 2. |
T |
What is pthread_create()? What arguments does it take? What is the thread start function signature? What happens to the process when the main thread calls return vs pthread_exit()? |
| 3. |
P |
A process has two threads. Thread A calls fork(). How many threads does the child process have? What happens to the state locked by thread B's mutex in the child? What is pthread_atfork() used for? |
| 4. |
P |
Two threads share a global int counter. Both execute counter++ in a loop. Why can the final value be less than expected even on a single-core machine? Explain at the assembly / load-store level. |
| 5. |
C |
Write a program that creates two threads. Each thread receives its thread number (1 or 2) as an argument through pthread_create(), prints "Thread N running", and returns a pointer to a heap-allocated result string. The main thread joins both and prints the returned strings. |
| 6. |
C |
Write a program that creates a thread to compute the sum of integers from 1 to 1,000,000. The thread returns the result via pthread_exit(). The main thread retrieves it with pthread_join() and prints the result. Compare with the formula N*(N+1)/2. |
| 1. |
T |
What is a mutex? Describe the pthread_mutex_lock() / pthread_mutex_unlock() contract. What is a deadlock, and give a simple two-mutex example that produces one? |
| 2. |
T |
What is the difference between pthread_mutex_trylock() and pthread_mutex_lock()? When would you use trylock() instead of lock()? |
| 3. |
P |
Two threads both increment a shared counter 1,000,000 times without synchronization. The final value is consistently less than 2,000,000. Explain exactly why — trace the load/increment/store sequence and show a concrete interleaving that loses an update. |
| 4. |
P |
A mutex is declared as a local variable inside a function and locked before spawning a thread that uses it. The function returns, destroying the local mutex. The thread then unlocks it. What is the behavior, and how should the code be fixed? |
| 5. |
C |
Write a program with two threads that both increment a shared long counter 1,000,000 times. Protect the counter with a pthread_mutex_t. Print the final value and verify it equals 2,000,000. |
| 6. |
C |
Write a program that models a bank account. Two threads each perform 10,000 deposits and 10,000 withdrawals of $1. Protect the balance with a mutex and assert the final balance equals the initial balance. |
| 1. |
T |
Explain the difference between real user ID, effective user ID, and saved set-user-ID. When does each matter, and which one does the kernel check for most permission decisions? |
| 2. |
T |
What is the setuid bit on an executable? How does it change the effective UID when the program runs? Give a real example of a setuid program in Unix and explain why it needs elevated privileges. |
| 3. |
P |
A setuid-root program needs elevated privileges for part of its work but wants to run with the user's real UID for most operations. What sequence of seteuid() calls achieves this? Why is it important to drop privileges as early as possible? |
| 4. |
P |
What is the difference between setuid() and seteuid() for a process running as root? When does setuid() permanently drop privileges, and when is that irreversible? |
| 5. |
C |
Write a program that prints real UID (getuid()), effective UID (geteuid()), real GID (getgid()), and effective GID (getegid()). Run it normally and (if possible) as a setuid binary to observe the difference. |
| 6. |
C |
Write a program that calls seteuid(getuid()) to temporarily lower its effective UID, tries to open /etc/shadow (which should fail), restores the original effective UID, and tries again. Print the result of each attempt. |
| 1. |
T |
What information is returned by stat()? List at least eight fields in struct stat and explain what each contains. Which fields are NOT stored in the inode on traditional Unix filesystems? |
| 2. |
T |
What are the three timestamps stored in struct stat? Explain st_atime, st_mtime, and st_ctime. Which one is updated by chmod()? Can st_ctime be set directly by an application? |
| 3. |
P |
A program calls stat() on a directory and checks st_nlink. An empty newly created directory has link count 2. Explain why. A directory with three subdirectories has link count 5. Why? |
| 4. |
P |
st_size for a symbolic link reports the length of the link's target string, not the size of the target file. How would you find the actual size of the file a symlink points to? Show the sequence of calls. |
| 5. |
C |
Write a program that takes a filename as an argument, calls stat(), and prints: file type, permissions (as rwxrwxrwx), hard link count, owner UID and GID, file size, and the last modification time formatted with ctime(). |
| 6. |
C |
Write a program that takes two filenames and determines whether they refer to the same underlying file (same inode on same device) by comparing st_ino and st_dev from stat(). Print "same file" or "different files". |
| 1. |
T |
Define orphan process. What happens to an orphan in Linux/Unix? Which process takes over as the new parent and why? |
| 2. |
T |
When is SIGCHLD generated? What is the default disposition of SIGCHLD? Why can ignoring SIGCHLD prevent zombies on some systems, but is not portable? |
| 3. |
P |
A server process forks many children to handle requests and never calls wait(). The children exit. Describe the zombie accumulation problem. Present two correct solutions: using a SIGCHLD handler with waitpid(-1, ..., WNOHANG) in a loop, and the double-fork technique. |
| 4. |
P |
A program installs a SIGCHLD handler that calls wait() once. Two children exit simultaneously. Is it possible for the handler to be called only once? Explain and give the correct handler implementation. |
| 5. |
C |
Write a program that installs a SIGCHLD handler. The handler calls waitpid(-1, &status, WNOHANG) in a loop until no more children are ready. The main program forks three children that exit immediately, then sleeps. Print each reaped child's PID and exit status from the handler. |
| 6. |
C |
Write a program that demonstrates the double-fork technique: the parent forks, the child immediately forks again and exits, and the grandchild does the real work. Show that the grandchild's PPID is 1 (or the subreaper), so the parent never needs to wait for it. |
| 1. |
T |
What is a controlling terminal? How does a process acquire one? What is the relationship between the controlling terminal and a session? |
| 2. |
T |
What is SIGHUP? When is it generated, and to which processes? Why do daemons need to detach from the controlling terminal to avoid receiving SIGHUP unexpectedly? |
| 3. |
P |
A user logs in over SSH and runs a program that does not daemonize. The user closes the SSH connection. What signal is sent? Which processes receive it? What can the program do to survive the disconnection? |
| 4. |
P |
Explain the purpose of opening /dev/tty inside a program. When would a program need to read directly from the controlling terminal rather than from file descriptor 0? |
| 5. |
C |
Write a program that uses isatty(STDIN_FILENO) to detect whether it has a controlling terminal. If it does, open /dev/tty and read a password-like string (turn off echo using tcgetattr/tcsetattr). If it does not, print "No controlling terminal". |
| 6. |
C |
Write a program that calls setsid() in a child process to detach from the controlling terminal, then attempts to open /dev/tty. Show that the open fails with ENXIO, confirming the session has no controlling terminal. |
| 1. |
T |
What is the current working directory of a process? How is it used when resolving relative pathnames? Is it stored in the process or in the filesystem? |
| 2. |
T |
How does chdir() affect the process and its children? Is the working directory inherited across fork()? Across exec()? |
| 3. |
P |
A daemon calls chdir("/") at startup. Why? What problems arise if a daemon keeps the working directory of the shell that launched it? |
| 4. |
P |
A program changes its working directory to a removable drive using chdir(), and the drive is then unmounted while the program still runs. What may happen? How does this affect relative path resolution? |
| 5. |
C |
Write a program that prints the current working directory using getcwd(), changes to /tmp using chdir(), prints the new working directory, creates a file using a relative path ("testfile.txt"), and confirms the file exists in /tmp. |
| 6. |
C |
Write a program that opens a file descriptor to the current directory using open(".", O_RDONLY), changes to a different directory with chdir(), does some work, and then restores the original directory using fchdir(). Explain why this technique is useful in library code. |
| 1. |
T |
Explain the Unix process memory layout: text segment, initialized data, uninitialized data (BSS), heap, and stack. Where do malloc() allocations go? |
| 2. |
T |
What is a memory leak? What are the consequences of memory leaks in a short-lived program versus a long-running daemon? What tools can detect leaks? |
| 3. |
P |
A function allocates memory with malloc(), stores the pointer in a local variable, and returns without freeing it. The caller has no way to retrieve the pointer. Is this a memory leak? Explain. Now the same allocation is stored in a global variable that is never freed before exit(). Is that a leak? |
| 4. |
P |
What is a dangling pointer? Show a code sequence that creates one and explain why accessing it is undefined behavior, even though the pointer value itself may still appear valid. |
| 5. |
C |
Write a program that dynamically allocates an array of 100 integers, fills it with values 1–100, computes the sum, prints it, and correctly frees the memory. Then deliberately introduce a double-free bug in a separate code path and explain what goes wrong. |
| 6. |
C |
Write a program that builds a singly linked list using malloc(), populates it with 5 nodes, traverses it to print values, and then correctly frees every node with a proper traversal-and-free loop. Show the incorrect version (freeing node before using node->next) and the correct one. |
| 1. |
T |
What does alarm() do? What signal does it generate, and after how long? What happens if alarm() is called again before the previous alarm expires? |
| 2. |
T |
What is sigsuspend()? How does it differ from calling sigprocmask() to unblock a signal followed by pause()? Why is the difference important for correctness? |
| 3. |
P |
A program wants to implement a timeout on a read() call using alarm() and a SIGALRM handler. Sketch the code sequence. What happens to the read() call when the alarm fires? What must be checked on read()'s return value? |
| 4. |
P |
What is the interaction between alarm() and sleep()? Why should a program generally not mix both? What is the POSIX-correct way to sleep for a fractional number of seconds? |
| 5. |
C |
Write a program that uses alarm(3) and a SIGALRM handler to print "Timeout!" if a read() from stdin takes more than 3 seconds. If the user types something in time, print what was read. Cancel the alarm afterward with alarm(0). |
| 6. |
C |
Write a program that uses sigsuspend() to wait for SIGUSR1. Block SIGUSR1 before installing the handler, prepare a mask that allows SIGUSR1, call sigsuspend(), and print "Got SIGUSR1" when it arrives. Send the signal from another terminal using kill. |
| 1. |
T |
Compare fread()/fwrite() (standard I/O) with read()/write() (POSIX I/O) along these dimensions: buffering, portability, error handling, and when each is preferred. |
| 2. |
T |
What does fread() return when fewer items than requested are available? How do you distinguish end-of-file from a read error after fread() returns a short count? |
| 3. |
P |
A program uses write() to write 1 byte at a time to a file in a loop of 1,000,000 iterations. Another program uses fwrite() with a buffer. Both write the same total data. Which is faster and why? What does this reveal about system-call overhead? |
| 4. |
P |
A program opens a file with fopen() and uses fread() to read records. It then calls lseek() on fileno() of the same FILE * to jump to a different position. What hazard does mixing standard I/O buffering with direct lseek() create? |
| 5. |
C |
Write a program that reads a binary file using fread() in chunks of 512 bytes and writes each chunk to another file using fwrite(). Correctly handle short reads at the end of file using feof() and ferror(). |
| 6. |
C |
Write a program that writes 10 structs of type struct { int id; double value; } to a file using fwrite(), then reads them back with fread() and prints each one. Explain why this approach is not portable across different architectures. |
| 1. |
T |
Explain the purpose of the following open() flags: O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, and O_SYNC. Which are file access modes and which are file status flags? |
| 2. |
T |
What is the purpose of O_EXCL when combined with O_CREAT? What atomicity guarantee does this combination provide, and why is it used for lock files? |
| 3. |
P |
A program opens a file with O_CREAT | O_TRUNC | O_WRONLY. The file already exists and contains important data. What happens to the data? What is the minimum flag set needed to create a file that does not already exist, failing if it does? |
| 4. |
P |
A program opens a file with O_SYNC. What does this change about the behavior of subsequent write() calls? What is the performance cost? When is it worth paying? |
| 5. |
C |
Write a program that safely creates a lock file at /tmp/myapp.lock using O_CREAT | O_EXCL | O_WRONLY. If the file already exists, print "Another instance is running" and exit. Otherwise write the current PID to the file, sleep 5 seconds, remove the lock, and exit. |
| 6. |
C |
Write a program that demonstrates the difference between O_SYNC and default writes: open the same file twice (once with O_SYNC, once without), write the same 1 MB of data to each, and print the elapsed time for each using clock_gettime(). |
| 1. |
T |
What is the difference between wait() and waitpid()? What additional control does waitpid() provide via its pid argument and options flags? |
| 2. |
T |
What does WNOHANG do in waitpid()? What does WUNTRACED do? When is each useful? |
| 3. |
P |
A shell runs three background jobs and wants to reap whichever finishes first without blocking. Write the waitpid() call that achieves this, and explain the return value for each possible outcome: a child exited, no child is ready, or no children exist. |
| 4. |
P |
A parent calls wait() but only has stopped (not terminated) children. Does wait() return? What option to waitpid() would allow detecting stopped children? |
| 5. |
C |
Write a program that creates three children. Child 1 sleeps 1 second, child 2 sleeps 3 seconds, child 3 sleeps 2 seconds. The parent uses waitpid(-1, &status, 0) in a loop to reap them in order of completion. Print each child's PID and the order in which it was reaped. |
| 6. |
C |
Write a program that uses waitpid() with WNOHANG in a polling loop: fork a child that sleeps 2 seconds, and have the parent poll every 500ms using nanosleep(). Print "not done yet" each time waitpid() returns 0 and "child done" when it returns the child's PID. |
| 1. |
T |
Explain the sequence a shell uses to execute an external command: how it parses the command line, forks, sets up redirections, and calls exec. Why is the fork() + exec() split (rather than a single "spawn") fundamental to Unix design? |
| 2. |
T |
How does a shell implement built-in commands like cd and exit differently from external commands? Why can't cd be implemented as an external program? |
| 3. |
P |
A user types ls -l /tmp in a shell. Trace every step from the shell reading the line to the output appearing on screen: tokenization, fork(), execvp(), wait(), and output. |
| 4. |
P |
How does a shell implement cmd1 && cmd2 (run cmd2 only if cmd1 succeeds)? How does it implement cmd1 || cmd2? What exit status information is used? |
| 5. |
C |
Write a mini shell that reads one line at a time from stdin, splits it on spaces into argv, forks a child, and uses execvp() to run the command. The parent waits and prints the exit status. Handle at least: empty lines, commands not found (print a helpful error), and Ctrl+D (EOF) to exit. |
| 6. |
C |
Extend the mini shell from question 5 to handle output redirection: if the command line contains > followed by a filename (e.g., ls -l > out.txt), redirect stdout to that file using open() and dup2() before calling exec(). |
| 1. |
T |
What is alarm()? What signal does it deliver, when, and to which process? What happens if you call alarm(0)? Can multiple alarms be pending simultaneously? |
| 2. |
T |
What is setitimer()? What three timer types does it provide (ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF) and what signal does each generate? |
| 3. |
P |
A program calls alarm(5) and then sleep(10). How long does the program actually sleep? Why? What is the POSIX specification about the interaction between alarm() and sleep()? |
| 4. |
P |
A program uses alarm() to implement a watchdog: if a function takes more than 2 seconds, terminate the process. Sketch the code including signal handler, alarm() calls, and the guarded function call. What cleanup must the handler perform? |
| 5. |
C |
Write a program that sets an alarm for 2 seconds and enters an infinite loop. The SIGALRM handler prints "Alarm fired", cancels any further alarms, and returns. Use SA_RESTART appropriately and explain your choice. |
| 6. |
C |
Write a program that implements a countdown timer: print the number of seconds remaining every second for 5 seconds, then print "Done". Use alarm(1) re-armed inside the SIGALRM handler, and a counter decremented each time. Use sigsuspend() in the main loop. |
| 1. |
T |
What does popen() do? What does it create under the hood (fork, exec, pipe)? What does the type argument ("r" or "w") specify? |
| 2. |
T |
What does pclose() do that fclose() does not? Why is it important to use pclose() and not fclose() on a stream returned by popen()? |
| 3. |
P |
A program calls popen("ls /tmp", "r"). Trace the steps: what processes are created, how is the pipe connected, and how does the program read the output of ls? |
| 4. |
P |
What are the security risks of constructing the command string passed to popen() by concatenating user input? What attack is possible? What is the safer alternative? |
| 5. |
C |
Write a program that uses popen("date", "r") to read the current date and time and print it. Handle errors from popen() and pclose(). Print the exit status of the child command. |
| 6. |
C |
Write a program that uses popen() with "w" to pipe lines of text to sort, which writes its sorted output to a file (sort > /tmp/sorted.txt). Write 5 out-of-order lines and verify the output file is sorted. |
| 1. |
T |
What is POSIX shared memory? How does it differ from System V shared memory and from memory-mapped files? What system calls create and use POSIX shared memory objects? |
| 2. |
T |
What is the lifetime of a POSIX shared memory object? How is it removed? What happens if a process that created the object exits without calling shm_unlink()? |
| 3. |
P |
Two unrelated processes want to share a large data structure. Compare three IPC approaches: pipes, shared memory, and memory-mapped files. For which workload is shared memory most advantageous? |
| 4. |
P |
A shared memory region is set up between two processes. Process A writes a value and process B reads it. Is a memory barrier or synchronization primitive needed? Why can't process B simply spin-wait on the shared variable safely? |
| 5. |
C |
Write a producer program that creates a POSIX shared memory object (shm_open() with O_CREAT), sets its size with ftruncate(), maps it with mmap(), writes the string "hello from producer", and exits without unlinking. |
| 6. |
C |
Write a consumer program that opens the same shared memory object, maps it, reads and prints the string, unmaps it, and calls shm_unlink() to remove the object. Run producer first, then consumer. |
| 1. |
T |
What is a semaphore? Explain the semantics of sem_wait() (P operation) and sem_post() (V operation). How are POSIX named semaphores different from unnamed (memory-based) semaphores? |
| 2. |
T |
What is the difference between a binary semaphore and a counting semaphore? Give a use case for each. |
| 3. |
P |
A producer-consumer system has a buffer of capacity N. Describe the two semaphores needed (empty and full) and show the pseudo-code for producer and consumer using sem_wait() and sem_post(). Why are two semaphores needed? |
| 4. |
P |
What happens if a process that holds a semaphore (sem_wait() but not yet sem_post()) crashes? Is the semaphore automatically released? How does this compare to a mutex with PTHREAD_MUTEX_ROBUST? |
| 5. |
C |
Write a program that uses an unnamed semaphore (sem_init()) to synchronize two threads: a producer that generates numbers 1–5 and posts them to the semaphore, and a consumer that waits on the semaphore and prints each number. |
| 6. |
C |
Write two programs using a named semaphore (sem_open()): one creates the semaphore with initial value 0 and signals it (sem_post()) five times with 1-second intervals; the other waits on it five times and prints a message each time. Demonstrate they work across processes. |
| 1. |
T |
What is thread-specific data (TSD)? What problem does it solve? How does it differ from a global variable and from a stack-local variable? |
| 2. |
T |
Describe the pthread_key_create() / pthread_setspecific() / pthread_getspecific() API. What is the destructor function passed to pthread_key_create(), and when is it called? |
| 3. |
P |
The standard errno variable must be thread-safe: each thread must have its own copy. How is this typically implemented on modern systems — using TSD or using __thread (thread-local storage)? What is the difference? |
| 4. |
P |
A library function allocates a buffer for its return value and stores it in thread-specific storage. A thread calls the function twice. Does the second call leak the first buffer? How should the destructor be written to avoid the leak? |
| 5. |
C |
Write a program with 3 threads. Each thread stores its own integer ID in thread-specific data using pthread_key_create() and pthread_setspecific(). A helper function retrieves and prints the ID using pthread_getspecific(). Show that each thread sees its own value. |
| 6. |
C |
Write a program that implements a thread-safe version of strtok() using thread-specific data to store the saved pointer between calls. Test it with two threads tokenizing different strings simultaneously. |
| 1. |
T |
What is a read-write lock? How does it differ from a mutex? What is the concurrency advantage for read-heavy workloads? |
| 2. |
T |
Describe the fairness problem with read-write locks: if readers arrive continuously, can writers starve? How do different implementations handle writer priority? |
| 3. |
P |
A shared hash table is read by 8 threads and updated by 1 thread. Compare using a single mutex versus a pthread_rwlock_t. Under what access pattern does the read-write lock provide significant benefit? |
| 4. |
P |
A thread holds a read lock and tries to upgrade it to a write lock on the same lock. What happens? Why can most implementations not support lock upgrading without risking deadlock? |
| 5. |
C |
Write a program with 4 reader threads and 1 writer thread sharing a global integer. Readers repeatedly read and print the value. The writer increments it every second. Protect all accesses with pthread_rwlock_rdlock() / pthread_rwlock_wrlock(). |
| 6. |
C |
Write a program that wraps a sorted array with a read-write lock. Provide two operations: search(int val) (takes a read lock) and insert(int val) (takes a write lock). Spawn 3 reader threads and 1 writer thread operating concurrently. |
| 1. |
T |
What is a process resource limit? Name six limits defined by POSIX (RLIMIT_NOFILE, RLIMIT_STACK, RLIMIT_CORE, RLIMIT_CPU, RLIMIT_DATA, RLIMIT_FSIZE) and explain what each governs. |
| 2. |
T |
What is the difference between the soft limit and the hard limit? Who can raise and lower each? What happens when a process exceeds the soft limit for RLIMIT_CPU? |
| 3. |
P |
A process sets RLIMIT_NOFILE to 10. It then attempts to open an 11th file. What does open() return? What errno is set? Can the process raise the limit back afterward? |
| 4. |
P |
A program wants to guarantee it will produce a core dump on crash (for debugging). What resource limit must be checked and possibly raised, and to what value? |
| 5. |
C |
Write a program that prints the current soft and hard limits for RLIMIT_NOFILE using getrlimit(). Then use setrlimit() to lower the soft limit to 5, attempt to open 10 files in a loop, and print how many succeed before failure. |
| 6. |
C |
Write a program that lowers RLIMIT_CPU to 1 second using setrlimit(), then enters a CPU-intensive loop. Install a SIGXCPU handler that prints "CPU limit exceeded". Show the signal is delivered after 1 second of CPU time. |
| 1. |
T |
What is sysconf()? Why does POSIX provide it instead of requiring programs to use compile-time constants? Give three examples of values it can return and their _SC_* names. |
| 2. |
T |
What is pathconf()? How does it differ from sysconf()? Give two examples of filesystem-dependent limits it provides and their _PC_* names. |
| 3. |
P |
A program needs to allocate a buffer large enough to hold any pathname. Why is hard-coding PATH_MAX (e.g., 4096) fragile? How should the program determine the correct buffer size portably? |
| 4. |
P |
sysconf(_SC_OPEN_MAX) returns the maximum number of open file descriptors per process. A program uses this value to close all file descriptors in a loop (as part of daemonizing). What is the risk if this value is very large (e.g., 1,048,576)? |
| 5. |
C |
Write a program that prints the following system configuration values: _SC_CLK_TCK, _SC_OPEN_MAX, _SC_PAGESIZE, _SC_NPROCESSORS_ONLN, and _SC_LOGIN_NAME_MAX. Handle the case where a value is indeterminate (returns -1 without setting errno). |
| 6. |
C |
Write a program that takes a directory path as an argument and uses pathconf() to print _PC_NAME_MAX (max filename length), _PC_PATH_MAX (max relative path length), and _PC_LINK_MAX (max hard link count) for that filesystem. |
| 1. |
T |
What are real-time signals? How do they differ from standard Unix signals in terms of queuing, ordering, and additional information? What signal number range do they occupy? |
| 2. |
T |
What is sigqueue()? How does it differ from kill()? What is the union sigval used for, and how is it retrieved in the signal handler? |
| 3. |
P |
A process sends SIGUSR1 to another process 5 times using kill() while the signal is blocked. How many times is the handler called when the signal is unblocked? Now repeat with a real-time signal sent via sigqueue(). What is the difference? |
| 4. |
P |
What is the maximum number of real-time signals that can be queued? What happens when this limit is exceeded and sigqueue() is called again? |
| 5. |
C |
Write a sender program that uses sigqueue() to send SIGRTMIN with an integer value (e.g., 42) to a process whose PID is given as a command-line argument. |
| 6. |
C |
Write a receiver program that installs a SIGRTMIN handler using sigaction() with SA_SIGINFO. The handler receives the siginfo_t pointer and prints the signal number, the sender's PID (si_pid), and the integer value (si_value.sival_int). |
| 1. |
T |
What do truncate() and ftruncate() do? What is the difference between their arguments? What happens to the file offset of an open descriptor after ftruncate()? |
| 2. |
T |
What happens when truncate() is called with a length longer than the current file size? What do the newly added bytes contain? |
| 3. |
P |
A log rotation scheme truncates a log file to zero length using truncate() while another process has the file open for appending. Does the writing process see the truncation? What happens to its file offset and subsequent writes? |
| 4. |
P |
A program uses ftruncate() to extend a file to a large size as a way of pre-allocating space. How does this relate to sparse files? What is the difference between ftruncate() for pre-allocation and fallocate() on Linux? |
| 5. |
C |
Write a program that creates a file, writes 100 bytes to it, uses ftruncate() to shrink it to 50 bytes, and then uses ftruncate() to extend it to 200 bytes. Print the file size after each operation using fstat(). Read and print the bytes in the extended region. |
| 6. |
C |
Write a program that implements simple log rotation: if a log file exceeds 1 KB, rename it to log.1 and create a new empty log.0. Use stat() to check size, rename() to rotate, and open() with O_CREAT|O_TRUNC to create the new file. |
| 1. |
T |
Describe what the kernel does when execve() is called successfully: what happens to the text segment, data segment, stack, heap, signal handlers, open file descriptors, and the process ID. |
| 2. |
T |
What is the envp argument to execve()? How is it different from using execle() vs execvp()? What happens to the child's environment if envp is passed as NULL? |
| 3. |
P |
A parent process sets an environment variable and forks. The child calls execve() with a custom envp array that does not include the variable. Does the executed program see the variable? Explain what execve() does with the envp it receives. |
| 4. |
P |
putenv() stores a pointer to the string directly in the environment, while setenv() copies the string. A program calls putenv(buf) and then modifies buf. What happens to the environment variable? Why is setenv() generally safer? |
| 5. |
C |
Write a program that constructs a minimal environment ({"PATH=/bin:/usr/bin", "HOME=/tmp", NULL}) and uses execve("/usr/bin/env", ...) to print it, demonstrating that the child sees only the supplied environment. |
| 6. |
C |
Write a program that uses putenv() to set MYVAR=original, prints it, modifies the string buffer passed to putenv() to change the value, and prints MYVAR again using getenv(). Show that the environment variable changed and explain the risk. |
| 1. |
T |
What exactly does dup() duplicate? After dup(fd), do the two descriptors share the same file offset, the same open-file table entry, or just the same inode? What is shared and what is independent? |
| 2. |
T |
What is the close-on-exec flag (FD_CLOEXEC)? Is it inherited by a descriptor created with dup() or dup2()? How can you create a duplicate that has close-on-exec set atomically using dup3() (Linux)? |
| 3. |
P |
A program calls close(1) to close stdout, then calls dup(fd) where fd is an open file. Why is descriptor 1 guaranteed to be returned by dup()? What POSIX rule governs this? What is the risk of this pattern in a multithreaded program? |
| 4. |
P |
A child process inherits a large number of open file descriptors. The parent wants to close all of them before exec() except for 0, 1, and 2. Compare two approaches: looping from 3 to sysconf(_SC_OPEN_MAX) calling close() on each, versus using closefrom() (where available) or /proc/self/fd. What is the performance concern? |
| 5. |
C |
Write a program that opens a file, uses dup2() to make descriptor 1 (stdout) point to the file, and then calls execlp("ls", "ls", "-l", NULL). Verify that ls output goes to the file, not the terminal. Close the original file descriptor before exec. |
| 6. |
C |
Write a program that demonstrates dup() sharing: open a file, duplicate the descriptor with dup(), write "first\n" through the original, then write "second\n" through the duplicate without seeking. Show that both writes advance the same shared file offset by reading back the file. |
| 1. |
T |
Describe the structure of the Unix password file (/etc/passwd). What fields does each entry contain? Why is the password not stored there on modern systems, and where is it stored? |
| 2. |
T |
What is /etc/group? What does each field in an entry represent? What is the difference between a user's primary group (from /etc/passwd) and supplementary groups? |
| 3. |
P |
A program wants to look up a username given a UID. What function should it use? What does it return when the UID does not exist? Why should the returned pointer not be used after the next call to the same function in a multithreaded program? |
| 4. |
P |
A setuid program needs to verify whether the real user (not the effective user) belongs to a specific group. What calls would you use — getgid(), getegid(), getgroups(), or getgrnam()? Show the logic. |
| 5. |
C |
Write a program that takes a username as a command-line argument, looks it up with getpwnam(), and prints: UID, GID, home directory, and default shell. If the user does not exist, print a clear error message. |
| 6. |
C |
Write a program that prints all groups the current user belongs to: first the primary group from getpwuid(getuid()) cross-referenced with getgrgid(), then all supplementary groups using getgroups() and getgrgid(). |
| 1. |
T |
What do chmod() and fchmod() do? What permissions are required to change a file's mode? Can a regular user change a file's setuid or setgid bit? |
| 2. |
T |
What do chown() and fchown() do? Why can only root change the owner of a file on most Unix systems? Under what conditions can a non-root user change the group of a file? |
| 3. |
P |
A program calls chmod("file.txt", 0777). The process's effective UID is not root and is not the file owner. What happens? What does chmod() return? |
| 4. |
P |
When chown() is called on a file that has the setuid or setgid bit set, what happens to those bits? Why does the kernel clear them? |
| 5. |
C |
Write a program that takes a filename and an octal mode (e.g., 0644) as command-line arguments and calls chmod() to set the mode. Before and after the change, print the file's permissions using stat() and the S_IS* / S_IRUSR etc. macros. |
| 6. |
C |
Write a program that uses fchmod() on an open file descriptor (not a path) to change permissions. Open a file, print its current mode, apply fchmod(fd, mode & ~S_IWUSR) to remove owner-write permission, print the new mode, and attempt to write to the file — handle the expected failure. |
| 1. |
T |
What is a socket? Describe the client-server model using sockets: what calls does the server make (socket, bind, listen, accept) and what calls does the client make (socket, connect)? What does accept() return? |
| 2. |
T |
What is a Unix domain socket? How does it differ from an internet socket (AF_INET)? What are the advantages of Unix domain sockets for local IPC (performance, security, credential passing)? |
| 3. |
P |
A server calls bind() with a Unix domain socket path /tmp/mysock. The server crashes without calling unlink(). The next time the server starts and tries to bind() the same path, what happens and what error is returned? How should a robust server handle this? |
| 4. |
P |
What is the difference between SOCK_STREAM and SOCK_DGRAM for Unix domain sockets? Give a use case for each. How do their delivery guarantees differ? |
| 5. |
C |
Write a Unix domain stream socket server that: creates /tmp/echo.sock, listens for one connection, reads a message from the client, echoes it back, and exits. Handle cleanup of the socket file on exit. |
| 6. |
C |
Write a Unix domain stream socket client that connects to /tmp/echo.sock, sends the string "hello server\n", reads the echoed response, prints it, and closes the connection. Run the server and client in separate terminals to demonstrate communication. |
Optional — supplementary topics
| 1. |
T |
What is the purpose of atexit()? How many handlers can be registered (POSIX minimum), and in what order are they called relative to each other and relative to fclose() of open streams? |
| 2. |
T |
What is the difference between exit(), _exit(), and _Exit()? Which ones invoke atexit handlers and flush stdio buffers? When would you use _exit() deliberately? |
| 3. |
P |
A child process registered an atexit handler before calling fork(). The child later calls _exit(). Is the handler called? What if the child calls exit()? |
| 4. |
P |
An atexit handler calls exit() itself. What happens? Is this behavior defined by POSIX? |
| 5. |
C |
Write a program that registers three atexit handlers named first, second, and third. Each prints its name. Call exit() at the end of main. Show and explain the order of execution. |
| 6. |
C |
Write a program that forks a child. Both parent and child have the same atexit handler registered (from before the fork). The child calls _exit() and the parent calls exit(). Show which process invokes the handler and which does not. |
| 1. |
T |
What does it mean for a function to be async-signal-safe? Why is async-signal safety required inside signal handlers, and why is printf() not async-signal-safe? |
| 2. |
T |
What is signal disposition inheritance? How does a child process inherit signal dispositions from its parent? What happens to caught signals after exec()? |
| 3. |
P |
A signal handler calls malloc(). Why is this potentially dangerous? What could go wrong if the signal arrived while the main program was inside malloc()? |
| 4. |
P |
A program installs a handler for SIGTERM and enters a blocking read() call. The user sends SIGTERM. What happens to the read() call? How does SA_RESTART change the behavior? |
| 5. |
C |
Write a program that installs a SIGTERM handler using sigaction(). The handler sets a volatile sig_atomic_t flag. The main loop checks the flag and exits gracefully when it is set. Explain why volatile sig_atomic_t is the correct type. |
| 6. |
C |
Write a program that demonstrates SA_RESTART vs no SA_RESTART: install a SIGUSR1 handler in two ways, send the signal while blocking in read(), and show whether read() restarts or returns EINTR. |
| 1. |
T |
What is a signal mask (the process signal mask)? How is it different from the set of pending signals? What happens to a signal that is delivered while it is blocked? |
| 2. |
T |
Explain sigprocmask(). What are the three operations SIG_BLOCK, SIG_UNBLOCK, and SIG_SETMASK, and when would you use each? |
| 3. |
P |
A program blocks SIGINT and then the user presses Ctrl+C three times. How many SIGINT signals are pending? When the program unblocks SIGINT, how many times will the handler be called? Explain the difference between standard signals and real-time signals in this context. |
| 4. |
P |
There is a race condition between installing a signal handler and blocking the signal. Show the sequence of events that creates the race, and explain how sigprocmask() used before fork() or sigsuspend() eliminates it. |
| 5. |
C |
Write a program that blocks SIGINT for 10 seconds using sigprocmask(). During that period, print a message every second. After unblocking, print whether SIGINT was pending using sigpending(). Test by pressing Ctrl+C during the blocked period. |
| 6. |
C |
Write a program that uses sigsuspend() to atomically unblock SIGINT and wait for it. The main program blocks SIGINT, installs a handler, then calls sigsuspend() with a mask that allows SIGINT. Explain why sigsuspend() is necessary here instead of sigprocmask() + pause(). |
| 1. |
T |
What is errno? Where is it defined, and how is it set? Why is it important that errno is thread-local in POSIX systems? |
| 2. |
T |
What is the difference between perror(), strerror(), and strerror_r()? Which is thread-safe? When is each appropriate? |
| 3. |
P |
A programmer writes this code: |
| |
if (open("x", O_RDONLY) < 0)
printf("Error: %d\n", errno);
|
| 4. |
P |
Why should you never check errno unless the function has already reported failure via its return value? Give an example of a function that may set errno to a nonzero value even on success. |
| 5. |
C |
Write a program that attempts to open a file that does not exist, a directory as if it were a regular file with O_WRONLY, and a path with a component that is not a directory. For each, print the specific errno value and the string returned by strerror(errno). |
| 6. |
C |
Write a wrapper function void die(const char *msg) that prints the message, appends the system error string using strerror(errno), and calls exit(1). Use it to wrap several failing syscalls in a small program. |
| 1. |
T |
What is the difference between exit(), _exit(), and _Exit()? Which ones flush stdio buffers? Which ones call atexit handlers? Which is a system call versus a library function? |
| 2. |
T |
What is the C standard's definition of normal program termination? List all the ways a process can terminate normally and abnormally. |
| 3. |
P |
A child process is created by fork() and does not call exec(). The child has data buffered in a FILE * stream. Why should the child call _exit() rather than exit() when it is done? What bug can occur if it calls exit()? |
| 4. |
P |
A program calls exit(256). What exit status does the parent see with WEXITSTATUS()? Explain why, and what the portable range for exit status values is. |
| 5. |
C |
Write a program that registers two atexit handlers. The first handler prints "handler A", the second prints "handler B". Demonstrate the LIFO (last-in-first-out) execution order. Then fork a child that calls _exit(0) and confirm the handlers are not invoked in the child. |
| 6. |
C |
Write a program that calls exit() from inside a function called from main. Show that atexit handlers still run. Contrast with calling return from main to demonstrate equivalent behavior. |
| 1. |
T |
What is syslog? Describe the openlog(), syslog(), and closelog() API. What are log facilities and log levels (priorities)? Give three examples of each. |
| 2. |
T |
How does the syslog daemon (syslogd / journald) receive log messages from processes? What IPC mechanism is used internally? |
| 3. |
P |
Why is syslog() the preferred logging mechanism for daemon processes rather than writing to a file or to stderr? |
| 4. |
P |
A daemon logs every incoming request at LOG_INFO level. In production, the system is flooded with log messages. How can the logging verbosity be adjusted without recompiling the daemon? |
| 5. |
C |
Write a daemon-like program that calls openlog("myapp", LOG_PID | LOG_CONS, LOG_DAEMON) and then logs one message at each of these levels: LOG_ERR, LOG_WARNING, LOG_INFO, and LOG_DEBUG. Call closelog() at exit. |
| 6. |
C |
Write a program that wraps syslog() in a helper function log_msg(int priority, const char *fmt, ...) using vsyslog(). Use it to log a formatted message including the caller's PID and a counter value. |
| 1. |
T |
What is advisory file locking? Why does it require cooperation between processes, and why does the kernel not enforce it automatically for all file accesses? |
| 2. |
T |
Describe the three lock types available through fcntl() locking: F_RDLCK, F_WRLCK, and F_UNLCK. What is the compatibility rule between read locks and write locks? |
| 3. |
P |
Two processes try to acquire a write lock on the same byte range of the same file. Describe what happens. Now one holds a read lock and the other requests a write lock. What happens? |
| 4. |
P |
What happens to fcntl() locks when the file descriptor is closed? What happens when the process exits? Why is this a common source of bugs in programs that use locking libraries? |
| 5. |
C |
Write a program that opens a file and applies a write lock to the entire file using fcntl() with F_SETLKW. While holding the lock, sleep for 5 seconds, then unlock. Run two instances simultaneously and observe that the second blocks until the first releases the lock. |
| 6. |
C |
Write a program that tests whether a write lock can be placed on a file (using F_GETLK) without actually acquiring it, and prints whether the file is locked, by which PID, and over which byte range. |
| 1. |
T |
What is the difference between a joinable thread and a detached thread? What happens to a thread's resources when it exits in each case? |
| 2. |
T |
In what two ways can a thread become detached? What happens if you call pthread_join() on a detached thread? |
| 3. |
P |
A program creates 1000 threads and never calls pthread_join() or pthread_detach(). The threads all exit quickly. What resource leak occurs, and what is the eventual consequence? |
| 4. |
P |
A detached thread is still running when the main thread calls return from main(). What happens to the detached thread? How is this different from the main thread calling pthread_exit()? |
| 5. |
C |
Write a program that creates 5 detached threads using pthread_attr_setdetachstate(). Each thread prints its ID and a message, then exits. The main thread sleeps long enough for all threads to finish, then prints "main done". |
| 6. |
C |
Write a program that creates a thread, and from inside the thread function, calls pthread_detach(pthread_self()) to detach itself. The main thread attempts to pthread_join() the same thread and handles the EINVAL error gracefully. |
| 1. |
T |
What is a condition variable? What problem does it solve that a mutex alone cannot? Describe the relationship between a condition variable and its associated mutex. |
| 2. |
T |
Why must the predicate (the condition being waited on) always be tested in a while loop rather than an if statement when using pthread_cond_wait()? What is a spurious wakeup? |
| 3. |
P |
A producer thread adds items to a queue and a consumer thread removes them. Sketch the synchronization needed: which mutex protects the queue, when is pthread_cond_signal() called, and what predicate does the consumer check in its while loop? |
| 4. |
P |
What is the difference between pthread_cond_signal() and pthread_cond_broadcast()? Give a scenario where using signal() instead of broadcast() would cause a deadlock or missed wakeup. |
| 5. |
C |
Write a producer-consumer program with one producer thread and one consumer thread sharing a buffer of capacity 1. The producer generates integers 1–10; the consumer prints them. Use a mutex and two condition variables (not_full, not_empty). |
| 6. |
C |
Write a program that uses a condition variable to implement a barrier for 3 threads: all threads must reach the barrier before any is allowed to continue. Use a counter, a mutex, and pthread_cond_broadcast(). |
| 1. |
T |
What does the O_APPEND flag do at the kernel level? What makes it different from manually calling lseek(fd, 0, SEEK_END) before each write()? |
| 2. |
T |
What is the POSIX guarantee about atomicity of write() to a regular file opened with O_APPEND? How does this guarantee break down on network filesystems? |
| 3. |
P |
Two processes open the same log file without O_APPEND and both call lseek(fd, 0, SEEK_END) followed by write(). Show a race-condition interleaving that causes one process's data to overwrite the other's. Now show why O_APPEND prevents this. |
| 4. |
P |
A process writes a 100-byte record to a file opened with O_APPEND. POSIX says writes up to PIPE_BUF to a pipe are atomic; is there a similar atomicity guarantee for O_APPEND writes to regular files on Linux? What actually happens for large writes? |
| 5. |
C |
Write a program that spawns 3 child processes. Each child opens the same file with O_WRONLY | O_CREAT | O_APPEND and writes 1000 lines of the form "child N: line M\n". After all children finish, the parent counts the total lines with read() and verifies no data was lost. |
| 6. |
C |
Write a program that demonstrates the race condition without O_APPEND: two threads share a file descriptor opened without O_APPEND, both seek to the end and write. Capture the interleaving by writing large enough blocks and show that data can be overwritten. Then fix it with O_APPEND. |
| 1. |
T |
What is the difference between an absolute path and a relative path? How does the kernel resolve each? At what point does a relative path become ambiguous? |
| 2. |
T |
What does realpath() do? What does it resolve — symlinks, ., .., and redundant slashes? What must be true of the path for realpath() to succeed? |
| 3. |
P |
A program accepts a user-supplied filename and passes it to fopen(). The user supplies "../../etc/passwd". Explain the security risk. What is a directory traversal attack and how can realpath() help mitigate it? |
| 4. |
P |
A daemon calls chdir("/var/myapp") and later constructs a path using a relative name. A second daemon instance also calls chdir("/var/myapp"). Is there any conflict between the two processes' working directories? Explain. |
| 5. |
C |
Write a program that takes a path (possibly relative, possibly containing symlinks and ..) as a command-line argument and prints its canonical absolute form using realpath(). Handle the case where the path does not exist. |
| 6. |
C |
Write a program that takes a user-supplied filename, resolves it with realpath(), and checks that the resolved path starts with a trusted prefix (e.g., /var/myapp/). If not, refuse to open it. Print "Access denied" or open and print the file. |
| 1. |
T |
Why is creating a temporary file with a predictable name (e.g., /tmp/prog_12345.tmp) a security vulnerability? Describe the time-of-check to time-of-use (TOCTOU) race condition involved. |
| 2. |
T |
How does mkstemp() solve the TOCTOU problem? What does it guarantee about the returned file descriptor? What are the permissions on the created file? |
| 3. |
P |
A program uses tmpnam() to get a temporary filename and then calls open() to create it. A malicious process running simultaneously creates a symlink at that path pointing to /etc/passwd. What can happen? |
| 4. |
P |
After mkstemp() returns a descriptor, the program calls unlink() on the filename immediately. Is the file still accessible through the descriptor? What is the benefit of unlinking it immediately? |
| 5. |
C |
Write a program that creates a temporary file using mkstemp() with template "/tmp/myapp_XXXXXX", writes some data to it, seeks back to the beginning, reads the data back and prints it, then closes and unlinks it. |
| 6. |
C |
Write a program that creates a temporary directory using mkdtemp(), creates three files inside it using mkstemp(), lists the directory with opendir()/readdir(), removes all files, and removes the directory with rmdir(). |
| 1. |
T |
What is mmap()? Explain its key parameters: addr, length, prot, flags, fd, and offset. What is the difference between MAP_SHARED and MAP_PRIVATE? |
| 2. |
T |
What is anonymous mapping (MAP_ANONYMOUS)? How can it be used for IPC between a parent and child? How does this differ from using a shared file mapping? |
| 3. |
P |
A program maps a file with MAP_SHARED | PROT_WRITE, modifies a byte in the mapped region, and then calls munmap(). Is the change reflected in the file? When exactly is the data written back to disk? |
| 4. |
P |
A program maps a 10 GB file into memory on a 64-bit system. No physical RAM for 10 GB is reserved upfront. Explain how this is possible using virtual memory and demand paging. What happens when a page is first accessed? |
| 5. |
C |
Write a program that maps a file into memory using mmap() with MAP_SHARED | PROT_READ | PROT_WRITE, uppercases every byte in the mapping, calls msync() to flush changes, and unmaps the region. Verify the file was modified. |
| 6. |
C |
Write a program that uses mmap() with MAP_SHARED | MAP_ANONYMOUS (no file) to share an integer counter between parent and child. The parent increments the counter 5 times; the child increments it 5 times. Print the final value from the parent after wait(). |
| 1. |
T |
What does times() return? Explain tms_utime, tms_stime, tms_cutime, and tms_cstime. What clock tick unit are they in, and how do you convert to seconds? |
| 2. |
T |
What is clock_gettime()? What are the differences between CLOCK_REALTIME, CLOCK_MONOTONIC, and CLOCK_PROCESS_CPUTIME_ID? Which should you use for measuring elapsed wall time? For measuring CPU usage? |
| 3. |
P |
A program measures execution time using time(NULL) (wall clock seconds). The machine is heavily loaded. Does this accurately measure the program's CPU usage? Which function and which clock ID would give a more accurate measure of CPU time? |
| 4. |
P |
CLOCK_MONOTONIC vs CLOCK_REALTIME: the system clock is adjusted (NTP step) while a program is timing an operation. Which clock is affected? Which is not? Which should be used for timeouts? |
| 5. |
C |
Write a program that uses clock_gettime(CLOCK_MONOTONIC, ...) to measure how long it takes to compute the sum of integers from 1 to 100,000,000. Print elapsed time in milliseconds with sub-millisecond precision. |
| 6. |
C |
Write a program that uses times() to print the user CPU time and system CPU time consumed by a child process. Fork a child that does significant computation, wait for it, and read tms_cutime and tms_cstime from the struct tms returned by the parent's times() call. |
| 1. |
T |
What does statvfs() return? List and explain at least six fields in struct statvfs: f_bsize, f_blocks, f_bfree, f_bavail, f_files, f_ffree. |
| 2. |
T |
What is the difference between f_bfree and f_bavail? Why do Unix filesystems reserve blocks for the superuser? |
| 3. |
P |
A program uses statvfs() to check available space before writing a large file. Calculate: if f_bavail is 500,000 and f_bsize is 4096, how many bytes of free space are available to a non-root user? |
| 4. |
P |
A disk has 1 million inodes total (f_files) and only 100 free (f_ffree), but gigabytes of free block space. Can you create new files? Explain the inode exhaustion problem. |
| 5. |
C |
Write a program that takes a path as an argument and prints: filesystem block size, total blocks, free blocks available to non-root users, total inodes, and free inodes. Also print total and used disk space in megabytes. |
| 6. |
C |
Write a program that monitors free disk space on a given path, checking every 2 seconds using statvfs(). If available space drops below 10%, print a warning "Low disk space!". Run it while filling the disk in another terminal. |
| 1. |
T |
What are readv() and writev()? What is the struct iovec structure? What advantage do they offer over multiple separate read()/write() calls, beyond convenience? |
| 2. |
T |
Is a single writev() call atomic with respect to other writers on the same file descriptor (e.g., a socket or pipe)? Under what conditions does the atomicity guarantee hold? |
| 3. |
P |
A network protocol message consists of a fixed-size header struct and a variable-length body. How does writev() allow sending both without copying them into a single contiguous buffer? Draw the iovec array. |
| 4. |
P |
A program reads a structured file format where each record has a 4-byte length field followed by data. How can readv() be used to read both the header and the data into separate buffers in one call? What must be known in advance? |
| 5. |
C |
Write a program that uses writev() to write three separate strings ("Hello, ", "world", "\n") to a file using a single system call via an array of three iovec structures. Verify the file contains the concatenation. |
| 6. |
C |
Write a program that reads a file using readv() into two separate buffers of 64 bytes each, then prints the contents of both buffers. Use lseek() to confirm the file offset advanced by the total number of bytes read. |