30 martie 2011

Exemple cu semnale - Linux

1. Urmatorul program trateaza semnale intre 20 (SIGTSTP) si 25 (SIGXFSZ), afisand pentru fiecare cate un caracter. Intr-un alt terminal se va tasta pgrep hitme , apoi kill -x pid , unde x este semnalul dorit, iar pid id-ul procesului aflat cu pgrep.

#include "stdio.h"
#include "string.h"
#include "signal.h"
#include "unistd.h"
#include "utils.h"

int must_leave = 0;

static void sig_handler(int signum)
{
switch(signum) {

case SIGTSTP:
printf("H");
break;

case SIGTTIN:
printf("e");
break;

case SIGTTOU:
printf("l");
break;

case SIGURG:
printf("l");
break;

case SIGXCPU:
printf("o");
break;

case SIGXFSZ:
printf("\n");
must_leave = 1;
break;

default:
printf("Go back and send the following signals: 20, 21, 22, 23, 24");
must_leave = 1;
}
fflush(stdout);
}

int main(void)
{
struct sigaction signals;
sigset_t mask;
int rc;

sigemptyset(&mask);

memset(&signals, 0, sizeof(struct sigaction));
signals.sa_flags = 0;
signals.sa_mask = mask;

signals.sa_handler = sig_handler;
rc = sigaction(SIGTSTP, &signals, NULL);
DIE(rc == -1, "sigaction");

rc = sigaction(SIGTTIN, &signals, NULL);
DIE(rc == -1, "sigaction");

rc = sigaction(SIGTTOU, &signals, NULL);
DIE(rc == -1, "sigaction");
rc = sigaction(SIGURG, &signals, NULL);
DIE(rc == -1, "sigaction");
rc = sigaction(SIGXCPU, &signals, NULL);
DIE(rc == -1, "sigaction");

rc = sigaction(SIGXFSZ, &signals, NULL);
DIE(rc == -1, "sigaction");
while(!must_leave) sleep(1);

return 0;
}


2. Urmatorul program face busy waiting, afisand la consola numere consecutive. Intercepteaza semnale generate de CTRL+\, CTRL+C, SIGUSR1. Fiecare semnal are asociat handler-ul ask_handler, iar pentru fiecare semnal primit programul va intreba daca este cazul sa se termine sau nu.


#define MY_MAX 32
#define TIMEOUT 1

static void print_next(void)
{
static int n = 1;

printf("n = %d\n", n);

n = (n + 1) % MY_MAX;
}

/* signal handler */
static void ask_handler(int signo)
{
char buffer[128];
printf("Got %d - Stop program? [Y/n] ", signo);
fflush(stdout);
fgets(buffer, 128, stdin);
buffer[strlen(buffer)-1] = '\0';

if (buffer[0] == 'y' || buffer[0] == 'Y')
exit(EXIT_SUCCESS);
}

/* configure handlers */
static void set_signals(void)
{
struct sigaction sa;
int rc;

/* set handler in struct sigaction sa */
sigset_t mask;
sigemptyset(&mask);

memset(&sa, 0, sizeof(struct sigaction));
sa.sa_flags = 0; //
sa.sa_mask = mask;

sa.sa_handler = ask_handler;

/* handle SIGINT, SIGQUIT and SIGUSR1 signals */
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
}

int main(void)
{
set_signals();

while (1) {
print_next();
sleep(TIMEOUT);
}

return 0;
}


3. Urmatorul exemplu simuleaza comanda nohup. Programul primeste ca parametru numele comenzii de executat si parametrii comenzii respective, si nu trebuie sa se termine la inchiderea terminalului din care a fost pornit.
Pentru asta, trebuie sa ignore semnalul SIGHUP pe care i-l livreaza shell-ul. Daca outputul programului (stdout) era legat la terminal, acesta va fi redirectat catre un fisier.

/* configure handlers */
static void set_signals(void)
{
struct sigaction sa;
int rc;

memset(&sa, 0, sizeof(sa));

/* ignore SIGHUP */
sa.sa_handler = SIG_IGN;
sigaction(SIGHUP, &sa, NULL);
}

/* execute a new program */
static void exec_func(int argc, char **argv)
{
int rc;

set_signals(); /* ignore SIGHUP */

/* if stdout is a terminal redirect output to NOHUP_OUT_FILE */
if (isatty(STDOUT_FILENO)) {
int new_fd = open ("nohup_out_file", O_CREAT | O_WRONLY, 0644);
rc = dup2 (new_fd, STDOUT_FILENO);
if(rc<0) {
printf("error\n");
exit(0);
}
}

/* replace image of process */
rc = execvp(argv[1], argv+1);
if (rc<0) {
printf("error\n");
exit(0);
}
}

int main(int argc, char **argv)
{
if (argc <= 1) {
fprintf(stderr, "Usage: %s command_and_arguments\n", argv[0]);
exit(EXIT_FAILURE);
}

exec_func(argc, argv);

return 0;
}

4 . In exemplele de mai jos, fiecare proces va crea cate un proces-copil care doar apeleaza exit.

Primul exemplu este de proces zombie, deoarece dupa crearea copilului, parintele nu face wait pe el; deci, intrarea copilului va ramane in tabela de procese, el nemaiexistand practic pentru ca a dat exit.

#define TIMEOUT 20

int main(void)
{
pid_t pid;
int status;

/* create child process without waiting */
pid = fork ();
switch (pid) {
case -1:
exit(-1);
break;
case 0:
exit(0);
default:
sleep(TIMEOUT);
break;
}

return 0;
}

In urmatorul exemplu, parintele creeaza copilul si de asemenea nu ii da wait. Insa nu mai avem de-a face cu un copil zombie pentru ca parintele ignora semnalul de tip SIGCHLD. Pe scurt, daca parintele ignora SIGCHLD prin setarea handler-ului la SIG_IGN, toate informatiile de exit status ale copiilor nu vor fi luate in considerare, si nu se vor lasa procese zombie.

/* configure signal handler */
static void set_signals(void)
{
struct sigaction sa;

memset(&sa, 0, sizeof(sa));

/* ignore SIGCHLD */
sa.sa_handler = SIG_IGN;
sigaction(SIGCHLD, &sa, NULL);
}

int main(void)
{
pid_t pid;
set_signals();

/* create child process without waiting */
pid = fork();

switch (pid) {
case -1:
exit(-1);
break;
case 0:
exit(0);
default:
sleep(TIMEOUT);
break;
}
// fara wait

return 0;
}

Niciun comentariu: