Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
2 views23 pages

@vtudeveloper - in Unix Mod 5

The document provides an overview of signals and daemon processes in programming, detailing how signals act as software interrupts that can be handled in various ways by processes. It explains the default actions associated with different signals, the use of signal handling functions like signal() and sigaction(), and the manipulation of signal masks and sets. Additionally, it describes the behavior of the SIGCHLD signal in relation to child processes and the waitpid API for managing child process termination.

Uploaded by

happy worker
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views23 pages

@vtudeveloper - in Unix Mod 5

The document provides an overview of signals and daemon processes in programming, detailing how signals act as software interrupts that can be handled in various ways by processes. It explains the default actions associated with different signals, the use of signal handling functions like signal() and sigaction(), and the manipulation of signal masks and sets. Additionally, it describes the behavior of the SIGCHLD signal in relation to child processes and the waitpid API for managing child process termination.

Uploaded by

happy worker
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 23

MODULE 5

Signals and Daemon Processes


SIGNALS
 Signals are software interrupts.
 Signals provide a way of handling asynchronous events.
 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:
1. Accept the default action of the signal, which for most signals will terminate the
process.
2. Ignore the signal. The signal will be discarded and it has no affect whatsoever
on the recipient process.
3. 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.

Name Description Default action

SIGABRT abnormal termination (abort) terminate+core

SIGALRM timer expired (alarm) terminate

SIGCHLD change in status of child ignore

SIGCONT continue stopped process continue/ignore

SIGFPE arithmetic exception terminate+core

SIGINT terminal interrupt character terminate

SIGIO asynchronous I/O terminate/ignore

SIGKILL termination terminate

SIGPIPE write to pipe with no readers terminate

SIGQUIT terminal quit character terminate+core

SIGSEGV invalid memory reference terminate+core

SIGSTOP stop stop process


Name Description Default action

SIGTTOU background write to control tty stop process

SIGUSR1 user-defined signal Terminate

SIGUSR2 user-defined signal Terminate

SIGTERM termination Terminate

SIGTSTP terminal stop character stop process

SIGTTIN background read from control tty stop process

The function prototype of the signal API is:


#include <signal.h>
void (*signal(int sig_no, void (*handler)(int))) (int);
Returns: previous disposition of signal (see following) if OK, SIG_ERR on error
• The prototype for the signal function states that the function requires two arguments
and returns a pointer to a function that returns nothing (void).
• The signal function's first argument, sig_no, is an integer.
• The second argument is a pointer to a function that takes a single integer argument and
returns nothing.
• The function whose address is returned as the value of signal takes a single integer
argument (the final (int)).

void (*signal(int sig_no, void (*handler)(int)))(int);


• The formal argument of the API are:
 sig_no is a signal identifier like SIGINT or SIGTERM.
 The handler argument is the function pointer of a user-defined signal handler
function.
• The sig_no argument is just the name of the signal.
• The value of handler is
(a) the constant SIG_IGN,
(b) the constant SIG_DFL, or
(c) the address of a function to be called when the signal occurs.

The following example attempts to catch the SIGINT signal and accepts the default action of
the SIGINT signal.
#include<stdio.h>
#include<iostream.h> #include<signal.h>
/*signal handler function*/ void catch_sig(int sig_num)
{
Printf(“I got signal no.:%d\n”,sig_num);
//cout<<”I got signal no.:”<<sig_num<<endl;
signal (SIGINT,SIG_DFL);
}
int main() /*main function*/
{
signal(SIGINT,catch_sig);
While(1);
}
#include<stdio.h> #include<signal.h>
/*signal handler function*/ static void sig_usr(int signo)
/* arg is signal number */
{
if (signo == SIGUSR1) printf("received SIGUSR1\n");
else if (signo == SIGUSR2) printf("received SIGUSR2\n");
else
printf("received signal %d\n", signo);
}
int main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR) perror("can't catch SIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR) perror("can't catch SIGUSR2");
for ( ; ; )
pause();
}

$ ./a.out &
[1] 7216
$ kill -USR1 7216 received SIGUSR1
$ kill -USR2 7216
received SIGUSR2
$ kill 7216
start process in background
job-control shell prints job number and process ID send it SIGUSR1
send it SIGUSR2
now send it SIGTERM
now send it SIGTERM
When we send the SIGTERM signal, the process is terminated, since it doesn't catch the signal,
and the default action for the signal is termination.
Kill and raise Functions
• The kill function sends a signal to a process or a group of processes.
• The raise function allows a process to send a signal to itself.
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
Both return: 0 if OK, –1 on error
The call raise(signo); is equivalent to the call kill(getpid(),signo);
#include <signal.h>
int kill(pid_t pid, int signo);
There are four different conditions for the pid argument to kill.

