You are on page 1of 49

Linux Programming

Unit-IV

COURSE OUTCOMES
1. Understand various Linux System utilities.
2. Demonstrate the shell scripts in shell Environment.
3. Understand the basic principles of file system architecture.
4. Apply the core concept of Process management and implement in the Linux Environment.
5. Compare various Inter-Process communication and Client-Server Application techniques In Linux environment
Process: Introduction to Process
• A Process is an instance of a running program.
• A process uses various system resources like memory
devices etc.
• Each Linux process has a parent and one or more
children.
• The init process is the first user process which is
created when the system boots for the first time
• It is the parent process for all the user processes.
• Each and every process has unique non-negative
identifier called the process ID.
Process: Introduction to Process
• A process is created by calling the fork() or vfork()
system call.
• A process may execute one or more programs.
• Life time of a process begins when it is created and ends
when the process terminates by calling exit system call
Process: Introduction to Process
A process is a program in execution.
Processes exist in main memory.
Process: Introduction to Process
Code Section: Sequence of Instructions

Static Data Section: e.g., global variables and arrays

Dynamic Data Section: e.g., malloc() or calloc() calls

Stack Section: e.g., local variables, function/procedure


parameters, and return address PC (Program Counter):
next instruction
Process : Process Identifiers
• Every process has a unique process ID, a non-negative integer.
Because the process ID is the only well-known identifier of a
process that is always unique.

• For example, applications sometimes include the process ID as


part of a filename in an attempt to generate unique filenames.

 Every process apart from the PID also has a PUID and a PGID.
 There are two types of PUID and PGID:

real and effective.


Process : Process Identifiers
The process is assigned the following attributes which are either inherited from its parent
or set by the kernel,

1) A Real User Identification Number (rUID)


The user ID of a user who created the parent process. This is used by the kernel to keep
track of who creates which processes on a system.

2) A Real Group Identification Number (rGID)


◦ The group ID of a user who created the parent process.
◦ This is used by the kernel to keep track of which group creates which processes on a
system.
Process : Process Identifiers
3) An effective User Identification Number (eUID)

◦ This is normally the same as the real UID, except when the file that was executed to
create the process has its set-UID flag turned on ( via the chmod command or API).
◦ In the case, the process eUID will take on the UID of the file.
◦ This allows the process to access and create files with the same privileges as the
program file owner.

4) An effective Group Identification Number (eGID)


◦ Same as the real GID except when the file that was executed to create the process has
its set-GID flag turned on ( via the chmod command or API).
◦ In that case, the process eGID will take on the GID of the file. This allows the
process to access and create files with same privileges as the group to which the
program file belongs.
Process : Process Identifiers
 The following functions return these identifiers. These functions does
not have error return values like other functions.
 #include<sys/types.h>

#include<unistd.h>
pid_t getpid (void) :- returns process ID of calling process
pid_t getppid (void) :- returns parent process ID of calling process
uid_t getuid (void) :-returns real user ID of calling process
uid_t geteuid (void) :- returns effective user ID of calling process
gid_t getgid (void) :- returns reak griyo ID if calling process
gid_t getegid (void) :- returns effective group ID of calling process
Process : Process Structure
• The kernel stores the list of processes in a circular doubly linked
list called the task list.
• Each element in the task list is a process descriptor of the
type struct task_struct, which is defined in <linux/sched.h>.
• The task structure contains all the information about a specific
process.
Process : Process Table
Process : Process Table
• The process control block (PCB) maintains information that the
operating system needs in order to manage a process.
• PCBs typically include information such as the process ID, the
current state of the process (e.g. running, ready, blocked, etc.),
• the number of the next program instruction to be executed, and the
starting address of the process in memory.
• The PCB also stores the contents of various processor registers
(the execution context),
• which are saved when a process leaves the running state and
which are restored to the processor when the process returns to
the running state.
Process :Viewing Processes
• The most common way to list processes currently running on your
system is to use the command ps

• To get information about all processes running on the system,


use ps -A:
Process :Process Scheduling
• In Linux Processes are scheduled using time slice based on
the process priority.
Process : Process Scheduling
1. Running
Which means that the process is currently using the CPU.
2. Runnable
Process can make use of the CPU as soon as it becomes available.
3. Sleeping
Process is waiting for an event to occur. For example, if a process executes a sleep() system call,
it sleeps until the I/O request is completed
4. Suspended
The process has been “frozen” by a signal such as SIGSTOP. It will resume only when sent a
SIGNONT signal. For e.g. a Ctrl+Z from keyboard suspends all of the processes in the
foreground
5. Idle
process is being created by a fork() system call and is not yet runnable.
6. Terminated
process has terminated, but has not yet returned its exit code to its parent. A process remains a
zombie until its parent accepts its return code via the wait() system call
Process : Starting new Process
A new process is created because an existing process makes an exact
copy of itself. 

