Module_5_Unix System Programming (BCS515C)
Module_5_Unix System Programming (BCS515C)
)
R. L. JALAPPA INSTITUTE OF TECHNOLOGY
(Approved by AICTE, New Delhi, Affiliated to VTU, Belagavi & Accredited by NAAC “A” Grade)
Kodigehalli, Doddaballapur- 561 203
Department of CS&E (Artificial Intelligence & Machine Learning)
Institute Mission
M1: To provide an outstanding Teaching, Learning and Research environment through Innovative
Practices in Quality Education.
M2: Develop Leaders with high level of Professionalism to have career in the Industry, Zeal for Higher
Education, focus on Entrepreneurial and Societal activities.
Department Vision
To empower the students with knowledge and skills to develop the competency in the field of Artificial
Intelligence and Machine Learning.
Department Mission
M1: To craft the students with Novel and Intellectual skills to capability in the field of Artificial
Intelligence and Machine Learning.
M2: To train the students to have Professional career in the field of AI and ML and zeal for Higher Studies
and Research.
Module – 5
SIGNALS AND DAEMON PROCESSES
Signals are software interrupts. Signals provide a way of handling asynchronous events: a user at a
terminal typing the interrupt key to stop a program or the next program in a pipeline terminating
prematurely.
When a signal is sent to a process, it is pending on the process to handle it. The process can react to
pending signals in one of three ways:
Accept the default action of the signal, which for most signals will terminate the process.
Ignore the signal. The signal will be discarded and it has no affect whatsoever on the recipient
process.
Invoke a user-defined function. The function is known as a signal handler routine and the
signal is said to be caught when this function is called.
SIGNAL
The function prototype of the signal API is:
#include <signal.h>
void (*signal(int sig_no, void (*handler)(int)))(int);
The formal argument of the API are: signal_num is a signal identifier like SIGINT or SIGTERM
defined in the <signal.h>.
The handler is the function pointer of a user defined signal handler function. This function should take
an integer formal argument and does not return any value.
The following example attempts to catch the SIGTERM signal, ignores the SIGINT signal, and accepts
the default action of the SIGSEGV signal. The pause API suspends the calling process until it is
interrupted by a signal and the corresponding signal handler does a return:
#include<iostream.h>
#include<signal.h>
/*signal handler function*/
void catch_sig(int sig_num)
{
signal (sig_num,catch_sig);
cout<<”catch_sig:”<<sig_num<<endl;
}
/*main function*/
int main()
{
signal(SIGTERM,catch_sig);
signal(SIGINT,SIG_IGN);
signal(SIGSEGV,SIG_DFL);
pause( ); /*wait for a signal interruption*/
}
The SIG_IGN specifies a signal is to be ignored, which means that if the signal is generated to the
process, it will be discarded without any interruption of the process.
The SIG_DFL specifies to accept the default action of a signal.
Signal Mask
Each process in UNIX or POSIX.1 system has signal mask that defines which signals are blocked when
generated to a process.
A blocked signal depends on the recipient process to unblock it and handle it accordingly.
A process initially inherits the parent’s signal mask when it is created, but any pending signals for the
parent process are not passed on.
A process may query or set its signal mask via the sigprocmask API:
#include <signal.h>
int sigprocmask(int cmd, const sigset_t *new_mask, sigset_t
*old_mask);
The return value of sigprocmask call is zero if it succeeds or -1 if it fails.
The new_mask defines a set of to be set or reset in a calling process signal mask. cmd specifies how the
new_mask value is to be used by the API. The possible values
If the actual argument to new_mask argument is a NULL pointer, the cmd argument will be ignored,
and the current process signal mask will not be altered. The old_mask argument is the address of a
sigset_t variable that will be assigned the calling process’s original signal mask prior to a sigprocmask
call. If the actual argument to old_mask is a NULL pointer, no previous signal mask will be returned.
The following example checks whether the SIGINT signal is present in a process signal mask and adds
it to the mask if it is not there.
#include<stdio.h>
#include<signal.h>
int main()
{
sigset_t sigmask;
sigemptyset(&sigmask); /*initialise set*/
Sigaction
The sigaction API is a replacement for the signal API in the latest UNIX and POSIX systems.
The sigaction API is called by a process to set up a signal handling method for each signal it wants to
deal with. .
The sigaction API prototype is:
#include<signal.h>
int sigaction(int signal_num, struct sigaction* action, struct
sigaction* old_action);
Returns: 0 if OK, 1 on error
The struct sigaction data type is defined in the <signal.h> header as:
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flag;
}
The sa_handler field can be set to SIG_IGN, SIG_DFL, or a user defined signal handler function.
The sa_mask field specifies additional signals that process wishes to block when it is handling
signal_num signal.
The signal_num argument designates which signal handling action is defined in the action argument.
The previous signal handling method for signal_num will be returned via the old_action argument if it
is not a NULL pointer. If action argument is a NULL pointer, the calling process’s existing signal
handling method for signal_num will be unchanged.
{
sigset_t sigmask;
struct sigaction action,old_action;
sigemptyset(&sigmask);
if(sigaddset(&sigmask,SIGTERM)==-1||sigprocmask(SIG_SETMASK,&sigmask,0)==-1)
perror(“set signal mask”);
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask,SIGSEGV);
action.sa_handler=callme;
action.sa_flags=0;
if(sigaction(SIGINT,&action,&old_action)==-1)
perror(“sigaction”);
pause();
cout<<argv[0]<<”exists\n”;
return 0;
}
The sigsetjmp and siglongjmp are created to support signal mask processing. Specifically, it is
implementation- dependent on whether a process signal mask is saved and restored when it
invokes the setjmp and longjmp APIs respectively.
The only difference between these functions and the setjmp and longjmp functions is that
sigsetjmp has an additional argument. If savemask is nonzero, then sigsetjmp also saves the
current signal mask of the process in env. When siglongjmp is called, if the env argument was
saved by a call to sigsetjmp with a nonzero savemask, then siglongjmp restores the saved signal
mask. The siglongjmp API is usually called from userdefined signal handling functions. This is
because a process signal mask is modified when a signal handler is called, and siglongjmp
should be called to ensure the process signal mask is restored properly when “jumping out”
from a signal handling function.
The following program illustrates the uses of sigsetjmp and siglongjmp APIs.
#include<iostream.h>
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<setjmp.h>
sigjmp_buf env;
siglongjmp(env,2);
int main()
sigset_t sigmask;
sigemptyset(&sigmask);
if(sigaddset(&sigmask,SIGTERM)==-1)||sigprocmask(SIG_SETMASK,&sigmask,0)==-
1)
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask,SIGSEGV);
action.sa_handler=(void(*)())callme;
action.sa_flags=0;
if(sigaction(SIGINT,&action,&old_action)==-1)
perror(“sigaction”);
if(sigsetjmp(env,1)!=0)
return 0;
else
pause();
Kill
A process can send a signal to a related process via the kill API. This is a simple means of inter-process
communication or control. The function prototype of the API is:
#include<signal.h>
int kill(pid_t pid, int signal_num);
Returns: 0 on success, -1 on failure.
The signal_num argument is the integer value of a signal to be sent to one or more processes
designated by pid. The possible values of pid and its use by the kill API are:
pid > 0 The signal is sent to the process whose process ID is pid.
pid == 0 The signal is sent to all processes whose process group ID equals the process group ID
of the sender and for which the sender has permission to send the signal.
pid < 0 The signal is sent to all processes whose process group ID equals the absolute
value of pid and for which the sender has permission to send the signal.
pid == 1 The signal is sent to all processes on the system for which the sender has permission to
send the signal.
The following program illustrates the implementation of the UNIX kill command using the kill API:
#include<iostream.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>
ALARM
The alarm API can be called by a process to request the kernel to send the SIGALRM signal after a
certain number of real clock seconds.
The function prototype of the API is:
#include<signal.h>
unsigned int alarm(unsigned int time_interval);
Returns: 0 or number of seconds until previously set alarm
The alarm API can be used to implement the sleep API:
#include<signal.h>
#include<stdio.h>
#include<unistd.h>
void wakeup( )
{ ; }
INTERVAL TIMERS
The interval timer can be used to schedule a process to do some tasks at a fixed time interval, to time the
execution of some operations, or to limit the time allowed for the execution of some tasks.
The following program illustrates how to set up a real-time clock interval timer using the alarm API:
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#define INTERVAL 5
void callme(int sig_no)
{
alarm(INTERVAL);
/*do scheduled tasks*/
}
int main()
{
struct sigaction action;
sigemptyset(&action.sa_mask);
action.sa_handler=(void(*)( )) callme;
action.sa_flags=SA_RESTART;
if(sigaction(SIGALARM,&action,0)==-1)
{
perror(“sigaction”);
return 1;
}
if(alarm(INTERVAL)==-1)
perror(“alarm”);
else while(1)
{
/*do normal operation*/
}
return 0;
}
In addition to alarm API, UNIX also invented the setitimer API, which can be used to define up to three
different types of timers in a process:
Real time clock timer
Example program:
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#define INTERVAL 5
void callme(int sig_no)
{
sigemptyset(&action.sa_mask);
action.sa_handler=(void(*)( )) callme;
action.sa_flags=SA_RESTART;
if(sigaction(SIGALARM,&action,0)==-1)
{
perror(“sigaction”);
return 1;
}
val.it_interval.tv_sec =INTERVAL;
val.it_interval.tv_usec =0;
val.it_value.tv_sec =INTERVAL;
val.it_value.tv_usec =0;
else while(1)
{
/*do normal operation*/
}
return 0;
}
POSIX.1b TIMERS
POSIX.1b defines a set of APIs for interval timer manipulations. The POSIX.1b timers are more
flexible and powerful than are the UNIX timers in the following ways:
Users may define multiple independent timers per system clock.
DAEMON PROCESSES
Daemons are processes that live for a long time. They are often started when the system is bootstrapped
and terminate only when the system is shut down.
DAEMON CHARACTERISTICS
The characteristics of daemons are:
Daemons run in background.
Daemons have super-user privilege.
Daemons don’t have controlling terminal.
Daemons are session and group leaders
CODING RULES
Some basic rules to coding a daemon prevent unwanted interactions from happening. We state these
rules and then show a function, daemonize, that implements them.
1. Call umask to set the file mode creation mask to 0. The file mode creation mask that's
inherited could be set to deny certain permissions. If the daemon process is going to create
files, it may want to set specific permissions.
2. Call fork and have the parent exit. This does several things. First, if the daemon was started
as a simple shell command, having the parent terminate makes the shell think that the
command is done. Second, the child inherits the process group ID of the parent but gets a new
process ID, so we're guaranteed that the child is not a process group leader.
3. Call setsid to create a new session. The process (a) becomes a session leader of a new session,
(b) becomes the process group leader of a new process group, and (c) has no controlling terminal.
4. Change the current working directory to the root directory. The current working directory
inherited from the parent could be on a mounted file system. Since daemons normally exist
until the system is rebooted, if the daemon stays on a mounted file system, that file system
cannot be unmounted.
5. Unneeded file descriptors should be closed. This prevents the daemon from holding open any
descriptors that it may have inherited from its parent.
6. Some daemons open file descriptors 0, 1, and 2 to /dev/null so that any library routines
that try to read from standard input or write to standard output or standard error will
have no effect. Since the daemon is not associated with a terminal device, there is nowhere for
output to be displayed; nor is there anywhere to receive input from an interactive user. Even if
the daemon was started from an interactive session, the daemon runs in the background, and
the login session can terminate without affecting the daemon. If other users log in on the same
terminal device, we wouldn't want output from the daemon showing up on the terminal, and
the users wouldn't expect their input to be read by the daemon.
Example Program:
#include <unistd,h>
#include <sys/types.h>
#include <fcntl.h>
int daemon_initialise( )
{
pid_t pid;
if (( pid = fork() ) < 0)
return –1;
else if ( pid != 0)
exit(0);
/* child continues
*/ setsid( );
chdir(“/”);
umask(0);
return 0;
}
Error Logging
One problem a daemon has is how to handle error messages. It can't simply write to standard error,
since it shouldn't have a controlling terminal. We don't want all the daemons writing to the console
device, since on many workstations, the console device runs a windowing system. A central daemon
error- logging facility is required.
A user process on this host, or on some other host that is connected to this host by a TCP/IP
network, can send log messages to UDP port 514. Note that the syslog function never generates
these UDP datagrams: they require explicit network programming by the process generating
the log message. Normally, the syslogd daemon reads all three forms of log messages.
On start-up, this daemon reads a configuration file, usually /etc/syslog.conf, which determines where
different classes of messages are to be sent. For example, urgent messages can be sent to the system
administrator (if logged in) and printed on the console, whereas warnings may be logged to a file. Our
interface to this facility is through the syslog function.
#include <syslog.h>
void openlog(const char *ident, int option, int facility); void
syslog(int priority, const char *format, ...);
void closelog(void);
int setlogmask(int maskpri);
Daemon Conventions
Several common conventions are followed by daemons in the UNIX System.
1. If the daemon uses a lock file, het file is usually stored in /var/run. Note, however, that the
daemon might need superuser permissions to create a file here. The name of the file is usually
name.pid, where name is the name of the daemon or the service. For example, the name of the
cron daemon's lock file is /var/run/crond.pid.
2. If the daemon supports configuration options, they are usually stored in /etc. The
configuration file is named name.conf, where name is the name of the daemon or the name of
the service. For example, the configuration for the syslogd daemon is /etc/syslog. conf.
3. Daemons can be started from het command line, but they are usually started from one of the
system initialization scripts (/etc/rc* or /etc/init.d/*). If the daemon should be restarted
automatically when it exits, we can arrange for init to restart it if we include a respawn entry
for it in /etc/inittab.
4. If a daemon has a configuration file, the daemon reads it when it starts, but usually won't look
at it again. If an administrator changes the configuration, the daemon would need to be stopped
and restarted to account for the configuration changes. To avoid this, some daemons will catch
SIGHUP and reread their configuration files when they receive the signal. Since they aren't
associated with terminals and are either session leaders without controlling terminals or
members
of orphaned process groups, daemons have no reason to expect to receive SIGHUP. Thus, they can
safely reuse it.
Assignment Questions
1. Explain the signal concepts
2. Explain sigaction API with demonstrating program.
3. What is signal mask of a process? WAP to check whether the SIGINT signal present in signal
mask.
4. Write a short note on the SIGCHLD signal and the waitpid API
5. Explain the sigsetjmp and siglongjmp Functions with examples.
6. Explain the kill() API and alarm() API?
7. What are daemon processes? Explain daemon characteristics and basic coding rules. Write a
function that can be called from a program that wants to initialize itself as a daemon
8. What is error logging? Explain with a neat diagram the error logging facility for a daemon
process.
9. Write a short note on client server model.