循序漸進學unix——上機記錄(五),signal
阿新 • • 發佈:2019-01-11
本文的主題是unix中的基本訊號處理(signal)。
在Unix中,一個程序可以向另一個程序傳送訊號,接收程序再收到訊號後可以根據配置做出相應反應。這也是一種通訊機制。
涉及到的主要函式有:
- signal,配置當前程序,在收到什麼訊號時執行什麼函式。第一個引數為想要處理的訊號,第二個引數為待執行函式名。
- kill,向指定pid的程序傳送一個指定訊號。(子程序的pid即父程序中fork的返回值)
- sigaction,使用signal定義的動作函式只能有一個int型引數,代表訊號值。如果想獲得更多資訊,可使用sigaction。它與signal功能類似,但是傳遞了更多資訊。(見後文示例程式碼)。
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<signal.h> #include<stdlib.h> void signalHandler(int signum) { printf("In signalHandler, PID=%d, signum=%d.\n", getpid(), signum); } void main(int argn, char** argv) { pid_t val_fork; int choice = -1; if(argn>1)choice = atoi(argv[1]); if((val_fork=fork())==0) { printf("In fils, before 'signal()'.\n"); if(choice==0) signal(SIGUSR1, SIG_DFL);//Invoke default action else if(choice==1) signal(SIGUSR1, SIG_IGN);//Ignore signals else signal(SIGUSR1, signalHandler);//Call user defined function. printf("In fils, after 'signal()'.\n"); printf("In fils, sleep for keeping alive\n"); sleep(5); printf("In fils, exit.\n"); exit(0); } else { printf("In pere, sleep for leaving enough time to my son.\n"); sleep(3); printf("In pere, before kill().\n"); kill(val_fork, SIGUSR1); if(choice==0) printf("In pere, OMG my son terminated himself!\n"); else if(choice==1) printf("In pere, OMG my son ignored my knife!\n"); else printf("In pere, I've just let my son execute a function!\n"); } }
2,嘗試在收到訊號後顯示傳送訊號程序的id。這裡我們就不得不使用sigaction了。請通過man檢視相關資料結構。
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<signal.h> #include<stdlib.h> void signalHandler(int signum, siginfo_t* siginfo, void* ucontent) { printf("In signalHandler, PID=%d, signum=%d, sending process=%d.\n", getpid(), signum, siginfo->si_pid); } void main(int argn, char** argv) { pid_t val_fork; int choice = -1; struct sigaction sa; sa.sa_sigaction=signalHandler; sa.sa_flags=SA_SIGINFO; if(argn>1)choice = atoi(argv[1]); if((val_fork=fork())==0) { printf("In fils, before 'signal()'.\n"); if(choice==0) signal(SIGUSR1, SIG_DFL);//Invoke default action else if(choice==1) signal(SIGUSR1, SIG_IGN);//Ignore signals else sigaction(SIGUSR1, &sa, NULL);//Call user defined function. printf("In fils, after 'signal()'.\n"); printf("In fils, sleep for keeping alive\n"); while(1)sleep(5); printf("In fils, exit.\n"); exit(0); } else { printf("In pere, pid=%d, sleep for leaving enough time to my son.\n", getpid()); sleep(3); printf("In pere, before kill().\n"); kill(val_fork, SIGUSR1); if(choice==0) printf("In pere, OMG my son terminated himself!\n"); else if(choice==1) printf("In pere, OMG my son ignored my knife!\n"); else printf("In pere, I've just let my son execute a function!\n"); } }
3,實現無阻塞通訊。程序B執行自己的任意任務。程序A向pipe中寫入資訊並通過signal通知B,B收到訊號後立即讀取pipe中的資訊。
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>
int tube[2];
void faire(int sig)
{
char buf[50];
read(tube[0], buf, 50);
printf("Fils reçois : %s. JE suis un devider parfaittttttttttttttttttttttttttttttttttttttttttttttttt\n", buf);
}
void main()
{
char *str = "msg from father to son";
pid_t val_fork;
pipe(tube);
if( (val_fork=fork())==0)
{
signal(SIGUSR1, faire);
while(1)
{
printf("Fils est en train de travailer\n");
sleep(1);
}
}
else
{
sleep(5);
write(tube[1], str, sizeof(str));
kill(val_fork, SIGUSR1);
wait(0);
}
}
4,稍微複雜些的練習:父程序從終端接受使用者輸入的兩個數字,將數字寫入pipe中並向子程序傳送訊號,子程序收到訊號後讀取數字並計求和,將結果通過pipe返回,父程序顯示結果並繼續等待使用者輸入。當父程序被ctrl+C終止時,父程序向子程序傳送訊號,子程序收到訊號後自行了斷。 需要了解的是,當我們在終端裡按下ctrl+C時相當於向終端中的所有程序傳送了訊號SIGINT,而所有程序的預設動作是結束自己。所以為了實現我們練習的要求,我們需要在父程序中改變對此訊號的處理方法,並且在子程序中忽略這一訊號(否則子程序會自動終止而不是等待父程序的終止命令)。
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
int pipe1[2];
int pipe2[2];
int var;
void sommef(int signum)
{
float val1, val2;
char buf[20]={0};
char *str;
int n = read(pipe1[0], buf, 20);
buf[n] = '\0';
printf("\tIn sommef, buf_read = %s\n",buf );
str = strtok(buf, "<>");//What's the warning here???
printf("\tIn sommef, val1 = %s\n",str );
val1 = atof(str);
str = strtok(NULL, "<>");
printf("\tIn sommef, val2 = %s\n",str );
val2 = atof(str);
sprintf( buf, "<%d><%f>\n", getpid(), val1+val2);
printf("\tIn sommef, buf_write = %s\n",buf );
write( pipe2[1], buf, sizeof(buf));
}
void suicide(int signum)
{
printf("Le fils est terminé.\n");
close(pipe1[0]);
close(pipe2[1]);
exit(0);
}
void killfils( int signum)
{
printf("Père reçois SIGINT, envoie SIGUSR2 pour terminer le fils %d.\n", var);
kill(SIGUSR2, var);
printf("Père termine sois même.\n");
close(pipe1[1]);
close(pipe2[0]);
exit(0);
}
void main(int argc, char** argv)
{
pid_t val_fork;
char buf[20];
if(argc!=3)
{
fprintf(stderr,"Usage: somme <valeur1> <valeur2>\n");
exit(0);
}
pipe(pipe1);
pipe(pipe2);
if( (val_fork=fork())==0 )
{
//fils
close(pipe1[1]);
close(pipe2[0]);
//忽略sigintsignal(SIGINT, SIG_IGN);//Le fils dois aussi traiter le SIGINT //求和操作signal(SIGUSR1, sommef); //自殺操作signal(SIGUSR2, suicide);while( 1){ sleep(3);}}else{var = val_fork;int n;float val1,val2;//Existe-il une meilleure façon?signal(SIGINT, killfils);close(pipe1[0]);close(pipe2[1]);sprintf(buf, "<%s><%s>", argv[1], argv[2]);write( pipe1[1], buf, sizeof(buf));//Laisser le fils finir sa configurationsleep(1);while(1){kill( val_fork, SIGUSR1);n=read( pipe2[0], buf, 20);buf[n] = '\0';printf("Résultat : %s\n", buf);printf("Nouveau calcul :\n");scanf("%f %f",&val1, &val2);sprintf(buf, "<%f><%f>", val1, val2);write( pipe1[1], buf, sizeof(buf));}}}