循序漸進學unix——上機記錄(五),signal

本文的主題是unix中的基本信號處理(signal)。

在Unix中,一個進程可以向另一個進程發送信號,接收進程再收到信號後可以根據配置做出相應反應。這也是一種通信機制。

涉及到的主要函數有:

  • signal,配置當前進程,在收到什麼信號時執行什麼函數。第一個參數爲想要處理的信號,第二個參數爲待執行函數名。
  • kill,向指定pid的進程發送一個指定信號。(子進程的pid即父進程中fork的返回值)
  • sigaction,使用signal定義的動作函數只能有一個int型參數,代表信號值。如果想獲得更多信息,可使用sigaction。它與signal功能類似,但是傳遞了更多信息。(見後文示例代碼)。
1,Basic use。可通過在命令行提供參數來選擇對信號的處理:默認,忽略,或執行某一函數。
#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));}}}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章