This child process has the same environment as its parent, only the


Process ID number is different.
This procedure is called forking.

After the forking process, the address space of the child process is


overwritten with the new process data.

This is done through an exec call to the system.


Process : Starting new Process
Fork-and-exec mechanism
Process : Starting new Process
In this case, the shell is a parent process, and at the ls, the shell does fork() to
create a child process. The newly created child process does exec() to run ls by
replacing itself with the ls.
Process : Waiting for a Process
The system call wait() , This function blocks the calling process until
one of its child processes exits or a signal is received.

The execution of wait() could have two possible situations.


 If there are at least one child process running when the call
to wait() is made, the caller will be blocked until one of its child
processes exits. At that moment, the caller resumes its execution.

 Ifthere is no child process running when the call to wait() is made,


then this wait() has no effect at all.
That is, it is as if no wait() is there.
Process : Zombie Process
Process : Zombie Process
When a process finishes execution, it will have an exit status to report to its parent
process.
Because of this last little bit of information, the process will remain in the operating
system’s process table as a zombie process,
indicating that it is not to be scheduled for further execution,
but that it cannot be completely removed (and its process ID cannot be reused)
until it has been determined that the exit status is no longer needed.
When a child exits, the parent process will receive a SIGCHLD signal to indicate that
one of its children has finished executing;
To see Zombie Process
Run “ps aux” and look for a Z in the STAT column.
Removing zombie processes from a system
*Wait until parent process exit
* Send SIGCHLD signal to Parent Process using KILL command
* kill(Terminate) the parent process of the zombie
Process : Orphan Process
An Orphan Process is a process whose parent is dead (terminated). A process with
dead parents is adopted by the init process.
Process : Process System calls
Fork: create a child process
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);

System call fork() is used to create processes.


It takes no arguments and returns a process ID.
The purpose of fork() is to create a new process, which becomes
the child process of the caller.
After a new child process is created, both processes will execute the
next instruction following the fork() system call.
Process : Process System calls
 RETURN VALUE    
 On success, the PID of the child process is returned in the parent, and
0 is returned in the child.
 On failure, -1 is returned in the parent, no child process is created,
and errno is set appropriately.
ERRORS 
ENOMEM : fork() failed to allocate the necessary kernel structures
because memory is tight.
ENOSYS :fork() is not supported on this platform (for example,
hardware without a Memory-Management Unit).
Process : Process System calls
Vfork : create a child process and block parent
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);

The vfork() function creates a new process as does fork(),


except that the child process shares the same address space as the
calling process.
RETURN VALUES
 If successful, vfork() returns 0 in the child process, and returns the
process ID of the child process to the parent process. On failure, it
returns -1.
Process : Process System calls
 Exit( ) :- Cause normal process termination
#include <stdlib.h>
void exit(int status );
 System call exit() terminates the calling process "immediately".
 Any open file descriptors belonging to the process are closed;
 Any children of the process are inherited by process 1, init, and the
process's parent is sent a SIGCHLD signal.
 The value status is returned to the parent process as the process's exit
status, and can be collected using one of the wait() family of calls.
Return Value
 These functions do not return.
Process : Process System calls
Wait() :-wait for process to change state
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
One of the main purposes of wait() is to wait for completion of child
processes.
 This function blocks the calling process until one of its child
processes exits or a signal is received.
 wait() takes the address of an integer variable and returns the process
ID of the completed process.
 Some flags that indicate the completion status of the child process
are passed back with the integer pointer.
Process : Process System calls
Wait() :-wait for process to change state
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, intoptions);

One of the main purposes of wait() is to wait for completion of child


processes.
 This function blocks the calling process until one of its child
processes exits or a signal is received.
 wait() takes the address of an integer variable and returns the process
ID of the completed process.
 Some flags that indicate the completion status of the child process
are passed back with the integer pointer.
Process : Process System calls
Wait() :-wait for process to change state
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, intoptions);
 The waitpid() system call suspends execution of the calling process until a child
specified by pid argument has changed state.
 By default, waitpid() waits only for terminated children, but this behavior is
modifiable via the options argument, as described below.
 The value of pid can be:
 < -1 meaning wait for any child process whose process group ID is equal to the