Pid Meaning
value

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.

Kill:
#include<iostream.h> #include<signal.h>
/*signal handler function*/ void catch_sig(int sig_num)
{
cout<<”catch_sig:”<<sig_num<<endl;
}
int main() /*main function*/
{
signal (SIGINT,catch_sig); cout<<“from main\n”;
kill(getpid(), SIGINT);
}
raise:
#include<iostream.h> #include<signal.h>
/*signal handler function*/ void catch_sig(int sig_num)
{
cout<<”catch_sig:”<<sig_num<<endl;
}
int main() /*main function*/
{
signal (SIGQUIT,catch_sig); cout<<“from main\n”; raise(SIGQUIT);
}
Alarm and pause Functions
• The alarm function allows us to set a timer that will expire at a specified time in the
future.
• When the timer expires, the SIGALRM signal is generated.
• If we ignore or don't catch this signal, its default action is to terminate the process.
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
Returns: 0 or number of seconds until previously set alarm.
 The seconds value is the number of clock seconds in the future when the signal should
be generated.
 If, when we call alarm, a previously registered alarm clock for the process has not yet
expired, the number of seconds left for that alarm clock is returned as the value of this
function. That previously registered alarm clock is replaced by the new value.
 If a previously registered alarm clock for the process has not yet expired and if the
seconds value is 0, the previous alarm clock is canceled.
 Although the default action for SIGALRM is to terminate the process, most processes
that use an alarm clock catch this signal.
Pause():
The pause function suspends the calling process until a signal is caught.
#include <unistd.h>
int pause(void);
Returns: –1 with errno set to EINTR
The only time pause returns is if a signal handler is executed and that handler returns.
In that case, pause returns –1 with errno set to EINTR.
Using alarm and pause, we can put a process to sleep for a specified amount of time. The
sleep() can be implemented using alarm() and pause().
#include <signal.h> #include <unistd.h>
static void sig_alrm(int signo)
{
/* nothing to do, just return to wake up the pause */
}

unsigned int sleep(unsigned int nsecs)


{
if (signal(SIGALRM, sig_alrm) == SIG_ERR) return(nsecs);
alarm(nsecs); /* start the timer */
pause(); /* next caught signal wakes us up */ return(alarm(0));
/* turn off timer, return unslept time */
}
SIGNAL SETS
• We need a data type to represent multiple signals—a signal set
POSIX.1 defines the data type sigset_t to contain a signal set and the following five functions
to manipulate signal sets.
#include <signal.h>
int sigemptyset(sigset_t*sigmask);
int sigfillset(sigset_t* sigmask);
int sigaddset(sigset_t *sigmask, int signal_num );
int sigdelset(sigset_t * sigmask, int signal_num );
Returns: 0 if OK, -1 on error.
int sigismember(const sigset_t*sigmask, int signal_num );
Returns: 1 if true, 0 if false, –1 on error
int sigemptyset(sigset_t*sigmask);
int sigfillset(sigset_t* sigmask);
int sigaddset(sigset_t *sigmask, int signal_num );
int sigdelset(sigset_t * sigmask, int signal_num );
int sigismember(const sigset_t *sigmask, int signal_num );
• The sigemptyset API clears all signal flags in the sigmask argument.
• The sigaddset API sets the flag corresponding to the signal_num signal in the sigmask
argument.
• The sigdelset API clears the flag corresponding to the signal_num signal in the
sigmask argument.
• The sigfillset API sets all the signal flags in the sigmask argument
• The sigismember will test a certain bit,API returns 1 if flag is set, 0 if not set
and -1 if the call fails.
SIGNAL MASK
• 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);
Returns: 0 if OK, -1 on error
• The new_mask argument defines a set of signals to be set or reset in a calling process
signal mask, and the cmd argument specifies how the new_mask value is to be used by
the API.
• The possible values of cmd and the corresponding use of the new_mask value are:

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. Then clears the SIGSEGV signal from the
process signal mask.

#include <stdio.h> #include <signal.h> int main()


