Linux Unit 4 Complete
Linux Unit 4 Complete
Unit 4
Processes in Linux
A program/command when executed, a special instance is provided by the system to the process. This
instance consists of all the services/resources that may be utilized by the process under execution.
● Whenever a command is issued in Unix/Linux, it creates/starts a new process. For example, pwd
when issued which is used to list the current directory location the user is in, a process starts.
● Through a 5 digit ID number Unix/Linux keeps an account of the processes, this number is called
process ID or PID. Each process in the system has a unique PID.
● Used up pid’s can be used in again for a newer process since all the possible combinations are used.
● At any point of time, no two processes with the same pid exist in the system because it is the pid that
Unix uses to track each process.
Process Identifiers
Process identifiers (PIDs) are unique values that are automatically assigned to processes on a Linux
system. PIDs start from 0. The process that has the id 0 is part of the kernel and is not regarded as
a normal user-mode process.
The process with the ID of the value 1 is the init process. The init process is the first process that is
sprung up when a system starts. There is a maximum limit for the number of processes a system
can have. Usually, this depends on the memory capacity of the system.
Process Structure
In Linux, the term "process structure" typically refers to the data structure used by the operating system
to manage and control individual processes. Each process in Linux has a corresponding process structure
that contains information about the process's state, resources, and execution context. Here's an
overview of the key components of a process structure in Linux:
These are some of the key components of a process structure in Linux. The kernel uses this data to
manage and control the execution of processes, allocate resources, and maintain the integrity and
security of the system. Each process on a Linux system has its own unique process structure that allows
the kernel to manage and control it independently.
Attribute Description
Process ID (PID) Every process is assigned a unique identifier (PID) to track and manage it.
Parent Process ID The ID of the parent process that created or spawned the current process (except
(PPID) for PID 1, e.g., init/systemd).
User and Group IDs IDs that determine the user and group permissions for the process.
Process State Current state of the process (e.g., Running, Sleeping, Stopped, Zombie).
Program Counter (PC) Stores the address of the next instruction to be executed by the process.
Registers Includes CPU register states such as general-purpose registers, stack pointer, etc.
Process Priority Defines the priority of the process, influencing its scheduling for execution.
File Descriptors References to open files or network connections used by the process.
Information about the process's memory allocation, such as address space layout
Memory Management
and memory limits.
Signal Handlers Contains instructions for handling specific signals received by the process.
Parent-Child
Tracks hierarchical relationships, including child processes created by the process.
Relationships
Resource Limits Specifies limits on resources like CPU time, memory usage, and open files.
Executable Path The path to the program or binary associated with the process.
Process Tables
The "process table" typically refers to a data structure maintained by the kernel that keeps track of
information about all running processes on the system. This data structure is essential for process
management and resource allocation. The process table is often implemented as an array or a similar
data structure where each entry corresponds to a process.
It allows the kernel to allocate resources, schedule CPU time, track process relationships, and enforce
security and resource limits. When you view a list of processes using commands like ps or top, you are
essentially accessing information from the process table to monitor the status of processes on your Linux
system.
Process ID (PID): A unique identifier assigned to each process. PIDs are used to distinguish and manage
individual processes.
Parent Process ID (PPID): The PID of the parent process that spawned or created the current process.
This field indicates the hierarchical relationship between processes.
User and Group IDs: The user and group IDs associated with the process, which determine the process's
permissions and access rights.
Process State: Indicates the current state of the process, such as "Running," "Sleeping," "Stopped," or
"Zombie."
Program Counter (PC): The memory address of the next instruction to be executed by the process.
Registers: A set of CPU registers that store the current state of the process, including the values of
general-purpose registers and the stack pointer.
Priority and Scheduling Information: Information related to the process's scheduling priority and
scheduling policy, which determine when and how the process gets CPU time.
File Descriptor Table: A table that lists the open files and network sockets associated with the process.
File descriptors are small integers that reference these open resources.
Memory Management Information: Details about the process's memory allocation, including the
address space layout, memory limits, and pointers to the program's code and data.
Signal Handlers: Information about how the process should handle different types of signals sent by
other processes or the kernel.
Resource Limits: Information about resource limits imposed on the process, such as CPU time, memory
usage, and open file limits.
Environment Variables: Information about the environment variables set for the process.
Executable Path: The path to the program or binary associated with the process.
Timers and Alarms: Information about timers and alarms set by the process.
Other Process-Specific Information: Additional data related to the process, which may vary depending
on the specific implementation and kernel version.
1. Created: A process is created when a program is executed. At this stage, the process is in a
"created" state, and its data structures are initialized.
2. Ready: The process enters the "ready" state when it is waiting to be assigned to a processor by
the Linux scheduler. At this stage, the process is waiting for its turn to execute.
3. Running: The process enters the "running" state when it is assigned to a processor and is actively
executing its instructions.
4. Waiting: The process enters the "waiting" state when it is waiting for some event to occur, such
as input/output completion, a signal, or a timer. At this stage, the process is not actively
executing its instructions.
5. Terminated: The process enters the "terminated" state when it has completed its execution or
has been terminated by a signal. At this stage, the process data structures are removed, and its
resources are freed.
6. Zombie: A process enters the "zombie" state when it has completed its execution but its parent
process has not yet read its exit status. At this stage, the process details still have an entry in the
process table, but it does not execute any instructions. The zombie process is removed from the
process table when its parent process reads its exit status.
ps: The ps command is a versatile utility for displaying information about processes. You can use it with
various options to customize the output. For example:
htop: htop is an enhanced alternative to top. It provides a more user-friendly and customizable interface
for viewing and managing processebs. To use it, you need to install it if it's not already available on your
system:
pgrep and pkill: These commands are used to search for and signal processes based on their names or
attributes. For example:
ps -p PID
System Process
In Linux, system processes are background processes that are essential for the functioning of the
operating system. These processes run in the background and perform various system-level tasks and
services, helping to maintain system stability and provide necessary functionalities. Here are some
common system processes you may encounter on a Linux system:
1. init or systemd: Traditionally, the init process was responsible for managing system initialization and
spawning other processes. However, many modern Linux distributions have transitioned to using
systemd as the init system. systemd is responsible for initializing the system and managing various
system services and daemons.
2. kthreadd: This kernel thread is responsible for creating and managing other kernel threads. It plays a
crucial role in managing kernel-level tasks and processes.
3. ksoftirqd: These are kernel threads that handle software interrupts, which are typically related to
network and I/O operations. They help ensure efficient handling of these interrupts.
4. kworker: Kernel worker threads that perform various background tasks for the kernel, such as managing
hardware events and asynchronous I/O operations.
5. kswapd: The kernel swap daemon manages the system's swap space, which is used when physical
memory (RAM) is exhausted. It helps in optimizing memory usage.
6. jbd2: The Journaling Block Device 2 (jbd2) process is associated with file systems that use journaling,
such as ext4. It is responsible for journaling file system operations, ensuring data consistency.
7. dbus-daemon: The D-Bus daemon facilitates inter-process communication (IPC) between various
software components on a Linux system. It enables applications and services to communicate with each
other.8
8. cron and crond: The cron daemon is responsible for executing scheduled tasks at specific times or
intervals. It reads and processes crontab files that specify when and what tasks to run.
9. sshd: The SSH (Secure Shell) daemon handles incoming SSH connections, allowing secure remote access
to the system.
10.rsyslogd: The syslog daemon is responsible for collecting, processing, and forwarding system logs and
messages.
11.udevd: The udev daemon manages and handles dynamic device events. It plays a crucial role in device
detection and management.
12.acpid: The ACPI (Advanced Configuration and Power Interface) daemon manages power management
events and ACPI-related tasks, including system power states and device control.
13.Network daemons: Various network-related daemons, such as networkd or NetworkManager, manage
network interfaces and connectivity.
14.Printing daemons: Daemons like cupsd manage printing services on the system, allowing users to print
documents.
15.Hardware-related daemons: Depending on the hardware and drivers installed, there may be additional
system processes responsible for managing hardware components like sound servers (pulseaudio) or
graphics-related processes (Xorg or Wayland).
These are just a few examples of system processes in Linux. The specific set of system processes and
their names can vary depending on the Linux distribution and system configuration. System processes
are fundamental to the proper functioning of the operating system and various services running on the
system.
1. Process Priority: Each process in Linux is assigned a priority value, which determines its relative
importance for CPU allocation. Priorities range from -20 (highest priority) to 19 (lowest priority).
Negative values indicate higher priorities, while positive values indicate lower priorities.
2. Nice Value: The nice value is a user-defined parameter that allows users and administrators to adjust the
priority of a process. Higher nice values (e.g., nice +10) make a process less important, while lower nice
values (e.g., nice -10) make it more important.
3. Scheduling Policy: Linux supports different scheduling policies, with the most common one being the
Completely Fair Scheduler (CFS). CFS aims to allocate CPU time fairly among processes. Other policies
include Real-Time (RT) scheduling for time-critical tasks and Round Robin for equal time-sharing.
4. Load Balancing: Linux uses load balancing mechanisms to ensure that CPU usage is distributed evenly
across all available CPU cores. This helps prevent situations where some cores are heavily loaded while
others remain idle.
5. Time Quantum: Each process is allocated a time quantum, which is the maximum amount of time it can
execute on the CPU before being preempted. In the CFS, this quantum is relatively long, allowing for
efficient execution of interactive tasks.
6. Preemption: Preemption means that a higher-priority process can interrupt the execution of a
lower-priority process to ensure fairness and responsiveness. Linux is preemptive in nature, and
processes can be preempted based on priority and time quantum.
7. Process State Transitions: Processes in Linux can be in various states, including Running, Ready, Blocked,
and Terminated. The scheduler transitions processes between these states as they are created, wait for
I/O, become ready to run, or finish execution.
8. Dynamic Priority Adjustment: The Linux scheduler continuously monitors the state of processes and
adjusts priorities dynamically to ensure fair distribution of CPU time. Processes that are waiting for I/O or
have lower nice values tend to get higher priority.
9. Scheduling Classes: Linux supports different scheduling classes, including the Completely Fair Scheduler
(CFS), the Real-Time (RT) scheduler for time-critical tasks, and the Round Robin scheduler for equal
time-sharing.
10.Affinity: CPU affinity allows processes to be bound to specific CPU cores. This can improve performance
for certain workloads and reduce cache contention.
11.Control Groups (cgroups): Control groups allow administrators to group processes together and set
resource limits, including CPU limits, for those groups. This helps in resource management and isolation.
12.Priority Inversion: The Linux scheduler handles priority inversion, a situation where a higher-priority
process is waiting for a resource held by a lower-priority process. Priority inheritance and priority ceiling
protocols are used to resolve such situations.
What Linux’s process scheduling offers?
Linux's process scheduling mechanism is designed to provide fairness, responsiveness, and efficient
resource utilization in multi-user and multi-tasking environments. It continuously adapts to changing
workloads and system conditions to ensure that critical tasks are executed promptly while maintaining
overall system stability.
Using a Shell Command: To start a new process from the command line, you can simply type the name
of the program you want to run, followed by any necessary arguments. For example:
ls –l
Create a shell script (e.g., a file with a .sh extension) and add the commands you want to run as a new
process. Make sure the script has executable permissions. Then, run the script using ./[Link]. For
example:
#!/bin/bash
The wait command is used to wait for the completion of a specific process or all child processes. You
typically provide the process ID (PID) as an argument to the wait command.
To wait for a specific process (replace PID with the actual process ID):
wait PID
To wait for all child processes to complete:
wait
Using Shell Scripting Constructs:
In shell scripts, you can use constructs like wait and & to run multiple processes in parallel and wait for
them to finish. For example:
long_running_command1 &
long_running_command2 &
Wait
Zombie Process
In Linux, a zombie process (also known as a "zombie") is a type of process that has completed execution
but still has an entry in the process table. It's in a "zombie" state because it's not running, but its exit
status has not yet been collected by its parent process. Zombie processes are a natural part of the
process lifecycle and are typically harmless, but they can accumulate if not properly handled.
1. Process Termination: When a process finishes its execution, it sends an exit status to its parent process.
2. Parent's Responsibility: It's the responsibility of the parent process to collect the exit status of its child
processes using the wait() system call or similar functions.
3. Zombie State: If the parent process does not collect the exit status of its child process, the child process
enters a zombie state. In this state, the process entry still exists in the process table, but it has no other
resources allocated to it, and it cannot be scheduled to run.
4. Kernel Cleanup: The kernel retains the process entry in this state until the parent collects the exit status,
at which point the process entry is removed, and the resources associated with the process are freed.
Zombie processes are usually temporary and pose no direct harm to the system. However, if too many
zombie processes accumulate, they can fill up the process table, potentially preventing the system from
creating new processes. Therefore, it's essential for parent processes to properly collect the exit status of
their child processes.
2. Write Properly Structured Code: Ensure that your programs properly handle child processes by using the
wait() or waitpid() system calls to collect exit statuses.
3. Implement Signal Handlers: You can use signal handlers (e.g., SIGCHLD) to automatically reap zombie
processes in your programs.
4. Configure Process Limits: You can configure system-wide or per-user limits on the number of processes
or child processes to prevent excessive zombie process accumulation.
Remember that while zombie processes are usually harmless, they can be a sign of a bug or improper
handling in your programs. It's good practice to clean up zombie processes promptly to keep your system
running efficiently.
Orphan Processes
An orphan process in Linux is a process that has been created by another process (its parent) but is then
left without an active parent process. Orphan processes are not zombies; they are still running and
consuming system resources. Unlike zombie processes, which have finished their execution but haven't
been reaped by the parent, orphan processes are actively running.
Here's how orphan processes come into existence and how they are handled in Linux:
1. Process Hierarchy: In a Unix-like operating system, processes are organized into a hierarchical structure.
Each process (except for the initial process, usually PID 1, such as "init" or "systemd") has a parent
process.
2. Parent Termination: When a parent process terminates, the kernel assigns its child processes to a new
parent, which is usually the initial process (PID 1). This ensures that every process in the system has a
parent.
3. Orphan Process: If a child process outlives its parent and the parent terminates before the child, the
child becomes an orphan. It is then reassigned to the initial process as its new parent.
Orphan processes can be created in various ways, such as when a parent process exits unexpectedly or
when a daemon process forks and detaches from its parent terminal.
1. Adoption by the Init Process: When a process becomes an orphan, the kernel reassigns it to the init
process (or its modern equivalent, such as systemd). The init process becomes the new parent of the
orphaned process.
2. Exit Status Collection: The init process, as the new parent, is responsible for collecting the exit status of
orphaned child processes. When an orphaned process terminates, its exit status is collected by init.
3. Cleanup: Once the exit status is collected, the process entry for the orphaned process is removed from
the process table, and any system resources associated with it are freed.
Orphan processes are essential for maintaining the stability and integrity of the system. Without this
mechanism, processes could potentially become "zombies" or leave system resources in an undefined
state. Therefore, the adoption of orphan processes by the init process ensures proper cleanup and
resource management, allowing the system to continue running smoothly.
FORK
In Linux, the fork() system call is used to create a new process. The new process is known as the child
process, and the process that called fork() is the parent process. The child process is a copy of the parent
process, with its own address space and resources, but it starts executing from the same point as the
parent process.
Here's a basic overview of how the fork() system call works and its purpose:
1. Parent Process Calls fork(): When a program calls the fork() system call, the operating system
creates a new process that is an exact copy of the calling process, including the program's code,
data, and execution context.
2. Child Process Creation: After the fork() call, two processes exist: the parent and the child. These
processes are identical in every way except for their process IDs (PIDs). The child process
receives a new PID, while the parent's PID remains unchanged.
3. Return Value: In the parent process, the fork() system call returns the child's PID as the return
value, allowing the parent to identify the newly created child. In the child process, fork() returns
0.
4. Parallel Execution: After the fork() call, both the parent and child processes continue to execute
independently from that point onward. They do not share memory or variables, and changes
made by one process do not affect the other.
VFORK
In Linux and Unix-like operating systems, vfork() is a system call that is similar to fork(), but with some
key differences. Both fork() and vfork() are used for creating new processes, but they serve different
purposes and have distinct characteristics.
[Link]:
fork(): The primary purpose of fork() is to create a new process that is a copy of the parent process. The
child process created by fork() gets its own copy of the parent's address space, including data, heap, and
stack segments. Both the parent and child processes continue execution independently.
vfork(): The primary purpose of vfork() is to create a new process that shares the parent's address space
without copying it. In other words, the child process created by vfork() shares the same memory space
as the parent process. This can be used for efficiency when the child process intends to execute a new
program immediately (typically via exec) and doesn't need its own copy of the parent's memory.
[Link] and Child Relationship:
fork(): The parent and child processes created by fork() are independent of each other and run
concurrently. They have separate copies of memory and resources.
vfork(): The child process created by vfork() shares the parent's memory and resources until it calls
exec*() or _exit(). This means that any changes made by the child to the memory will affect the parent as
well until the child calls exec*() or _exit(). The parent is typically suspended while the child is running to
prevent data corruption.
[Link] Values:
fork(): In the parent process, fork() returns the child's process ID (PID), and in the child process, it returns
0 to indicate that it is the child.
vfork(): The return values of vfork() are the same as fork(). In the parent process, it returns the child's
PID, and in the child process, it returns 0.
In this example, the vfork() system call is used to create a child process. The child process then uses
execl() to execute the "ls" command, listing the directory contents. The key difference is that the child
process shares the same address space as the parent until it calls _exit() after exec.
EXIT
In Linux and Unix-like operating systems, the exit() system call is used by a process to terminate its own
execution and return an exit status to its parent process. The exit() system call is typically invoked when a
process has completed its intended task or encounters an error that requires termination. Here's how it
works:
c
#include <stdlib.h>
The process is terminated immediately, and its resources, including memory, file descriptors, and other
system resources, are released.
The exit status specified in the exit() call is returned to the parent process. The parent can retrieve this
exit status using the wait() system call or similar functions.
WAIT
In Linux and Unix-like operating systems, the wait() system call is used by a parent process to suspend its
execution and wait for the termination of one of its child processes. It is a way for the parent process to
collect information about the status of its child processes, including their exit status. The wait() system
call is often used in conjunction with the fork() system call to manage and synchronize child processes.
If you want the parent process to wait for any of its child processes to terminate, you can call wait()
without specifying a particular child process. The parent will be suspended until any child process exits,
and the exit status of that child process will be stored in the status variable.
If you have multiple child processes, each with a unique PID, and you want to wait for a specific child,
you can pass the PID of that child process to wait(). The parent will wait for the specified child to
terminate.
If you have multiple child processes and want to wait for all of them to terminate, you can use wait() or
waitpid() in a loop until there are no more child processes left to wait for.
Top of Form
EXEC
In Linux and Unix-like operating systems, the exec command is used to replace the current process image
with a new process image. It is a fundamental system call and command that allows a process to start
running a new program while discarding the current program and its data. This is commonly used when
one program wants to launch another program.
In C and other programming languages, you can use one of the exec*() functions, such as execvp(),
execv(), execle(), etc., to execute a new program. These functions are part of the C standard library and
are typically used in programs to replace the current process image with a new one.
The exec*() functions accept the path to the new program to be executed and, optionally, an array of
command-line arguments and environment variables.
After a successful exec*() call, the current process is replaced by the new program, and the new program
starts running from its main() function.
Shell Command:
From the Linux command line, you can use the exec command to replace the current shell process with
another program. This is typically used to run a program directly from the command line without
creating a new shell process.
exec ls –l
In both cases, the current process is completely replaced, and the new program becomes the new
process, inheriting the PID (Process ID) of the original process. Any code or data in the original program
is lost, and the new program starts executing from its entry point.
The exec command is a powerful way to launch new programs from within an existing process, and it's
commonly used in scripting, program execution, and process management in Linux.
Signals
Signals are a form of inter process communication (IPC) used to notify processes and threads about
various events and conditions. Signals are asynchronous notifications sent to a process or thread to
notify it that a specific event or condition has occurred. Processes and threads can handle or ignore
signals, and they can take specific actions in response to signals.
Signal Types:
Signals are represented by integers, where each integer corresponds to a specific signal type. For
example, SIGTERM is signal number 15, and SIGINT is signal number 2.
Sending Signals:
Linux Signals
Signals are the interrupts that are sent to the program to specify that an important event has occurred.
Events can vary from user requests to invalid memory access errors. Various signals, like the interrupt
signal, means that the user has asked the program to perform something which is not present in the user
flow of control.
1. Maskable
2. Non-Maskable
Maskable: - Maskable Signals are the signals that the user can change or ignore, for
example, Ctrl+C.
Non-Maskable: - Non-Maskable Signals are the signals that the users cannot change or ignore.
Non-Maskable signals mainly occur if a user is signaled for non-recoverable hardware errors.
Unreliable Signals
Unreliable signals is when there is interruption by signal, system calls could not restart automatically and
signal handler does not remain installed, while reliable signal, signal handler remains installed upon
calling and condition for re-installation does not occur.
What is an interrupt?
An interrupt is an event that alters the normal execution flow of a program and can be generated by
hardware devices or even by the CPU itself. When an interrupt occurs the current flow of execution is
suspended and interrupt handler runs. After the interrupt handler runs the previous execution flow is
resumed.
Interrupts can be grouped into two categories based on the source of the interrupt. They can also be
grouped into two other categories based on the ability to postpone or temporarily disable the interrupt:
Synchronous interrupts, usually named exceptions, handle conditions detected by the processor itself in
the course of executing an instruction. Divide by zero or a system call are examples of exceptions.
Asynchronous interrupts, usually named interrupts, are external events generated by I/O devices. For
example a network card generates an interrupts to signal that a packet has arrived.
Here are some key points to understand about interrupted system calls in Linux:
1. EINTR Error Code: When a system call is interrupted by a signal, it returns an error code of
EINTR, which stands for "Interrupted system call." This error code indicates that the system call
did not complete its intended operation due to a signal interrupting its execution.
2. Handling EINTR: When a system call returns EINTR, it's up to the application to handle it
appropriately. The application can choose to:
● Retry the interrupted system call, often in a loop until it succeeds or another error occurs.
● Abort the operation and return an error to the user.
● Ignore the signal and continue with the operation.
● The appropriate action depends on the specific requirements and design of the application.
Common Causes: System calls that involve blocking or waiting for events, such as read(), write(), select(),
poll(), and accept(), are more likely to be interrupted by signals. For example, if a process is blocked in a
read() system call waiting for input, pressing Ctrl+C may interrupt the read() operation with EINTR.
Signal Blocking: Applications can use the sigaction() system call with the SA_RESTART flag to specify that
certain system calls should be automatically restarted after being interrupted by a signal. When
SA_RESTART is set, the system call is retried instead of returning EINTR. However, not all system calls
support automatic restart, and some may still return EINTR.
Non-Blocking Operations: For some applications, it may be beneficial to use non-blocking I/O or
asynchronous I/O operations to avoid being blocked in system calls and to handle signals more
predictably.
Signal Handlers: When a signal is delivered to a process, it can be caught by a signal handler. In the signal
handler function, the application can choose to handle the signal gracefully, possibly cleaning up
resources or setting flags to inform the main program loop to exit.
Interrupting Long-Running Operations: Signals can be used to interrupt long-running or potentially stuck
operations. For example, a timer signal (e.g., SIGALRM) can be set to interrupt a system call that takes
longer than a specified time to complete.
kill in linux
In Linux and Unix-like operating systems, the kill command is used to send signals to processes. It is a
versatile command that allows you to interact with running processes in various ways. The primary
purpose of the kill command is to send signals to processes to request specific actions or notify them of
certain events.
● options: Optional flags that modify the behavior of the kill command.
● <pid>: The Process ID (PID) of the target process to which you want to send a signal.
● SIGTERM (15): This is the default signal sent by kill if no signal is specified. It requests a process
to terminate gracefully. The process can catch this signal and perform cleanup operations before
exiting.
● SIGKILL (9): This signal forcefully terminates a process. It does not allow the process to perform
any cleanup operations or save state. It is often used as a last resort to terminate a process that
is not responding to other termination signals.
● SIGHUP (1): This signal is typically used to instruct a process to reload its configuration or restart.
It is often used with daemons and services.
● SIGINT (2): This signal is typically generated by pressing Ctrl+C in the terminal. It interrupts the
process, allowing it to clean up and terminate gracefully.
● SIGUSR1 (10) and SIGUSR2 (12): These are user-defined signals that can be used by applications
for custom actions.
● -l or --list: Lists the available signal names and their corresponding numbers.
● -s or --signal: Specifies the signal to send.
● -a or --all: Sends the specified signal to all processes except for the kill process itself.
● -q or --queue: Queues the signal instead of sending it immediately.
● -p or --parent: Sends the signal to the parent process of the specified PID.
kill -9 54321
kill –l
Send a SIGHUP signal to a process group (e.g., all processes in the group):
kill -s SIGHUP -12345
Raise in linux
In Linux and Unix-like operating systems, the raise function is a C library function that is used to send a
signal to the calling process or thread. It is a way for a program to generate a signal intentionally within
its own context. The raise function allows a program to trigger a specific signal handler function
registered for that signal or to perform some action in response to the signal.
#include <signal.h>
sig: The signal number to be raised. It should be one of the valid signal numbers, such as SIGTERM,
SIGINT, SIGHUP, SIGUSR1, etc.
The raise function returns zero if it successfully raises the signal and -1 if it encounters an error. Common
errors may occur if an invalid signal number is specified
Alarm in linux
In Linux and Unix-like operating systems, the alarm function is a system call that is used to set an alarm
or timer that will send a SIGALRM signal to the calling process after a specified number of seconds have
elapsed. The alarm function is often used for implementing timeouts or scheduling periodic tasks within
a program.
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
seconds: The number of seconds after which the SIGALRM signal will be sent. If seconds is zero, any
previously scheduled alarm is canceled.
The alarm function returns the number of seconds remaining until any previously scheduled alarm was
set to expire. If no previous alarm was set, it returns zero.
pause in linux
In Linux and Unix-like operating systems, the pause function is a system call that suspends the execution
of the calling process until a signal is received. When a signal is delivered to the process, the signal
handler for that signal is executed, and then the process resumes execution from where it was paused.
#include <unistd.h>
int pause(void);
The pause function has no arguments and returns -1 with errno set to EINTR (Interrupted system call)
when it is interrupted by a signal. It does not return normally until a signal is received and processed.
The primary purpose of pause is to allow a process to wait for a signal without actively consuming CPU
time. It is often used in conjunction with signal handlers to implement synchronization and
communication between processes or between different parts of a single program.
abort in linux
In Linux and Unix-like operating systems, the abort function is a standard C library function that is used
to generate a SIGABRT signal, which is a signal indicating that the calling process should terminate
abnormally. The abort function is typically used when an unrecoverable error is encountered within a
program, and the program needs to terminate immediately, optionally generating a core dump for
debugging purposes.
#include <stdlib.h>
void abort(void);
The abort function does not take any arguments. When it is called, it triggers the SIGABRT signal, and the
default action for this signal is to terminate the calling process. Additionally, it can produce a core dump
if core dumps are enabled on the system.
system in linux
In Linux and Unix-like operating systems, the system function is a C library function that allows you to run
shell commands or shell scripts from within a C or C++ program. It provides a way to execute external
commands as if they were entered directly in the system's shell (usually /bin/sh or another configured
shell).
#include <stdlib.h>
int system(const char *command);
command: A pointer to a null-terminated string containing the shell command you want to execute
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
seconds: The number of seconds for which the process or thread should be paused.
The sleep function returns the number of seconds remaining for the sleep period if the sleep is
interrupted by a signal, or it returns zero if the full sleep period elapses without interruption.
File locking is a mechanism used to restrict access to a file to only one process or user at a time. It is
essential in multi-user systems to avoid conflicts when multiple processes try to access the same file
simultaneously. In Linux, file locking is implemented through the use of locks. Lock prevents other
processes from accessing a file until the lock is released.
● Advisory locks
● Mandatory locks.
Advisory locks are file locks that allow processes to request a lock on a file, but they do not prevent other
processes from accessing or modifying the file. Advisory locks are helpful when multiple processes need
to access a file, but each process needs to ensure that it has exclusive access to a particular section of
the file.
Mandatory locks are file locks that prevent other processes from accessing or modifying a file until the
lock is released. Mandatory locks are set by the kernel and cannot be overridden by processes.
Mandatory locks are helpful when a file is critical and must not be modified by any other process.
DEADLOCKS
Deadlocks can occur in Linux, as in any multi-threaded or multi-process operating system, when two or
more processes or threads are unable to proceed because each is waiting for a resource held by the
other(s). In a deadlock situation, the involved processes or threads become effectively stuck, and no
progress can be made. Deadlocks are a common concern in concurrent programming and can impact
system stability and performance.
Here are some common scenarios that can lead to deadlocks in Linux:
1. Resource Contention: Deadlocks often occur when multiple processes or threads contend for
limited resources such as shared memory, file locks, or hardware devices. If processes hold a
resource and request another that is held by a different process, and these processes are stuck
in a circular wait, a deadlock can result.
2. Locking Mechanisms: Deadlocks can occur when locking mechanisms are not properly
implemented or coordinated. For example, if multiple threads attempt to acquire locks on
different resources in different orders, a deadlock may arise if they encounter different locking
orders.
To prevent and manage deadlocks in Linux, various strategies and techniques can be employed:
1. Resource Allocation: Ensure that resources are allocated and released in a consistent and
well-defined order to reduce the likelihood of circular waits.
2. Timeouts: Implement timeouts for resource requests. If a process or thread cannot acquire a
resource within a specified time, it can take action to break the deadlock, such as releasing
resources it holds.
3. Deadlock Detection: Implement deadlock detection mechanisms that periodically check for
deadlocks and take corrective action. The Banker's algorithm is an example of a deadlock
detection algorithm.
4. Resource Preemption: Allow resources to be preempted from one process and allocated to
another to break deadlocks. This approach is not always feasible and should be used with
caution.
5. Resource Hierarchies: Organize resources into a hierarchy and require processes to request
resources in a hierarchical order. This reduces the chances of circular waits.
6. Avoidance Algorithms: Implement deadlock avoidance algorithms that use a mathematical
model to predict whether resource allocation requests will lead to a deadlock. These algorithms
make resource allocation decisions to avoid potential deadlocks.
7. Proper Synchronization: Use synchronization primitives correctly and consistently in
multi-threaded and multi-process applications. Avoid common pitfalls such as holding locks
while making I/O requests.
8. Debugging Tools: Utilize debugging tools and profilers to identify and analyze deadlock scenarios
in your applications.