MODULE 3
*Note :-Content is taken from text book and pdf in simplified form
File Attributes (Linux / UNIX)
File attributes describe the properties and characteristics of a file stored in the file system.
These attributes provide information about the file’s type, size, ownership, permissions, and
timestamps, and are maintained by the operating system for file management and security.
In UNIX/Linux systems, file attributes are stored in the inode of the file.
Important File Attributes
1. File Type
● Indicates the type of the file.
● Common file types include:
○ Regular file
○ Directory
○ Character device
○ Block device
○ FIFO (pipe)
○ Symbolic link
● Helps the operating system decide how the file should be handled.
2. File Permissions
● Define access rights for a file.
● Permissions are divided into:
○ Read (r)
○ Write (w)
○ Execute (x)
● Permissions are specified for:
○ Owner
○ Group
○ Others
● Used to enforce security and access control.
3. Ownership (User ID and Group ID)
● Every file is owned by a user (UID) and a group (GID).
● Ownership determines who can access or modify the file.
● Helps in multi-user environments to manage file security.
4. File Size
● Represents the total size of the file in bytes.
● Used by the system to allocate disk space and manage storage.
● Can be zero for empty files.
5. Number of Links
● Indicates how many directory entries point to the same inode.
● Multiple links mean the file data is shared.
● When link count becomes zero, the file is deleted.
6. File Timestamps
Each file has three important timestamps:
● Access time (atime): Last time the file was read.
● Modification time (mtime): Last time file contents were modified.
● Change time (ctime): Last time file metadata was changed.
These timestamps are useful for backup and file monitoring.
7. Inode Number
● Each file has a unique inode number.
● Used by the file system to identify files internally.
● Links reference inode numbers, not filenames.
8. File Location (Pointers)
● Inode contains pointers to the disk blocks where file data is stored.
● Allows the system to retrieve file contents efficiently.
Library Functions (File Handling in
UNIX/Linux)
Library functions are high-level file handling functions provided by the C standard library.
These functions simplify file operations by providing buffered I/O, portability, and ease of use.
Unlike system calls, library functions internally use system calls but hide low-level details from
the programmer.
File library functions work on streams and are defined in the header file <stdio.h>.
What is a Stream?
A stream is a logical interface between a program and a file.
Each stream is represented by a FILE pointer (FILE *) and supports buffered input and
output.
Important File Library Functions
1. fopen()
● Used to open a file.
● Returns a pointer to a FILE structure.
● Requires filename and mode.
FILE *fp = fopen("[Link]", "r");
Common modes:
● r – read
● w – write
● a – append
● r+, w+, a+ – read and write
2. fclose()
● Used to close an open file.
● Flushes buffers and releases resources.
fclose(fp);
3. fgetc() and fputc()
● fgetc() reads one character from a file.
● fputc() writes one character to a file.
ch = fgetc(fp);
fputc(ch, fp);
Used for character-by-character I/O.
4. fgets() and fputs()
● fgets() reads a string (line) from a file.
● fputs() writes a string to a file.
fgets(str, size, fp);
fputs(str, fp);
Safer than gets().
5. fprintf() and fscanf()
● fprintf() writes formatted output to a file.
● fscanf() reads formatted input from a file.
fprintf(fp, "Age: %d", age);
fscanf(fp, "%d", &age);
Used when data has a specific format.
6. fread() and fwrite()
● Used for binary file operations.
● Read/write blocks of data.
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
Commonly used for structures and binary files.
7. fseek(), ftell(), rewind()
● fseek() moves the file pointer to a specific location.
● ftell() returns the current file pointer position.
● rewind() moves the pointer to the beginning of the file.
fseek(fp, 0, SEEK_SET);
pos = ftell(fp);
rewind(fp);
Used for random file access.
8. feof() and ferror()
● feof() checks for end of file.
● ferror() checks for file errors.
if (feof(fp)) { /* end reached */ }
Advantages of Library Functions
● Easy to use
● Provide buffered I/O (better performance)
● Portable across platforms
● Reduce programming complexity
Difference Between Library Functions and System Calls
(Short)
● Library functions are high-level
● System calls are low-level
● Library functions use buffering
● System calls work directly with the kernel
Standard I/O and Formatted I/O in C
In C programming, Input/Output (I/O) operations are performed using functions provided by the
standard I/O library. These functions make it easier to read data from input devices and write
data to output devices such as the keyboard, screen, and files.
All standard I/O and formatted I/O functions are declared in the header file <stdio.h>.
Standard I/O in C
Standard I/O refers to basic input and output operations that use predefined streams managed
by the C runtime system. These operations are buffered, which improves performance and
simplifies programming.
Standard Streams
C provides three standard I/O streams:
● stdin – standard input (keyboard)
● stdout – standard output (screen)
● stderr – standard error output (screen)
These streams are automatically opened when a program starts.
Standard I/O Functions
Standard I/O functions perform unformatted input and output.
Input Functions
● getchar() – reads a single character from standard input
● gets() (deprecated) – reads a line of text
● fgets() – safely reads a line of text from a stream
ch = getchar();
fgets(str, size, stdin);
Output Functions
● putchar() – writes a single character to standard output
● puts() – writes a string followed by a newline
● fputs() – writes a string to a stream
putchar(ch);
puts("Hello");
fputs(str, stdout);
These functions are simple and do not perform format conversion.
Formatted I/O in C
Formatted I/O allows input and output of data in a specified format. It is used when data
needs to be read or written in a structured or formatted way.
Formatted I/O functions use format strings containing conversion specifiers.
Formatted Output Functions
printf()
● Prints formatted output to standard output.
● Uses format specifiers to control output format.
printf("Age = %d, Salary = %.2f", age, salary);
Common format specifiers:
● %d – integer
● %f – float
● %c – character
● %s – string
fprintf()
● Writes formatted output to a file or stream.
fprintf(fp, "Marks = %d", marks);
Formatted Input Functions
scanf()
● Reads formatted input from standard input.
● Matches input according to format specifiers.
scanf("%d %f", &x, &y);
fscanf()
● Reads formatted input from a file or stream.
fscanf(fp, "%d", &value);
Difference Between Standard I/O and Formatted I/O
Standard I/O Formatted I/O
Simple input/output Structured input/output
No format conversion Uses format specifiers
Example: Example: printf()
getchar()
Easier but limited More flexible
Advantages of Standard I/O
● Easy to use
● Buffered I/O improves performance
● Portable across platforms
● Reduces complexity of low-level I/O
Advantages of Formatted I/O
● Supports formatted data handling
● Useful for reports and structured files
● Allows control over data representation
Stream Errors (in C – Standard I/O)
In C, streams are used for input and output operations through the standard I/O library
(<stdio.h>). During file or stream operations, errors or exceptional conditions may occur,
such as reaching the end of a file or encountering an I/O failure. These conditions are called
stream errors and are handled using specific library functions.
Types of Stream Conditions
When performing stream I/O, two main conditions can occur:
1. End-of-File (EOF) condition
2. Error condition
Both are recorded internally in the stream structure.
End-of-File (EOF)
● EOF occurs when an input operation attempts to read beyond the available data in a
stream.
● It indicates that there is no more data to read.
● EOF is not an error; it is a normal condition.
Function Used: feof()
● Checks whether the end-of-file indicator is set for a stream.
● Returns non-zero if EOF is reached, otherwise zero.
if (feof(fp)) {
printf("End of file reached\n");
}
Stream Error Condition
● A stream error occurs when an input or output operation fails.
● Causes include:
○ Disk failure
○ Permission denied
○ Hardware or I/O errors
● Unlike EOF, this represents an abnormal condition.
Function Used: ferror()
● Checks whether an error has occurred on a stream.
● Returns non-zero if an error is present.
if (ferror(fp)) {
printf("Error occurred while accessing the file\n");
}
Clearing Stream Errors
Function: clearerr()
● Clears both EOF and error indicators associated with a stream.
● Used when you want to reuse a stream after handling an error or EOF.
clearerr(fp);
Return Values and Stream Errors
● Many standard I/O functions return special values on error:
○ fgetc() returns EOF
○ fgets() returns NULL
● To distinguish between EOF and an error:
○ Use feof() and ferror() after the function call
Kernel Support for Files (UNIX/Linux)
UNIX supports sharing of open files among multiple processes.
To manage this efficiently, the kernel uses three interconnected data structures:
1. Per-process file descriptor table
2. System-wide file table
3. V-node (virtual node) table
The interaction between these structures determines how file sharing, offsets, and flags behave
across processes.
1. Process Table and File Descriptor Table
● Each process has an entry in the process table
● Inside each process entry is a file descriptor table
● This table is a vector indexed by file descriptor numbers (0, 1, 2, …)
Each file descriptor entry contains:
1. File descriptor flags
○ Close-on-exec (FD_CLOEXEC)
2. Pointer to a file table entry
👉 File descriptor flags are private to the process
Example:
● fd 0 → standard input
● fd 1 → standard output
● fd 2 → standard error
2. File Table (System-Wide Open File Table)
● Maintained by the kernel
● Contains one entry per open instance of a file
Each file table entry contains:
1. File status flags
○ Read, write, append (O_APPEND)
○ Nonblocking, sync, etc.
2. Current file offset
3. Pointer to the v-node table entry
👉 File status flags and offsets are shared by all file descriptors pointing to the same file
table entry
3. V-node Table (Virtual Node Table)
● Each open file or device has one v-node
● The v-node:
○ Identifies the file type
○ Contains pointers to file operation functions
○ Usually contains or references the inode
Inode information includes:
● File size
● Ownership
● Permissions
● Disk block locations
👉 The inode is read from disk when the file is opened and stored in memory for fast access.
Relationship Between the Three Tables
Process → File Descriptor Table → File Table → V-node → Inode
Example: Two Processes Opening the Same File
● Process 1 opens a file → gets file descriptor 3
● Process 2 opens the same file → gets file descriptor 4
Important behavior:
● Each process gets:
○ Its own file descriptor entry
○ Its own file table entry
● Both file table entries point to:
○ The same v-node (and inode)
Why separate file table entries?
Because:
● Each process needs its own current file offset
File Offset Handling
Write operation:
● After each write():
○ The current file offset in the file table entry increases
○ If offset exceeds file size:
■ File size in the inode is updated (file grows)
O_APPEND Flag Behavior
● If a file is opened with O_APPEND:
○ Append flag is stored in the file status flags (file table entry)
● Before each write():
File offset is automatically set to:
current file size (from inode)
○
● Ensures:
○ All writes go to the end of the file
○ Prevents race conditions when multiple processes append
lseek() Behavior
● lseek():
○ Only changes the current file offset
○ Does not perform any I/O
Seeking to end of file:
● Offset is set to:
file size from inode
File Descriptors (FDs)
What is a File Descriptor?
A file descriptor (FD) is a small non-negative integer used by the operating system to
identify an open file within a process.
👉 It acts as a handle for files, devices, pipes, and sockets.
Standard File Descriptors
FD Name Purpose
0 stdin Standard input
1 stdout Standard
output
2 stderr Standard error
How File Descriptors Are Created
● Returned by system calls such as:
○ open()
○ pipe()
○ socket()
Example:
int fd = open("[Link]", O_RDONLY);
How File Descriptors Are Used
FDs are passed to system calls:
● read(fd, ...)
● write(fd, ...)
● lseek(fd, ...)
● close(fd)
Example:
read(fd, buffer, 100);
write(fd, buffer, 100);
close(fd);
Important Properties of File Descriptors
1. Process-Specific
● Each process has its own FD table
● Same number in two processes can refer to different files
2. Points to File Table Entry
● FD → file table → vnode → inode
● Actual file data is not stored in FD
3. Multiple FDs Can Refer to Same File
Happens due to:
● dup()
● fork()
👉 These FDs share file offset and status flags
4. File Offset Handling
● Offset is maintained in the file table
● Moves automatically after read() / write()
● Changed manually using lseek()
dup() and dup2() (IMPORTANT)
dup()
● Creates a copy of a file descriptor
int newfd = dup(fd);
dup2()
● Copies fd to a specific descriptor number
dup2(fd, 1); // redirect stdout
👉 Used for I/O redirection
Closing File Descriptors
close()
● Releases the FD
● Frees kernel resources
close(fd);
👉 File is removed only when:
● FD count = 0
● Link count = 0
File Descriptors vs FILE *
File Descriptor FILE *
Integer Pointer
Low-level High-level
System calls Library functions
Unbuffered Buffered
One-Line Exam Summary
A file descriptor is a process-specific integer that identifies an open file and
is used by system calls to perform I/O.
Low-Level File Access (UNIX / Linux)
What is Low-Level File Access?
Low-level file access means accessing files directly using system calls provided by the
kernel.
👉 It works with file descriptors, not FILE *.
Core Low-Level File System Calls (VERY IMPORTANT)
1. open()
Opens or creates a file and returns a file descriptor.
int fd = open("[Link]", O_RDONLY);
Common flags:
● O_RDONLY – read only
● O_WRONLY – write only
● O_RDWR – read & write
● O_CREAT – create file
● O_APPEND – append mode
2. read()
Reads data from a file.
read(fd, buffer, size);
● Reads from current file offset
● Moves offset automatically
3. write()
Writes data to a file.
write(fd, buffer, size);
● Writes at current offset
● Offset increases after writing
● With O_APPEND, writes always go to end of file
4. lseek()
Changes the file offset.
lseek(fd, 0, SEEK_END);
Seek modes:
● SEEK_SET – beginning
● SEEK_CUR – current position
● SEEK_END – end of file
👉 No I/O occurs, only offset change
5. close()
Closes the file descriptor.
close(fd);
● Frees kernel resources
File Structure–Related System Calls (File
APIs)
These system calls are used to create, access, modify, and manage files and directories.
They work at low level, directly with the kernel, using file descriptors.
1. File Creation & Opening
open()
● Opens an existing file or creates a new file
● Returns a file descriptor
int fd = open("[Link]", O_RDONLY);
Common flags:
● O_RDONLY – read only
● O_WRONLY – write only
● O_RDWR – read & write
● O_CREAT – create file
● O_APPEND – append mode
👉 Each open() creates a new open file instance
2. File Read & Write Operations
read()
● Reads data from a file
● Uses current file offset
read(fd, buffer, size);
write()
● Writes data to a file
● Updates file offset after writing
write(fd, buffer, size);
👉 With O_APPEND, data is always written at end of file
3. File Offset Control
lseek()
● Changes the file offset
● Does not perform I/O
lseek(fd, 0, SEEK_END);
Modes:
● SEEK_SET – beginning
● SEEK_CUR – current position
● SEEK_END – end of file
4. Closing Files
close()
● Closes a file descriptor
● Frees kernel resources
close(fd);
5. File Information (Metadata)
stat(), fstat(), lstat()
● Get file metadata from inode
stat("[Link]", &buf);
Provides:
● File size
● Permissions
● Owner
● Timestamps
6. File Linking & Deletion
link()
● Creates a hard link
link("file1", "file2");
symlink()
● Creates a symbolic (soft) link
symlink("file1", "link1");
unlink()
● Removes a file name
● File deleted when link count = 0
unlink("[Link]");
7. File Permission & Ownership
chmod()
● Changes file permissions
chmod("[Link]", 0644);
chown()
● Changes owner and group
chown("[Link]", uid, gid);
8. Directory Structure System Calls
mkdir()
● Creates a directory
mkdir("dir", 0755);
rmdir()
● Removes empty directory
rmdir("dir");
chdir() / getcwd()
● Change / get current working directory
chdir("/home/user");
getcwd(buf, size);
9. File Descriptor Manipulation
dup() / dup2()
● Duplicates file descriptors
● Used in I/O redirection
dup(fd);
dup2(fd, 1);
File and Directory Management
Directory File APIs (UNIX / Linux)
Directories are special files that store names of files and their inode numbers.
UNIX provides directory APIs to create, remove, navigate, and read directory contents.
1. Directory Creation & Removal
mkdir()
● Creates a new directory
mkdir("dir", 0755);
👉 Permissions are affected by umask
rmdir()
● Removes an empty directory
rmdir("dir");
👉 Directory must be empty
2. Changing & Getting Current Directory
chdir()
● Changes current working directory
chdir("/home/user");
getcwd()
● Returns current working directory path
getcwd(buf, size);
3. Opening & Reading a Directory (MOST IMPORTANT)
opendir()
● Opens a directory stream
DIR *dp = opendir("dir");
readdir()
● Reads next directory entry
struct dirent *entry;
entry = readdir(dp);
Returns:
● File name
● Inode number
closedir()
● Closes directory stream
closedir(dp);
4. Directory Positioning APIs
telldir()
● Returns current position in directory stream
long pos = telldir(dp);
seekdir()
● Moves to a specific directory position
seekdir(dp, pos);
rewinddir()
● Resets directory stream to beginning
rewinddir(dp);
5. Directory Entry Structure
struct dirent
Contains:
● d_name – file name
● d_ino – inode number
struct dirent {
ino_t d_ino;
char d_name[];
};
6. Directory APIs Summary Table (EXAM READY)
Function Purpose
mkdir() Create directory
rmdir() Remove empty directory
chdir() Change directory
getcwd() Get current directory
opendir() Open directory
readdir() Read directory entry
closedir( Close directory
)
telldir() Get position
seekdir() Set position
rewinddir Reset position
()
Symbolic Links & Hard Links (UNIX /
Linux)
What is a Link?
A link is an additional name for an existing file.
👉 Links allow multiple file names to refer to the same data.
1. Hard Links
What is a Hard Link?
A hard link is another directory entry that points to the same inode as the original file.
Creation
ln file1 file2
Key Characteristics
● Both names share the same inode number
● No “original” or “copy” — both are equal
● File data exists as long as at least one hard link exists
● Deleting one name does not delete the file
Limitations
● Cannot link directories (normally)
● Cannot span across different file systems
2. Symbolic (Soft) Links
What is a Symbolic Link?
A symbolic link is a special file that stores the path name of another file.
Creation
ln -s file1 link1
Key Characteristics
● Has a different inode
● Points to file by path, not inode
● Can link directories
● Can span different file systems
Broken Link
● If original file is deleted → symbolic link becomes invalid
3. Inode Perspective (VERY IMPORTANT)
Feature Hard Link Symbolic Link
Inode Same Different inode
inode
Stores File data File path
Link count Increases No effect
Breaks if original deleted ❌ No ✅ Yes
4. Deleting Files & Links (IMPORTANT)
Hard Link Deletion
● Removes only the name
● File deleted only when link count = 0
Symbolic Link Deletion
● Deletes only the link file
● Original file unaffected
5. Viewing Links
ls -l
ls -i
● Hard links → same inode number
● Symbolic links → l at start and -> shows target
6. System Calls Related to Links
System Purpose
Call
link() Create hard link
symlink() Create symbolic link
unlink() Remove a link
readlink( Read symbolic link
)
7. One-Line Exam Answers
● Hard link: Another name for a file that points to the same inode
● Symbolic link: A special file that contains the path to another file
8. Quick Exam Comparison Table
Aspect Hard Link Symbolic Link
File type Normal file entry Special file
Inode Same Different
Cross filesystem No Yes
Directory link No Yes
Breaks on No Yes
delete
Examples of Hard Links & Symbolic Links
1. Hard Link Example
Step 1: Create a file
echo "Hello Linux" > [Link]
Step 2: Create a hard link
ln [Link] [Link]
Step 3: Check inode numbers
ls -i [Link] [Link]
Output:
12345 [Link]
12345 [Link]
👉 Both files have the same inode number → hard link
Step 4: Delete one file
rm [Link]
Step 5: Check remaining file
cat [Link]
Output:
Hello Linux
👉 Data still exists because inode link count > 0
2. Symbolic (Soft) Link Example
Step 1: Create a symbolic link
ln -s [Link] [Link]
Step 2: View link
ls -l [Link]
Output:
lrwxrwxrwx 1 user user 9 [Link] -> [Link]
👉 l indicates symbolic link
Step 3: Check inode numbers
ls -i [Link] [Link]
Output:
12345 [Link]
67890 [Link]
👉 Different inode numbers → symbolic link
Step 4: Delete original file
rm [Link]
Step 5: Try accessing symbolic link
cat [Link]
Output:
cat: [Link]: No such file or directory
👉 Symbolic link is now broken
1. Process Concept
What is a Process?
A process is a program in execution.
👉👉Aa program is passive (stored on disk),
process is active (running in memory).
What a Process Contains
A process consists of:
● Program code (text section)
● Data section (global variables)
● Heap (dynamic memory)
● Stack (function calls, local variables)
● CPU registers
● Program Counter
● Process ID (PID)
Process States (IMPORTANT)
State Meaning
New Process is being
created
Ready Waiting for CPU
Running Currently executing
Waiting / Blocked Waiting for I/O or event
Terminated Finished execution
Process Identifier (PID)
● Each process has a unique PID
● Parent process ID = PPID
getpid();
getppid();
Kernel Support for Process (Easy
Explanation)
Role of the Kernel
The kernel is the core of the Unix/Linux operating system.
It controls and manages everything that happens in the system.
👉👉User programs do not directly access hardware.
All requests go through the kernel.
Main Responsibilities of the Kernel
The kernel supports UNIX requirements in two major areas:
1. Process management
2. File management (includes device files)
1. Process Management (IMPORTANT)
Process management means the kernel:
● Creates processes
● Allocates resources
● Controls execution
Kernel support includes:
● CPU allocation (decides which process runs)
● Memory allocation (assigns RAM to processes)
● Process scheduling (time sharing using clock interrupts)
👉 Kernel gives the illusion of parallel execution using time slicing.
2. File Management (IMPORTANT)
File management includes:
● Handling regular files
● Handling device files
● Communication with device drivers
● Managing data transfer between memory and peripherals
👉 Devices are treated as files in UNIX.
Hiding Hardware Complexity
● Hardware and peripherals work asynchronously
● Kernel hides this complexity
● User processes experience synchronous execution
Example:
● Disk I/O may take time
● Kernel blocks process and resumes it later
Summary: Kernel Support Operations (EXAM POINTS)
The kernel performs the following functions:
1. Process Scheduling
○ Decides which process runs and when
2. Memory Allocation
○ Assigns and manages memory for processes
3. Swapping
○ Moves processes between memory and disk
4. I/O Management
○ Transfers data between processes and peripherals
5. System Call Handling
○ Receives service requests from processes
○ Executes them safely in kernel mode
One-Line Exam Answer
Kernel support for processes includes scheduling, memory management,
swapping, I/O handling, and servicing process requests to ensure efficient
and safe execution.
Process Attributes
What are Process Attributes?
Process attributes are the properties and information that describe a process and are
maintained by the kernel in the process table (PCB).
👉 They help the kernel identify, manage, and control each process.
Important Process Attributes
1. Process Identification
● PID (Process ID) – unique number for each process
● PPID (Parent Process ID) – ID of parent process
getpid();
getppid();
2. Process State
Indicates the current status of the process.
State Meaning
New Being created
Ready Waiting for CPU
Running Executing
Waiting / Blocked Waiting for I/O
Terminated Finished
3. CPU Context Information
Stored during context switch:
● Program Counter
● CPU registers
● Stack pointer
👉 Allows process to resume execution correctly
4. Scheduling Information
● Process priority
● CPU time used
● Scheduling class / policy
● Time slice
👉 Used by the scheduler to select next process
5. Memory Management Information
● Base and limit registers
● Page tables / segment tables
● Address space (text, data, heap, stack)
👉 Helps kernel manage process memory
6. File Descriptor Information
● Table of open file descriptors
● Each FD points to:
○ File table entry
○ Inode / vnode
👉 Enables file and device access
7. Process Credentials
● User ID (UID)
● Group ID (GID)
● Effective UID/GID
👉 Used for access control and security
8. Signal Information
● Signals pending
● Signal handlers
● Signal masks
👉 Used for process control and communication
9. Accounting Information
● CPU usage time
● Start time
● Resource usage
👉 Used for performance tracking
Process Control – Process Creation
What is Process Creation?
Process creation is the mechanism by which the kernel creates a new process so that a
program can be executed.
In UNIX/Linux, a new process is created using the fork() system call, and a new program is
loaded using exec().
Role of Kernel in Process Creation
The kernel is fully responsible for creating and managing processes.
When a process creation request is made, the kernel:
● Allocates a new process entry
● Assigns a unique process ID (PID)
● Manages memory and resources
● Controls execution and scheduling
Main System Calls Used in Process Creation
1. fork() – Create a Process (MOST IMPORTANT)
What fork() does:
● Creates a child process from a parent process
● Child is an almost exact copy of the parent
pid = fork();
Return values:
● 0 → returned to child
● > 0 → returned to parent (child’s PID)
● -1 → error (process not created)
👉 After fork(), both parent and child run concurrently
What the Kernel Does During fork()
The kernel:
1. Creates a new entry in the process table
2. Assigns a new PID to the child
3. Copies:
○ Program code
○ Data
○ Stack
○ File descriptors
4. Sets parent–child relationship
5. Schedules both processes
👉 File descriptors are shared (point to same file table entries)
2. exec() – Load a New Program
Purpose:
● Replaces the current process image with a new program
execl("/bin/ls", "ls", NULL);
👉 Important:
● PID does not change
● Memory contents are replaced
● Used after fork() to run a different program
3. wait() – Parent Synchronization
wait(&status);
● Parent waits for child to finish
● Collects exit status
● Prevents zombie processes
Typical Process Creation Sequence (VERY IMPORTANT)
Parent process
|
fork()
|
Child process
|
exec()
|
New program runs
👉 This is the standard UNIX process creation model
Waiting for a Process
What does “waiting for a process” mean?
Waiting for a process means that a parent process pauses its execution until one of its
child processes terminates.
We can arrange for the parent process to wait until the child finishes before continuing by calling
wait.
The wait system call causes a parent process to pause until one of its child processes dies or is
stopped.
👉 This is done to:
● Synchronize parent and child
● Collect the child’s exit status
● Avoid zombie processes
Why is Waiting Needed? (IMPORTANT)
If a parent does not wait for its child:
● The child finishes execution
● But its entry remains in the process table
● This creates a zombie process
👉 Waiting allows the kernel to clean up child process resources
System Calls Used for Waiting
1. wait()
pid_t wait(int *status);
Function:
● Parent process blocks
● Waits until any child process terminates
● Returns PID of terminated child
Important Points:
● Parent sleeps until child exits
● Exit status is stored in status
● Prevents zombie processes
Example:
int status;
wait(&status);
2. waitpid() (More Control)
pid_t waitpid(pid_t pid, int *status, int options);
Function:
● Parent waits for:
○ A specific child, or
○ Any child
● Can be blocking or non-blocking
Important Options:
● pid > 0 → wait for specific child
● pid = -1 → wait for any child
● WNOHANG → non-blocking wait
Example:
waitpid(pid, &status, 0);
Process Termination
What is Process Termination?
Process termination is the act of ending a process and releasing all resources held by it.
A process may terminate:
● Normally (on completion)
● Abnormally (due to error or signal)
Ways a Process Can Terminate (IMPORTANT)
1. Normal Termination
Occurs when a process finishes execution.
exit()
● Called by a process to terminate itself
exit(status);
● status is returned to the parent
● Kernel:
○ Releases memory
○ Closes open files
○ Sends termination signal to parent
2. Returning from main()
● Equivalent to calling exit()
int main() {
return 0;
}
3. Abnormal Termination
Occurs due to errors or signals.
Examples:
● Division by zero
● Illegal memory access
● Explicit kill signal
4. Termination by Signal
kill()
● Sends a signal to terminate a process
kill(pid, SIGTERM);
kill(pid, SIGKILL);
Common signals:
Signal Meaning
SIGTERM Graceful
termination
SIGKILL Forceful
termination
SIGABRT Abort process
SIGSEGV Segmentation
fault
Orphan process
An orphan process is a process in an operating system whose parent process
has terminated or exited while the child process is still running. In simple
terms, the parent process is no longer available to manage the child process,
leaving it "orphaned." To ensure the orphan process continues to function
properly, the operating system reassigns it to a system process like the init
process (in Linux/Unix systems).
Orphan processes are a common occurrence and do not typically cause major
issues, as they are managed automatically by the operating system.
An orphan process is created when:
1. The parent process terminates or crashes unexpectedly and leaves
its child processes still running.
2. A parent process completes execution before its child processes
finish their tasks.
3. An application design flaw causes the parent to exit before handling
its child processes properly.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t pid;
// Fork a child process
pid = fork();
if (pid > 0) {
// Parent process
printf("Parent process (PID: %d) is terminating.\n", getpid());
exit(0); // Parent terminates
} else if (pid == 0) {
// Child process
sleep(5); // Simulate work being done by the child
printf("Child process (PID: %d, PPID: %d) is now an orphan and adopted by init.\n",
getpid(), getppid());
} else {
// Fork failed
perror("Fork failed");
exit(1);
}
return 0;
}
Zombie Process
What is a Zombie Process?
A zombie process is a process whose execution is completed but it still has an
entry in the process table. Zombie processes usually occur for child processes,
as the parent process still needs to read its child’s exit status. Once this is done
using the wait system call, the zombie process is eliminated from the process
table. This is known as reaping the zombie process.
A diagram that demonstrates the creation and termination of a zombie process
is given as follows −
Zombie processes do not consume system resources like CPU or memory,
but they can clutter the process table if not handled properly, especially if
many zombie processes accumulate.
● A zombie process occurs when a child process finishes but the
parent process has not called wait() to read its status.
● It is not an active process and does not consume CPU or memory
but it can still occupy an entry in the process table.
● Too many zombie processes can cause issues by filling up the
process table, limiting the creation of new processes.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
}
else if (pid == 0) {
// Child process
printf("Child process (PID: %d) exiting...\n", getpid());
exit(0); // Child terminates immediately
else {
// Parent process
printf("Parent process (PID: %d) created a child (PID: %d)\n", getpid(), pid);
printf("Parent is sleeping... check 'ps -l' to see the Zombie process.\n");
sleep(10); // Keep parent alive so child stays as a zombie
printf("Parent exiting now, zombie will be cleared.\n");
return 0;
}
PROCESS APIs (UNIX / Linux)
Process APIs are system calls provided by the kernel to create, control, synchronize, and
terminate processes.
1. Process Creation APIs
fork()
● Creates a new process
● Child is a copy of parent
pid_t pid = fork();
Return value:
● 0 → child process
● >0 → parent process (child PID)
● -1 → error
👉 Most important process creation call
exec() Family
● Replaces current process image with a new program
● PID remains the same
Common functions:
● execl()
● execv()
● execvp()
execl("/bin/ls", "ls", NULL);
👉 Used after fork() to run a new program
2. Process Synchronization APIs (Waiting)
wait()
● Parent waits for child to terminate
● Collects exit status
wait(&status);
waitpid()
● Waits for a specific child
waitpid(pid, &status, 0);
👉 Prevents zombie processes
3. Process Termination APIs
exit()
● Terminates a process normally
● Sends exit status to parent
exit(0);
_exit()
● Terminates process immediately
● No cleanup
_exit(0);
abort()
● Abnormal termination
abort();
4. Process Identification APIs
getpid()
● Returns process ID
getpid();
getppid()
● Returns parent process ID
getppid();
5. Process Control & Signals
kill()
● Sends signal to a process
kill(pid, SIGTERM);
Common signals:
● SIGKILL – force kill
● SIGTERM – terminate
● SIGSTOP – stop
● SIGCONT – continue
6. Process Group & Session APIs
setsid()
● Creates a new session
setsid();
getpgid()
● Get process group ID
7. Summary Table (EXAM READY)
API Purpose
fork() Create process
exec() Load new program
wait() Wait for child
waitpid Wait for specific
() child
exit() Terminate process
getpid( Get process ID
getppid Get parent PID
()
kill() Send signal