{
sigset_t mask; sigemptyset(&mask);
/*initialize set*/
if (sigprocmask(0, 0, &mask) == -1)
{ /*get current signal mask*/ perror(“sigprocmask”); exit(1);
}
else
if (sigismember(&mask, SIGINT)==0)
sigaddset(&mask, SIGINT); /*set SIGINT flag*/
sigdelset(&mask, SIGSEGV);
/*clear SIGSEGV flag*/
if (sigprocmask(SIG_SETMASK, &mask, 0) == -1) perror(“sigprocmask”);
/*set a new signal mask*/
}.
The program prints the names of the signals in the signal mask of the calling
process
#include <stdio.h> #include <signal.h>
int main()
{
sigset_t sigset; sigemptyset(&sigset);
/*initialize set*/
if (sigprocmask(0, NULL, &sigset) < 0)
perror("sigprocmask error");
if (sigismember(&sigset, SIGINT)) printf("SIGINT ");
if (sigismember(&sigset, SIGQUIT)) printf("SIGQUIT ");
if (sigismember(&sigset, SIGUSR1)) printf("SIGUSR1 ");
if (sigismember(&sigset, SIGALRM)) printf("SIGALRM ");
}
SIGPENDING FUNCTION
The sigpending function returns the set of signals that are blocked from delivery and currently
pending for the calling process.
The set of signals is returned through the set argument
#include <signal.h>
int sigpending(sigset_t *set);
Returns: 0 if OK, –1 on error.
The program reports to the console whether the SIGTERM signal is pending for
the process
#include <stdio.h> #include <signal.h> int main()
{
int i;
sigset_t sigset; sigemptyset(&sigset);
/*initialize set*/
if (sigprocmask(0, NULL, &sigset) < 0)
perror("sigprocmask error");
if (sigispending(&sigset)==-1)
perror(“sigpending”);
else
i=sigismemeber(&sigset,SIGTERM) ?1:0;
If(i==1)
printf(“SIGTERM is set”);
else
printf(“SIGTERM is not set”);
Sigaction() Function
 The sigaction() function allows us to examine or modify (or both) the action associated
with a particular signal.
 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.
 sigaction API returns the previous signal handling method for a given signal.
#include <signal.h>
int sigaction(int signo, const struct sigaction *action, struct sigaction *oldaction);
Returns: 0 if OK, –1 on error
 The signo argument designates which signal handling action is defined in the action
argument.
 The previous signal handling method for signo will be returned via the oldaction
argument if it is not a NULL pointer.
 If action argument is a NULL pointer, the calling
process‘s existing signal
handling method for signo will be unchanged.
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_flags;
};
/* addr of signal handler, or SIG_IGN, or SIG_DFL */
/* additional signals to block */
/* signal options */

 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 signo signal.
#include <signal.h> #include <iostream.h> void callme ( int sig_num )
{
cout <<“catch signal:”<<sig_num<< endl;
}
int main(void)
{
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(); /* wait for signal interruption*/ return 0;
}
 In the program, the process signal mask is set with SIGTERM signal.
 The process then defines a signal handler for the SIGINT signal and also specifies that
the SIGSEGV signal is to be blocked when the process is handling the SIGINT signal.
 The process then terminates its execution via the pause API.
The output of the program would be as:
% cc sigaction.c –o sigaction
% ./sigaction & [1] 495
% kill –INT 495 catch signal: 2 sigaction exits
[1] Done sigaction

THE SIGCHLD SIGNAL AND THE waitpid API


• When a child process terminates or stops, the kernel will generate a SIGCHLD signal
to its parent process.
• Depending on how the parent sets up the handling of the SIGCHLD signal, different
events may occur:
1. Parent accepts the default action of the SIGCHLD signal:
 SIGCHLD does not terminate the parent process.
 Parent process will be awakened.
 API will return the child’s exit status and process ID to the parent.
 Kernel will clear up the Process Table slot allocated for the child process.
 Parent process can call the waitpid API repeatedly to wait for each child it
created.
2. Parent ignores the SIGCHLD signal:
 SIGCHLD signal will be discarded.
 Parent will not be disturbed even if it is executing the waitpid system call.
 If the parent calls the waitpid API, the API will suspend the parent until all its
child processes have terminated.
 Child process table slots will be cleared up by the kernel.
 API will return a -1 value to the parent process.
3. Process catches the SIGCHLD signal:
 The signal handler function will be called in the parent process whenever a child
