进程间通信之信号

信号使用步骤:
1、在目的进程中安装信号,需要提供一个信号处理函数。
2、信号被某个进程产生。
3、操作系统响应信号,在目的进程中被注册。(信号在目的进程中被注册,操作系统将添加信号到目的进程的PCB的未决信号数据结构中)
4、信号在进程中注销,进程在执行信号处理函数之前,首先要把信号在进程中注销。

5、信号生命终止,保护上下文,进程捕获信号,即执行信号处理函数。

发送信号函数:
1、kill发送信号:
extern int kill(__pid_t __pid,int __sig)
2、raise()自举一个信号:
raise()函数用来给当前进程发送一个信号,即唤醒一个进程。
//come from  /usr/include/signal.h
/* Raise signal SIG,i.e.,send SIG to youself. */
extern int raise(int __sig)
此函数相当于采用一下方式执行kill()函数:
if(kill(getpid(),int __sig) == -1)
perror("raise");
3.alarm()/ualarm()函数产生定时信号SIGALRM。

信号安装:
signal()函数安装信号:
typedef void (*sighandler_t)(int);
//__sighandler_t是一种函数指针,这种指针指向的函数接受一个整型参数并返回一个无类型指针
sighandler_t signal(int signum, sighandler_t handler);
RETURN VALUE
       signal() returns the previous value of the signal handler, or SIG_ERR on error.

signal()函数用例:

#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<stdio.h>

void handler()
{
	printf("int:hello\n");	
}
int main()
{
	int i;
	signal(SIGALRM,handler);
	printf("%d\n",ualarm(500000,300000));//300000us即0.3s后系统自动产生SIGALRM信号,之后每0.5s产生SIGALRM信号。
	whlie(1)
	{
		sleep(1);
		printf("test\n");	
	}	
}

sigsction()函数安装信号:
extern int sigaction (int __sig,struct sigaction *__act,struct sigaction *__oact)

sigaction()函数第二个参数用来指定欲设置的信号处理方式,第三个参数将存储执行此函数前针对此信号的安装信息。对sigaction结构体,注意内核中的定义和应用程序中的定义是不一样的,内核空间的sigaction结构只支持函数类型为__sighandler_t的信号处理函数,不支持_sa_sigaction信号处理函数,不能处理信号传递的额外信息。

内核中sigaction定义:

struct sigaction
{
	void (*sa_handler)(int signo);
	sigset_t sa_mask;
	int sa_flags;
	void (*sa_restore)(void);
}

用户空间sigaction定义:

struct sigaction {
          union {
                  __sighandler_t _sa_handler;
                  void (*_sa_sigaction)(int, struct siginfo *, void *);
          } _u;
          sigset_t sa_mask;
          unsigned long sa_flags;	//设置用_sa_handler还是用_sa_sigaction作为处理函数,及其他行为标志。
          void (*sa_restorer)(void);
};

sa_handler或sa_sigaction标识了与指定信号相关联的操作,两者只取其一即可,如果选用sa_handler则信号捕获函数类型与signal函数的捕获函数一致,而后者有专门的用途,可以获取更多的信息。

1、以sa_handler作为处理函数的例子:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

void myHandler(int sig);

int main(int argc,char *argv[])
{
	struct sigaction act,oact;
	act.sa_handler = myHandler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;	
	sigaction(SIGUSR1,&act,&oact);
	
	while(1)
	{
		printf("Hello world.\n");
		pause();//pause()函数挂起当前进程,直到接收到一个信号后才重新恢复执行。	
	}	
}

void myHandler(int sig)
{
	printf("I got signal:%d.\n",sig);	
}
执行测试:
终端1:
book@book-desktop:~/workspace/zongde/chapter10$ ./a.out&
[2] 2101
book@book-desktop:~/workspace/zongde/chapter10$ Hello world.
终端2:
book@book-desktop:~$ kill -SIGUSR1 2020
终端1:
book@book-desktop:~/workspace/zongde/chapter10$ ./a.out&
[2] 2101
book@book-desktop:~/workspace/zongde/chapter10$ Hello world.
I got signal:10.

Hello world.

2、以sa_sigaction作为处理函数的例子:

当sigation()函数的第二个参数 struct sigaction *__act 的sa_flags成员设置了SA_SIGINFO时,sigaction()函数则是通过第二个参数 struct sigaction *__act的sa_sigaction成员来设置处理函数,而不是通过sa_handler来设置处理函数。
再看一下sigaction()函数:
int sigaction (int __sig,struct sigaction *__act,struct sigaction *__oact)

第二个参数的sa_sigaction成员:
act.sa_sigaction:
void (*_sa_sigaction)(int, struct siginfo *, void *);

其参数中的siginfo结构:

siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               int      si_trapno;   /* Trap number that caused
                                        hardware-generated signal
                                        (unused on most architectures) */
               pid_t    si_pid;      /* Sending process ID */
               uid_t    si_uid;      /* Real user ID of sending process */
               int      si_status;   /* Exit value or signal */
               clock_t  si_utime;    /* User time consumed */
               clock_t  si_stime;    /* System time consumed */
               sigval_t si_value;    /* Signal value */
               int      si_int;      /* POSIX.1b signal */
               void    *si_ptr;      /* POSIX.1b signal */
               int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
               int      si_timerid;  /* Timer ID; POSIX.1b timers */
               void    *si_addr;     /* Memory location which caused fault */
               int      si_band;     /* Band event */
               int      si_fd;       /* File descriptor */
}

使用sa_sigaction()作为处理函数的示例代码:

#include<stdio.h>
#include<stdlib.h>
#include<signa.h>
#include<unistd.h>

void func(int signo,siginfo_t *info,void *p)
{
	printf("signo = %d\n",signo);
	printf("sender pid =%d\n",info->si_pid);	
}

int main(int argc,char *argv[])
{
	struct sigaction act,oact;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	act.sa_sigaction = func;
	sigaction(SIGUSR1,&act,&oact);
	
	while(1)
	{
		printf("pid is %d Hello world.\n",getpid());
		pause();	
	}
}

执行测试:

终端1:
book@book-desktop:~/workspace/zongde/chapter10$ ./a.out
pid is 2177 Hello world.
终端2:
book@book-desktop:~$ kill -SIGUSR1 2177
终端1:
book@book-desktop:~/workspace/zongde/chapter10$ ./a.out
pid is 2177 Hello world.
signo = 10
sender pid =2078
pid is 2177 Hello world.

信号集操作:
1、设置进程屏蔽信号集
函数sigprocmask()用来设置当前进程屏蔽的信号集合,此函数声明如下:
/* Get and/or change the set of blocked signals. */
extern int sigprocmask (int __how,__const sigset_t *__restrict __set,sigset_t *__restrict __oset)
第一个参数为更改该集合的方式(添加、删除、屏蔽):
#define SIG_BLOCK 0               //for blocking signals
#define SIGPUNBLOCK 1         //for unblocking siganls
#define SIG_SETMASK 2          //for setting teh signal mask
         //SIG_BLOCK:将第二个参数所描述的集合添加到当前进程屏蔽的信号集中
         //SIG_UNBLOCK:将第二个参数所描述得集合从当前进程屏蔽的信号集中删除
         //SIG_SETMASK:无论之前屏蔽了那些信号,设置当前进程屏蔽的集合为第二个参数描述的对象。
如果set是空指针,则参数how的值没有任何意义,且不会更改进程屏蔽信号集合,因此该调用课用于查询当前屏蔽的信号集合。
第三个参数为当前进程屏蔽的信号集合。
系统强制不能屏蔽无法忽略的SIGKILL和SIGSTOP信号。
2、获取当前未决的信号集
未决信号包括没来得及捕获和因为进程暂时屏蔽了这个信号而导致的未决。
sigpending()函数返回当前进程所有未决的集合,声明:
/* put in set all signals that are blocked and waiting to be delivered.*/
extern int sigpending (sigset_t *__set);
成功返回0将未决信号添加到set中。
3、信号集操作函数:
1):清空信号集。extern int sigemptyset(sigset_t *__set)
2):填充所有信号到信号集。extern int sigfillset(sigset_t *__set)
3):添加信号到信号集中,extern int sigaddset (sigset_t *__set,int __signo).
4):从信号集中删除某个信号。extern int sigdelset(sigset_t *set,int __signo)
5):检测是否存在信号集中。extern int sigismember(__const sigset_t *__set,int __signo)
6):检测是否为空:extern int sigisemptyset(__const sigset_t *__set)
7):逻辑与方式信号集合并(重叠的部分留下):
extern int sigandset (sigset_t *__set,__const sigset_t*__lest,__const sigset_t *__right)
8):逻辑或方式合并(集合相加):
extern int sigorset (sigset_t *__set,__const sigset_t *__left,__const sigset_t *__right)

等待信号:
pause()函数:
pause()函数将使当前进程处于等待状态,直到当前进程屏蔽信号外的任意一个信号出现。
extern int pasue(void);
此函数将挂起当前进程,直到接收到一个信号后才重新恢复执行,其始终返回-1并设置error变量为EINTR。
sigsuspend()函数:(临时更改屏蔽集)
extern int sigsuspend(__const sigset_t *__set);
sigsuspend()函数将当前进程屏蔽的信号集合替换为其参数所指定的信号集合,直到收到一个非指定集合中的信号后继续执行,即在等待的过程中原来的进程屏蔽集合中的信号(不在其参数集合中)是可以递迭到当前进程的,然后阻塞该进程,直到一个非指定集合中的信号到来为止。当收到某个信号后,进程屏蔽的信号将自动恢复原来的屏蔽集合。如果某信号到来,且安装了该信号的捕获函数,则sigsuspend()将在信号捕获函数返回后返回。

猜你喜欢

转载自blog.csdn.net/qq_22863733/article/details/80346276