1. Trang chủ
  2. » Công Nghệ Thông Tin

Thực hành hệ điều hành 2 tín hiệu và truyền thông giữa các quy trình

11 169 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 11
Dung lượng 68 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Instead, when the signal is sent to the process, the operating system stops the execution of the process, and "forces" it to call the signal handler function.. When that signal handler f

Trang 1

Tirgul 2

Signals and communication between processes

What Are Signals?

Signals, to be short, are various notifications sent to a process in order to notify it

of various "important" events By their nature, they interrupt whatever the process

is doing at this minute, and force it to handle them immediately Each signal has an integer number that represents it (1, 2 and so on), as well as a symbolic name that

is usually defined in the file /usr/include/signal.h or one of the files included by it directly or indirectly (HUP, INT and so on Use the command 'kill -l' to see a list of signals supported by your system)

Each signal may have a signal handler, which is a function that gets called when the process receives that signal The function is called in "asynchronous mode", meaning that no where in your program you have code that calls this function directly Instead, when the signal is sent to the process, the operating system stops the execution of the process, and "forces" it to call the signal handler function When that signal handler function returns, the process continues execution from wherever it happened to be before the signal was received, as if this interruption never occurred

What Are Signals Used For?

Signals are usually used by the operating system to notify processes that some event occurred, without these processes needing to poll for the event

Trang 2

Sending Signals Using the Keyboard

The most common way of sending signals to processes is using the keyboard There are certain key presses that are interpreted by the system as requests to send signals to the process with which we are interacting:

Ctrl-C

Pressing this key causes the system to send an INT signal (SIGINT) to the running process By default, this signal causes the process to immediately terminate

Ctrl-Z

Pressing this key causes the system to send a TSTP signal (SIGTSTP) to the running process By default, this signal causes the process to suspend

execution

Ctrl-\

Pressing this key causes the system to send a ABRT signal (SIGABRT) to the running process By default, this signal causes the process to

immediately terminate Note that this redundancy (i.e Ctrl-\ doing the same

as Ctrl-C) gives us some better flexibility

Sending Signals from the Command Line

Another way of sending signals to processes is done using various commands, usually internal to the shell:

kill

The kill command accepts two parameters: a signal name (or number), and a process ID Usually the syntax for using it goes something like:

kill -<signal> <PID>

fg

On most shells, using the 'fg' command will resume execution of the process (that was suspended with Ctrl-Z), by sending it a CONT signal

Trang 3

Sending Signals Using System Calls

#include <unistd.h> /* standard unix functions, like getpid() */

#include <sys/types.h> /* various type definitions, like pid_t */

#include <signal.h> /* signal name macros, and the kill() prototype */

/* first, find my own process ID */

pid_t my_pid = getpid();

/* now that i got my PID, send myself the STOP signal */

kill(my_pid, SIGSTOP);

Catchable and Non-Catchable Signals

Some signals cannot be caught and handled by processes For example KILL which causes termination (from the shell: kill -9 …), STOP which causes

suspension (can later be resumed with a CONT signal)

Note STOP is not the signal generated by Ctrl+Z.

On the other hand, signals such as SEGV (notify of segmentation fault – meaning accessing illegal memory address) and BUS (notify of bus error – meaning

accessing memory address with invalid alignment) are both catchable.

Default Signal Handlers

There is a default signal handler for all signals For example the default handler for TERM is call the exit() system call.

The pause() System Call

the pause() system call causes the process to halt execution, until some signal is received.

The signal() System Call

usage: Signal( <signal number> , <pointer to function> );

Causes the next time the specified signal is received, to call the given function instead of the default function associated with that signal

Trang 4

#include <stdio.h> /* standard I/O functions */

#include <unistd.h> /* standard unix functions, like getpid() */

#include <sys/types.h> /* various type definitions, like pid_t */

#include <signal.h> /* signal name macros, and the signal() prototype */

/* first, here is the signal handler */

void catch_int(int sig_num)

{

/* re-set the signal handler again to catch_int, for next time */

signal(SIGINT, catch_int);

/* and print the message */

printf("Don't do that\n");

}

/* set the INT (Ctrl-C) signal handler to 'catch_int' */

signal(SIGINT, catch_int);

/* now, lets get into an infinite loop of doing nothing */

while (true) {

pause();

}