process terminates.
 If the SIGCHLD arrives while the parent process is executing the waitpid
system call, the waitpid API may be restarted to collect the child exit status and
clear its process table slots.
 Depending on parent setup, the API may be aborted and child process table slot
not freed.
sigsetjmp AND siglongjmp APIs:
The function prototypes of the APIs are:
#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);
int siglongjmp(sigjmp_buf env, int val);
• The sigsetjmp and siglongjmp are created to support signal mask processing.
• 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 user-defined 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.
#include <signal.h>
#include <iostream.h>
#include<unistd.h>
#include<signal.h>
#include<setjmp.h>
sigjmp_buf env;
void callme ( int sig_num )
{
cout <<“catch signal:”<<sig_num<< endl;
siglongjump(env, 2);
}
int main(void)
{
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”);
if (sigsetjmp(env,1)!=0)
printf(“return from signal interruption”);
else
printf(“return from first time sigsetjmp is called”);
pause(); /* wait for signal interruption*/
return 0;
}
Execution:
$cc -o jmp sigsetlongjmp.c
$ jmp &
[1] 499
return from first time sigsetjmp is called
$kill -INT 499
Catch signal: 2
return from signal interruption
[1] Done
$
sigsuspend Function
sigset_t newmask, oldmask;
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/* block SIGINT and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
/* critical region of code */
/* reset signal mask, which unblocks SIGINT */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
/* window is open */
pause(); /* wait for signal to occur */
/* continue processing */

sigsuspend Function
• If the signal is sent to the process while it is blocked, the signal delivery will be deferred
until the signal is unblocked.
• What if the signal occurs between the unblocking and the pause If this happens, or if
the signal does occur between the unblocking and the pause, we have a problem. Any
occurrence of the signal in this window of time is lost in the sense that we might not
see the signal again, in which case the pause will block indefinitely.
• This is another problem with the earlier unreliable signals.
• To correct this problem, we need a way to both reset the signal mask and put the process
to sleep in a single atomic operation. This feature is provided by the sigsuspend
function.
• Normally, at the beginning of a critical code section, a specified set of signals is blocked
using the sigprocmask() function. When the thread has completed the critical section
and needs to wait for the previously blocked signal(s), it pauses by calling sigsuspend()
with the mask that was returned by the sigprocmask() call.
sigsuspend Function
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
Returns: 1 with errno set to EINTR
• sigsuspend() function shall replace the current signal mask of the calling thread with
the set of signals pointed to by sigmask and then suspend the thread until delivery of a
signal whose action is either to execute a signal-catching function or to terminate the
process.
• This shall not cause any other signals that may have been pending on the process to
become pending on the thread.
• If the action is to terminate the process then sigsuspend() shall never return. If the action
is to execute a signal-catching function, then sigsuspend() shall return after the signal-
catching function returns, with the signal mask restored to the set that existed prior to
the sigsuspend() call.

static void sig_int(int);