absolute value of pid.
 -1 meaning wait for any child process.
 0 meaning wait for any child process whose process group ID is equal to that of
the calling process.
 > 0 meaning wait for the child whose process ID is equal to the value of pid.
Process : Process System calls
 Exec family
 execl(), execle(), execlp(), execlpe(), execv(), execve(), execvp(), execvpe()
 SYNOPSIS
 #include <unistd.h>
 extern char **environ;
 int execl(const char *path, const char *arg0, ..., const char *argn, (char *)0);
 int execle(const char *path, const char *arg0, ..., const char *argn, (char *)0, char
*const envp[]);
 int execlp(const char *file, const char *arg0, ..., const char *argn, (char *)0);
 int execlpe(const char *file, const char *arg0, ..., const char *argn, (char *)0,char
*const envp[]);
 int execv(const char *path, char *const argv[]);
 int execve(const char *path, char *const argv[], char *const envp[]);
 int execvp(const char *file, char *const argv[]);
 int execvpe(const char *file, char *const argv[], char *const envp[]);
Process : Process System calls
 Exec family
The exec() family of functions replaces the current process image with
a new process image.
The initial argument for these functions is the name of a file that is to
be executed.
 RETURN VALUE      
 The exec() functions return only if an error has occurred. The return
value is -1, and errno is set to indicate the error.
Signals : signals
 What is a signal?
 Signals are software interrupts.

A robust program needs to handle signals. This is because signals are


a way to deliver asynchronous events to the application.
 Every signal has a name that begins with "SIG".
 For example, SIGABRT is the abort signal that is generated where a
process calls abort.
 The signal names are representative of positive constants as defined
in <signal.h>.
Signals : signals
 Conditions that generate signals:
 1. The terminal generated signals occur when users press certain
keys.
 2. Hardware exceptions generate signals: (i.e. divide by 0, invalid
memory references, etc).

 3.The kill(2) function allows a process to send any signal to any


process or process group.

 4. The kill(1) command allows us to send signals to other processes

 5.
Software conditions can generate signals (i.e. SIGURG, SIGPIPE,
SIGALRM, etc).
Signals : signals
 Signal dispositions (i.e. Actions associated with signals):

 1. Ignore the signal. Some signals can be ignored while others


cannot.
 SIGKILL and SIGSTOP are examples of signals that cannot be
ignored.
 2. Catch the signal. We can register a function to be called when a
signal occurs, and perform whatever actions we like.
 3. Allow the default action to apply. Every signal has a default
action
 Note the default action in most cases is to terminate the process.
Signals : signal function
 #include <signal.h>
 typedef void (*sighandler_t)(int);
 sighandler_t signal(int signum, sighandler_t handler);    
 signal() sets the disposition of the signal signum to handler, which is
either SIG_IGN, SIG_DFL, or the address of a programmer-defined
function (a "signal handler").
 If the signal signum is delivered to the process, then one of the
following happens:
* If the disposition is set to SIG_IGN, then the signal is ignored.
* If the disposition is set to SIG_DFL, then the default Action
associated with the signal occurs.
* If the disposition is set to a function, then first either The disposition
is reset to SIG_DFL, or the signal is blocked, and then handler is
called with argument signum.
Signals : signal function
 RETURN VALUE
      
signal() returns the previous value of the signal handler, or SIG_ERR
on error. In the event of an error, errno is set to indicate the cause.
 ERRORS         

EINVAL signum is invalid.


Signals : Unreliable Signals
 In older versions of UNIX, signals could be lost and the process
would never know
 Processes had little control over signals. It could catch or ignore the
signal.
 Sometimes we want to temporarily block signals and process them
later to protect a critical section of code
 One problem with these early functions is that the action for the
signal was reset to default each time the signal occurred.

 This means there is a window of time in which a second signal could


get executed with the default action (i.e. terminate the process).
 This is another form of a race condition.
Signals : Interrupted System Calls
 If a process received a signal while the process was blocked in a
"slow" system call, the system call was interrupted (i.e. returned with
an error, and errno set to EINTR).
 "Slow" system calls are defined as those that can block forever:
 1. reads calls for files that can block the caller forever if data is not
present (i.e. pipes, terminals, network devices, etc).
 2. write call to those same files if data cannot be accepted
immediately
 3. open calls for files that block until some condition occurs (i.e.
open of a terminal device that depends on modem answering).
 4. certain ioctl operations
 5. some of the inter-process communications functions

To save from the mess of handling this situation, some versions of