Pre-defined Signal Handlers

SIG_IGN:

Causes the process to ignore the specified signal.

signal(SIGINT, SIG_IGN);

SIG_DFL:

Causes the system to set the default signal handler for the given signal (i.e the same handler the system would have assigned for the signal when the process started running): signal(SIGTSTP, SIG_DFL);

Example 1 – Process communication:

Trang 5

/***************************************************************************/ /* This program demonstrates sending signals between processes: */ /* The 'father' process creates two 'son' processes Trying to compute the */ /* same value The first to finish exits and the 'father' 'kills' its */ /* slow 'son' by sending a signal */ /***************************************************************************/

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <fcntl.h>

#include <sys/uio.h>

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <sys/fcntl.h>

typedef struct _param {

int number;

int sleep_time;

} param;

//This function will be calld as a reaction to catching a signal

void catchSigT(int sig_num)

{

printf("pid %d operated catchSigT\n",getpid());

signal(SIGTERM, catchSigT);

};

void elegant_death(int sig_num)

{

printf("pid %d declares it's loss with dignity\n",getpid());

signal(SIGINT, catchSigT);

exit(0);

};

int pid1 = 1, pid2 = 1; //will hold the process id's of the sons

int main (char **argv, int argc)

{

param param1, param2;

param args;

int * stat;

int first,i;

long result;

param1.number = 10; //the number of times the first son will

preform the calculation

param1.sleep_time = 2; //the time that the first son will sleep in each comutation loop

param2.number = 10; //the number of times the second son will preform the calculation

param2.sleep_time = 7; //the time that the first son will sleep in each comutation loop

Trang 6

//defining the catch of SIGINT and SIGTERM to activate the 'elegent_death' & 'catch sig' function

signal(SIGTERM, catchSigT);

signal(SIGINT, elegant_death);

//forking the two sons

pid1 = fork();

if(pid1 != 0)

pid2 = fork();

//sons code

if(pid1 == 0 || pid2 == 0)

{

pause();//waiting for the father to begin the computation

//updating the pointer to the right structure

if(pid1 == 0)

args = param1;

else

args = param2;

//computing

result = 1;

for (i=1; i<args.number; i++)

{

result = result * i;

printf("Current result = %d from %d\n", result, getpid());

sleep(args.sleep_time);

}

printf("Final result =%d, given by pid %d\n", result, getpid());

exit(0);

return NULL;

}

sleep(1);//waiting for sons to pause

//waiking sons

kill(pid1,SIGTERM);

kill(pid2,SIGTERM);

printf("Created processes with pid 1 =%d and pid 2 =%d\n", pid1, pid2); first = wait(stat);//waiting for the first son to finish

//killing the son that did not finish

if(first == pid1)

{

kill(pid2, SIGINT);

printf("pid1: %d finished first and dad is killing pid2: %d\n",

pid1,pid2);

}

else

{

kill(pid1, SIGINT);

printf("pid2: %d finished first and dad is killing pid1: %d\n",

pid2,pid1);

}

}

Example1 output:

Trang 7

pid 14876 operated catchSigT

Current result = 1 from 14876

pid 14877 operated catchSigT

Current result = 1 from 14877

Created processes with pid 1 =14876 and pid 2 =14877

Current result = 2 from 14876

Current result = 6 from 14876

Current result = 24 from 14876

Current result = 2 from 14877

Current result = 120 from 14876

Current result = 720 from 14876

Current result = 5040 from 14876

Current result = 40320 from 14876

Current result = 6 from 14877

Current result = 362880 from 14876

Final result =362880, given by pid 14876

pid1: 14876 finished first and dad is killing pid2: 14877

pid 14877 declares it's loss with dignity

Trang 8

Example 2 – more complicated:

/

******************************************************************************

***/

/* Description : This program runs a few processes which send to each other

*/

/* signals using the kill command The signals activate the procedure

*/

/* sigCathcher which sends a signal to the next process Each child process */

/* exits after receiving the signal The parent collects all the zombies

*/

/* and then exits

*/

/

******************************************************************************

***/

#include <string.h>

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <sys/uio.h>

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <sys/fcntl.h>

int cpid[5]; //holds the pids of the childs

int j; //pointer to cpid