Int main(void)
{
sigset_t newmask, oldmask, waitmask;
pr_mask("program start: ");
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
sigemptyset(&waitmask);
sigaddset(&waitmask, SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/* Block SIGINT and save current signal mask.*/
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
/*Critical region of code.*/
pr_mask("in critical region: ");
/*Pause, allowing all signals except SIGUSR1.*/
if (sigsuspend(&waitmask) != -1)
err_sys("sigsuspend error");
pr_mask("after return from sigsuspend: ");
/*Reset signal mask which unblocks SIGINT.*/
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
/*And continue processing ...*/
pr_mask("program exit: ");
exit(0);
Abort Function
#include <stdlib.h>
void abort(void);
This function never returns
• abort function causes abnormal program termination.
• This function sends the SIGABRT signal to the caller. (Processes should not
ignore this signal.)
• ISO C states that calling abort will deliver an unsuccessful termination
notification to the host environment by calling raise(SIGABRT).
• But unlike the exit() function, abort() may not close files that are open. It may
also not delete temporary files and may not flush the stream buffer. Also, it does
not call functions registered with atexit().
Abort()Function
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* fp = fopen("C:\\myfile.txt", "w");
if (fp == NULL) {
printf("\n could not open file ");
getchar();
exit(1);
}
fprintf(fp, "%s", “Hello ");
/* ....... */
/* ....... */
/* Something went wrong so terminate here */
abort();
return 0;
}
System Function
#include<stdlib.h> int system(const char *string)
Returns: This function returns 0 if the command is executed successfully. Otherwise, it returns
a non-zero value.
• Used to execute an operating system command specified by the string 'command' from
a c/c++ program.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main() {
// char command[50];
//strcpy(command, “ls"); // Set the command to “ls" (list files and directories)
//system(command); //use the system fucntion to execute the command.
system(“ls”);
Sleep Function
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
Returns: 0 or number of unslept seconds
• sleep() function in C allows the users to wait/suspend for a current thread for a specific
time in seconds.
• The sleep() function in C returns 0 if the requested time has elapsed.
#include <stdio.h>
#include <unistd.h>
void main()
{
printf("Program to sleep for 10 second in Linux.\n");
sleep(10);
// after 10 seconds this next line will be executed.
printf("This line will be executed after 10 second.");
}
Sleep Function
sleep function causes the calling process to be suspended until either
1. The amount of wall clock time specified by seconds has elapsed.
2. A signal is caught by the process and the signal handler returns.
• As with an alarm signal, the actual return may be at a time later than requested, because
of other system activity.
• In case 1, the return value is 0. When sleep returns early, because of some signal being
caught (case 2), the return value is the number of unslept seconds (the requested time
minus the actual time slept).
Nanosleep Function
#include <time.h>
int nanosleep(const struct timespec *t1, struct timespec *t2)
Return values: On successful execution, it returns the value 0 else it returns -1.
• tv_sec, which is the number of seconds.
• tv_nsec, which is the number of nanoseconds.
• It is used to suspend the execution of the program for a specific amount of time for
nanoseconds (1,000,000,000 or 1e+9 nanoseconds in a second).
#include <stdio.h>
#include <time.h>
int main()
{
struct timespec request, remaining = { 5, 100 };
printf("Taking a nap...\n");
int response = nanosleep(&request, &remaining);
if (response == 0) { printf("Nap was Successful .\n"); }
else {
printf("Nap was Interrupted.\n"); }
}
clock_nanosleep
#include <time.h>

int clock_nanosleep(clockid_t clockid, int flags,


const struct timespec *t,
struct timespec *_Nullable remain);
DESCRIPTION top
Like nanosleep(2), clock_nanosleep() allows the calling thread to sleep for an interval
specified with nanosecond precision. It differs in allowing the caller to select the clock against
which the sleep interval is to be measured, and in allowing the sleep interval to be specified as
either an absolute or a relative value.
The time values passed to and returned by this call are specified using timespec(3) structures.
The clockid argument specifies the clock against which the sleep interval is to be measured.
This argument can have one of the following values:
CLOCK_REALTIME
A settable system-wide real-time clock.
CLOCK_TAI (since Linux 3.10)
A system-wide clock derived from wall-clock time but counting leap seconds.
CLOCK_MONOTONIC
A nonsettable, monotonically increasing clock tha measures time since some unspecified
point in the pass that does not change after system startup.
CLOCK_BOOTTIME (since Linux 2.6.39)
Identical to CLOCK_MONOTONIC, except that it also includes any time that the system
is suspended.
CLOCK_PROCESS_CPUTIME_ID
A settable per-process clock that measures CPU time consumed by all threads in the process
If flags is 0, then the value specified in t is interpreted as an interval relative to the current
value of the clock specified by clockid.
If flags is TIMER_ABSTIME, then t is interpreted as an absolute time as measured by the
clock, clockid. If t is less than or equal to the current value of the clock, then clock_nanosleep()
returns immediately without suspending the calling thread.
clock_nanosleep() suspends the execution of the calling thread until either at least the time
specified by t has elapsed, or a signal is delivered that causes a signal handler to be called orthat
terminates the process. If the call is interrupted by a signal handler, clock_nanosleep() fails
with the error EINTR. In addition, if remain is not NULL and flags was not
TIMER_ABSTIME, it returns the remaining unslept time in remain. This value can then be
used to call clock_nanosleep() again and complete a (relative) sleep.

RETURN VALUE
On successfully sleeping for the requested interval, clock_nanosleep() returns 0. If the call is
interrupted by a signal handler or encounters an error, then it returns one of the positive error
number listed in ERRORS.
ERRORS
EFAULT t or remain specified an invalid address.
EINTR The sleep was interrupted by a signal handler; see
. EINVAL The value in the tv_nsec field was not in the range [0,999999999] or tv_sec was
negative.
EINVAL clockid was invalid. (CLOCK_THREAD_CPUTIME_ID is not a
permitted value for clockid.)
ENOTSUP
The kernel does not support sleeping against this clockid.
Job-Control Signals
• Job control signals are used to control processes, such as suspending or stopping them,
and resuming them later
• POSIX.1 considers six to be job-control signals:
• SIGCONT: Used to continue a stopped process
• SIGSTOP: Used to stop a process (can’t be caught or ignored)
• SIGTSTP: Used to stop a process (interactive stop signal)
• SIGTTIN: Used to stop a background process when it tries to read from the controlling
terminal
• SIGTTOU: Used to stop a background process when a user tries to write to or modify
the controlling terminal
• SIGKILL: Used to terminate a process and cannot be blocked, handled, or ignored

To use job control in Bash shell, we can:


• Type the suspend character, typically ^Z or Control-Z, while a process is running
• Type the delayed suspend character, typically ^Y or Control-Y, to stop the process
when it tries to read input from the terminal
• Use the bg command to continue the job/process in the background
• Use the fg command to continue the job/process in the foreground
• Use the kill command to kill the job/process
Sigqueue functions(like kill () )
• The sigqueue() function is used to queue a signal to a process or to itself, along with an
application-defined value. It's commonly used in real-time applications.
• The sigqueue() function causes the signal specified by signo to be sent with the value
specified by value to the process specified by pid. If signo is zero (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.
#include<sys/types.h>
#include <signal.h>
int sigqueue(pid_t pid, int signo, const union sigval value)
Return value: 0 on success, -1 on failure
Here's how sigqueue() works:
Sends signal: If the resources are available and SA_SIGINFO is set for the signal, the signal is
queued and sent to the receiving process.
Checks for permission: The sending and receiving processes must have the same real or
effective user ID.
Error checking: If signo is zero, error checking is performed but no signal is sent.
Returns: sigqueue() returns immediately
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.
 Because they don't have a controlling terminal, we say that they run in the background.
 UNIX systems have numerous daemons that perform day-to-day activities.
Deamon Characteristics
 $ps –axj

Deamon Characteristics
 Daemons run in background.
 Daemons have super-user privilege.
 Daemons don’t have controlling terminal.
 Daemons are session and group leaders.
 Anything with a parent process ID of 0 is usually a kernel process started as part of the
system bootstrap procedure.
 Kernel processes are special and generally exist for the entire lifetime of the system.
 They run with superuser privileges and have no controlling terminal and no command
line.
 Process ID of 1 is usually init.
 It is a system daemon responsible for, among other things, starting system services
specific to various run levels.
CODING RULES
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. 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); /* parent exits */
/* 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.
 We also don't want each daemon writing its own error messages into a separate file.
 A central daemon errorlogging facility is required.

Error Logging

There are three ways to generate log messages:


1. Kernel routines can call the log function.These messages can be read by any user
process that opens and reads the
/dev/klog device.
2. Most user processes (daemons) call the syslog(3) function to generate log messages.
This causes the message to be sent to the UNIX domain datagram socket /dev/log.
3. 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.

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);
SINGLE-INSTANCE DAEMONS:
 Some daemons are implemented so that only a single copy of the daemon should be
running at a time for proper operation. The file and record-locking mechanism provides
the basis for one way to ensure that only one copy of a daemon is running. If each
daemon creates a file and places a write lock on the entire file, only one such write lock
will be allowed to be created. Successive attempts to create write locks will fail, serving
as an indication to successive copies of the daemon that another instance is already
running.
 File and record locking provides a convenient mutual-exclusion mechanism. If the
daemon obtains a write-lock on an entire file, the lock will be removed automatically if
the daemon exits. This simplifies recovery, removing the need for us to clean up from
the previous instance of the daemon.
PROGRAM:Ensure that only one copy of a daemon is running
DAEMON CONVENTIONS:
If the daemon uses a lock file, the 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.
 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.
 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.

CLIENT-SERVER MODEL:
In general, a server is a process that waits for a client to contact it, requesting some type of
service. The service being provided by the syslogd server is the logging of an error message.
The communication between the client and the server is one-way. The client sends its service
request to the server; the server sends nothing back to the client. In the upcoming chapters,
we'll see numerous examples of two-way communication between a client and a server. The
client sends a request to the server, and the server sends a reply back to the client.

You might also like