UNIX automatically restarted some system calls
Signals : Kill System Calls
 kill - send a signal to a process or a group of processes
 #include <signal.h>
int kill(pid_t pid, int sig);
 The kill() function shall send a signal to a process or a group of
processes specified by pid.
 The signal to be sent is specified by sig and is either one from the list
given in <signal.h> or 0.
 If sig is 0 (the null signal), error checking is performed but no signal
is actually sent. The null signal can be used to check the validity
of pid.
 kill sends a signal to a process or group of processes
◦ pid > 0 – send to process with PID = pid
◦ pid = 0 – send to all processes with PGID = PGID of caller
◦ pid < 0 – send to all processes with PGID = |pid|
◦ To send signals to other processes, EUID must match
Signals : Raise System Calls
 Raise - send a signal to the caller
 #include <signal.h>
int raise(int sig);
The raise() function sends a signal to the calling process or thread.
In a single-threaded program it is equivalent to kill(getpid(), sig);

If the signal causes a handler to be called, raise() will return only after
the signal handler has returned.
raise() returns 0 on success, and nonzero for failure.
Signals : alarm System Calls
 alarm - set an alarm clock for delivery of a signal
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
 The alarm function sets an alarm clock to send a SIGALRM signal
after "seconds".
 It is possible due to system overhead for the signal to be delivered
slightly off time.
 There is only one of these alarm clocks per process. If we call alarm
more than once, we are resetting the alarm clock to a new value.
 alarm(0) cancels any pending alarms
 Returns: 0 or number of seconds since previously set alarm
Signals : Pause System Calls
 pause - wait for signal
 #include <unistd.h>
 int pause(void);
pause() causes the calling process (or thread) to sleep until a signal is
delivered that either terminates the process or causes the invocation
of a signal-catching function.

pause() returns only when a signal was caught and the signal-catching
function returned.
In this case, pause() returns -1, and errno is set to EINTR.

ERRORS
EINTR a signal was caught and the signal-catching function returned.
Signals : abort System Calls
 abort - cause abnormal process termination
 #include <stdlib.h>
 void abort(void);
 The abort() first unblocks the SIGABRT signal, and then raises that
signal for the calling process.

 This results in the abnormal termination of the process unless the


SIGABRT signal is caught and the signal handler does not return.

 If the SIGABRT signal is ignored, or caught by a handler that


returns, the abort() function will still terminate the process.

 It does this by restoring the default disposition for SIGABRT and


then raising the signal for a second time.
Signals : System function
 system - execute a shell command
 #include <stdlib.h>
 int system(const char *command);
The system() library function uses fork( ) to create a child process that
executes the shell command specified in command using execl()
During execution of the command, SIGCHLD will be blocked, and
SIGINT and SIGQUIT will be ignored, in the process that calls
system().
The return value of system() is one of the following:
• If command is NULL, then a nonzero value if a shell is available, or
0 if no shell is available.
• * If a child process could not be created, or its status could not be
retrieved, the return value is -1 and errno is set to indicate the error.
• * If all system calls succeed, then the return value is the termination
status of the child shell used to execute command.
Signals : sleep function
 sleep- sleep for a specified number of seconds
 #include <unistd.h>
 unsigned int sleep(unsigned int seconds);

 sleep() causes the calling thread to sleep either until the number of
real-time seconds specified in seconds have elapsed or until a signal
arrives which is not ignored.

Zero if the requested time has elapsed, or the number of seconds left to
sleep, if the call was interrupted by a signal handler.
Signals : Signal Set
 Signal Sets
 All of the signal blocking functions use a data structure called
a signal set to specify what signals are affected.
 Thus, every activity involves two stages: creating the signal set, and
then passing it as an argument to a library function.
 These facilities are declared in the header file signal.h.
 Data Type: sigset_t

5 functions to manipulate a signal set


int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
File Locking : creating a lockfile
 lockfile - conditional semaphore-file creator

 lockfile can be used to create one or more semaphore files.


 If lockfile can’t create all the specified files (in the specified order), it
waits sleeptime  seconds and retries the last file that didn’t succeed.
 the number of retries to do until failure is returned.
 If the number of retries is -1 (default, i.e., -r-1) lockfile will retry
forever.
 If the number of retries expires before all files have been created,
lockfile returns failure and removes all the files it created up till that
point.
 All files created by lockfile will be read-only, and therefore will have
to be removed with rm -f.
File Locking : creating a lockfile
 ...
 lockfile important.lock
 ...
 access_"important”
 ...
 rm -f important.lock
 ...
File Locking : locking regions

You might also like