int sigCatcher(){ // function to activate when a signal is caught

printf("PID %d caught one\n",getpid());

signal(SIGINT,sigCatcher); // reset the signal catcher

if(j>-1)

kill(cpid[j],SIGINT); //send signal to next child in cpid

}

int main(){

int i;

int zombie;

int status;

int pid;

signal(SIGINT,sigCatcher); // set the signal catcher to sigCatcher

for(i=0;i<5;i++){

if((pid=fork())== 0){ // create new child

printf("PID %d ready\n",getpid());

j=i-1;

pause(); // wait for signal

exit(0); // end proccess (become a zombie)

}

else // Only father updates the cpid array

cpid[i]=pid;

Trang 9

}

sleep(2); // allow children time to enter pause

kill(cpid[4],SIGINT); // send signal to first child

sleep(2); // wait for children to become zombies

for(i=0;i<5;i++){

zombie = wait(&status); // collect zombies

printf("%d is dead\n",zombie);

}

exit(0);

}

Example 2 output:

PID 22899 ready

PID 22900 ready

PID 22901 ready

PID 22902 ready

PID 22903 ready

PID 22903 caught one

PID 22902 caught one

PID 22901 caught one

PID 22900 caught one

PID 22899 caught one

22903 is dead

22901 is dead

22902 is dead

22899 is dead

22900 is dead

execvp and Signal handlers

Since the process image is replaced when performing execvp, all functions

(including the signal handlers) will not exist in the new memory image Therefore execvp sets the signal handler of all signals back to default However, the bit that specifies if the signal should be ignore or not (if SIG_IGN was used) is not

changed, so signals which were ignored before the execvp will still be ignored after the execvp

Process Groups

Each process has an id (PID) and belongs to a group One of the processes in the group is the group leader, and all member’s group leader id (GID) are equal to his PID

int getpid() – return the process’s PID.

int getpgrp() – return the process’s GID.

setpgrp() – set this process’s GID to be equal to his PID.

setpgrp(int pid1, int pid2) – set process’s pid1 GID to be equal to pid2’s PID.

Trang 10

Example 3:

#include <stdio.h>

#include <sys/fcntl.h>

#include <sys/types.h>

#include <signal.h>

#include <unistd.h>

void cntl_c_handler(int dummy)

{

printf("\n%d caught SIGINT\n\n", getpid());

signal(SIGINT, cntl_c_handler);

}

main() {

int x; /* just to show that both will have it */

int pid; /* distinguish between father and son */

int pgrp, stat;

signal(SIGINT, cntl_c_handler);

x = 33;

pid = 1;

pid = fork();

if (pid == 0){ /* son */

signal(SIGINT, SIG_DFL); /* will not affect the father */

pgrp = getpgrp();

if(kill(pgrp, SIGCONT) == -1){ /* send to original father */

perror("problem with kill");

exit(1);

}

printf("Son - pid = %d group leader = %d father pid = %d\n", getpid(), getpgrp(), getppid());

setpgid(getpid(), getpid()); /* same as using setpgrp() */

printf("Son - pid = %d group leader = %d father pid = %d\n", getpid(), getpgrp(), getppid());

if(kill(-pgrp,SIGINT) == -1){ /* send to original fathers group */ perror("problem with kill");

exit(2);

}

printf("can I get here?\n"); /* i.e am I a member of the original fathers group? */

if(kill(-getpgrp(),SIGINT) == -1){

perror("problem with kill");

exit(3);

}

printf("can I get here now?\n");

}

else{

pause();

printf("Father - pid = %d group leader = %d \n", getpid(), getpgrp()); }

}

Trang 11

Example 3 output:

Son - pid = 22879 group leader = 22878 father pid = 22878

Son - pid = 22879 group leader = 22879 father pid = 22878

can I get here?

22878 caught SIGINT

Father - pid = 22878 group leader = 22878

Process Groups and Signals

Signals sent from the keyboard (like by ctrl-C) are actually sent to the current foreground process group This means that if the current process performed fork, and his sons did not move to another group, then the signal will be received by both father and sons

The shell is a process which manages the user

processes It receives the command from the user, forks and has his son exec the process requested Therefore the shell must contain some code that will make sure signals (like by ctrl-C) will only effect the current foreground process, and not the shell or any of the background processes.

References:

Signals

Ngày đăng: 04/12/2015, 00:54

🧩 Sản phẩm bạn có thể quan tâm

w