iPC - Unix - 信号

1、signal - 捕获信号

signal 函数的使用方法简单,但并不属于 POSIX 标准,在各类 UNIX 平台上的实现不尽相同,因此其用途受到了一定的限制。

 
功能简单的信号处理函数(信号捕捉函数)
头文件#include <signal.h>
原型

typedef void (*sighandler_t)(int); //信号处理函数的 指针

 

sighandler_t signal(int signum,  sighandler_t handler);

参数

signum:要捕捉的信号

handler:处理方式

               1、SIG_IGN:忽略这个信号
               2、SIG_DFL:按照默认动作执行这个信号
               3、如果是一个函数,则是捕捉这个信号,收到这个信号的时候去执行这个函数。

                

返回值

成功:返回信号处理程序的先前值

出错:返回SIG_ERR,并设置errno

备注详细请查看man手册:man 2 signal (第7本也有)。可以配合kill()函数使用,kill()函数向某个进程发送某个指定的信号。

示例:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
 
void SIGINT_handle(int sig_num)
{
	printf("reveice signal %d \n", sig_num);
}
 
int main(int argc, char argv[])
{
	int i;
	signal(SIGINT, SIGINT_handle);
 
	for(i=0; i<10; i++)
	{
		printf("i=%d \n", i);
		sleep(1);
	}
	return 0;
}

2、kill - 发送信号

 
功能向一个进程发送信号
头文件#include <sys/types.h>
#include <signal.h>
原型int kill(pid_t pid,  int sig);
参数

(1)pid:目标进程ID  (process id)

  pid>0给指定进程 发送sig信号,pid=0给同组进程 发送sig信号,pid<-1给指定进程组 发送sig信号,pid=-1给所有进程 发送sig信号。

(2)sig:要发送的信号   

返回值

成功:返回0

失败:返回-1,并设置error。

备注详细请查看man手册:man 2 kill

示例:

发送方 kill.c

//发送方 kill.c

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
 
int main(int argc, char *argv[])
{
	int pid, ret;
	if(argc <2)
	{
		printf("arg error![./kill pid]\n");
		return -1;
	}
	
	sscanf( argv[1], "%d", &pid);
	ret = kill(pid, SIGUSR1);
	if(ret != 0 )
	{
		perror("kill SIGUSR1 error");
		return -1;
	}
	printf("send SIGUSR1 to [pid]%d successful!\n", pid);
 
	return 0;
}

接收方 signal.c

#include <stdio.h>
#include <unistd.h> //sleep()
#include <signal.h> 
#include <stdlib.h> //exit()
 
void SIGUSR1_handle(int sig_num)
{
	printf("reveice signal %d \n", sig_num);
	exit(0);
}
 
int main(int argc, char argv[])
{
	int i;
	signal(SIGUSR1, SIGUSR1_handle);
 
	for(i=0; i<30; i++)
	{
		printf("i=%d \n", i);
		sleep(1);
	}
	return 0;
}

3、sigprocmask - 阻塞信号

在阻塞过程中,信号不会丢失,会被挂起,等解开阻塞的时候再响应。意思就是在接收到信号的时候,对应的处理函数会暂停执行,当阻塞停止时,触发几次信号不会执行几次对应的处理函数,具体执行几次跟处理函数是否在执行有关!!这个切记

 
功能阻塞信号,延迟响应 。(检查并更改阻塞的信号)
头文件#include <signal.h>
原型int sigprocmask(int how,  const sigset_t  *set,  sigset_t *oldset);
参数

how:对信号的操作(应用于信号集合中的所有函数)

          SIG_BLOCK:添加信号集合里面的信号进行阻塞(原本的设置上添加设置)
          SIG_UNBLOCK:解除信号集合里面的信号的阻塞。
          SIG_SETMASK: 直接阻塞信号集合里面的信号,原本的设置直接被覆盖。

set:要设置的信号集合
            int sigemptyset(sigset_t *set);//清空信号集合
            int sigfillset(sigset_t *set);//将所有信号登记进集合里面
            int sigaddset(sigset_t *set, int signum);//往集合里面添加signum信号
            int sigdelset(sigset_t *set, int signum);// 往集合里面删除signum信号
            int sigismember(const sigset_t *set, int signum);//测试信号集合里面有无signum信号

注意:sigaddset()、sigdelset()、sigismember()等函数调用之前必须先调用sigemptyset()或者sigfillset()函数初始化信号集。

oldset:用来保存旧的信号集合设置
            如果这里是一个具体的指针,则将原本的设置保存在这里
            如果是NULL则不保存。

返回值

成功:返回0

失败:返回-1,并设置error

备注在阻塞状态时,非实时信号可能会丢失

例子:对中断信号、退出信号进行阻塞。然后向sigprocmask程序发送中断信号(Ctrl+c)、退出信号(Ctrl+\)。那么sigprocmask程序收到信号后,不会立即响应,直到解除阻塞,才进行响应。其中,发送了两次中断信号(姑且命名为中断A,中断B),但是只响应了一次,是因为: 在某个时刻或阻塞时 接收到多个同一种非实时信号时,只会响应一次,其余丢弃,如果有一个已经开始响应了但是还没有结束,然后又来一个,那么这一个也会响应

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
 
 
void signal_handle(int sig_num)
{
	printf("receive signal=%d\n", sig_num);
 
}
 
int main(void)
{
	int i;
	sigset_t sigset;
 
	signal(SIGINT, signal_handle);
	signal(SIGQUIT, signal_handle);
 
	sigemptyset(&sigset);//清空集合
 
	sigaddset(&sigset, SIGINT);//将中断信号添加到 阻塞集合中
	sigaddset(&sigset, SIGQUIT);//将退出信号添加到 阻塞集合中
 
	sigprocmask(SIG_BLOCK, &sigset, NULL);//对信号进行阻塞
 
	for(i=0; i<10; i++)
	{
		printf("i=%d \n", i);
		sleep(1);
	}
 
	
	sigprocmask(SIG_UNBLOCK, &sigset, NULL);//解开对信号的阻塞
 
	printf("hello\n");
 
	return 0;
}

运行结果:

4、sigaction - 带数据的信号处理

POSIX 标准定义的信号处理接口是 sigaction 函数

 
功能检查并改变信号动作(捕捉信号,指定处理动作)
头文件#include <signal.h>
原型int sigaction(int signum,  const struct sigaction *act,  struct sigaction *oldact);
参数

(1)signum:捕捉的信号

(2)act:信号动作结构体,用来登记对这个信号进行处理的动作。  

    struct sigaction 
    {
       void     (*sa_handler)(int);    //不带参数的信号处理函数,默认执行这个不带参数的信号处理函数
       void     (*sa_sigaction)(int, siginfo_t *, void *);//带参的信号处理函数,如果想要使能这个信号处理函数,需要设置一下sa_flags为SA_SIGINFO
       sigset_t   sa_mask;        //信号阻塞设置
                                             //1,在信号处理函数执行的过程当中阻塞掉指定的信号,指定的信号过来将会被挂起,等函数结束后再执行
                                             //2,sigset_t信号集合类型,参照sigprocmask中sigset的使用方式。
       int        sa_flags;        //信号的操作标识,例如设置使用的是带参信号处理函数,还是不带参数的
       void     (*sa_restorer)(void);    //被遗弃的设置
    };

(3)oldact:用来保存原来的设置,如果是NULL则不保存。

返回值

成功:返回0

失败:返回-1,并设置error

备注

siginfo_t 参数是一个如下定义的结构体:从中可以获取信号编号,进程ID等等信息

siginfo_t  {

int   si_signo; /*信号编号*/

int   si_errno; /*错误号*/

int   si_code; /*信号码*/

int   si_trapno;/*导致硬件产生信号的trap号*/

pid_t    si_pid; /*发送进程ID*/

uid_t    si_uid; /*发送进程真实的用户ID*/

int    si_status; /*退出值*/

clock_t    si_utime; /*花费的用户时间*/

clock_t    si_stime; /*花费的系统时间*/

sigval_t    si_value; /*信号值*/

int    si_int ; /*POSIX.1b 信号*/

void*   si_ptr; /*POSIX.1b 信号*/

int     si_overrun

int     si_timerid

void*   si_addr

long    si_band

int     si_fd;

short    si_addr_lsb; 

void*    si_lower;     /* Lower bound when address violation occurred (since Linux 3.19) */
         void*   si_upper;     /* Upper bound when address violation occurred (since Linux 3.19) */
         int      si_pkey;      /* Protection key on PTE that caused fault (since Linux 4.6) */
         void    *si_call_addr; /* Address of system call instruction (since Linux 3.5) */
         int      si_syscall;   /* Number of attempted system call (since Linux 3.5) */
        unsigned int   si_arch;  /* Architecture of attempted system call (since Linux 3.5) */

}

 

当 sa_flags 成员的值包含了 SA_SIGINFO 标志时,系统将使用 sa_sigaction 函数作为信号处理函数,否则使用 sa_handler 作为信号处理函数。

sa_flags 成员用于指定信号处理的行为,它可以是一下值的“按位或”组合。
 ◆ SA_RESTART:使被信号打断的系统调用自动重新发起。
 ◆ SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
 ◆ SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
 ◆ SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
 ◆ SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
 ◆ SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数

 

 

  

5、sigqueue - 带数据的信号发送

 
功能将信号和数据发送到指定进程
头文件#include <signal.h>
原型int sigqueue(pid_t pid,  int sig,  const union sigval value);
参数

(1)pid:目标进程ID

(2)sig:要发送的信号

(3)value:附加数据

           union sigval 
           {
                int   sival_int;//只附加一个int数据
                void *sival_ptr;//附加更多数据(附加一个地址) 
            };

         //注:这是共用体、两者取其一

返回值

成功:返回0

失败:返回-1,并设置error

备注跟sigaction结合使用

例子:sigqueue程序向sigaction程序发送SIGUSR1信号,并附带int型数据123,sigaction捕捉到信号后,打印出信号发送方的PID和附加的数据,然后退出。

接收方 sigaction.c 

include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
 
 
void sigaction_handle(int signum, siginfo_t *info, void * ucontext)
{
 
	printf("info.si_pid=%d\n", info->si_pid);//打印信号发送方的pid
	printf("info.si_int=%d\n", info->si_int);//打印信号发送方的附加数据
	exit(0);
 
}
 
int main(void)
{
	int i;
	struct sigaction act, oldact;
	
	act.sa_flags = SA_SIGINFO;//使用带参数的信号处理函数
	act.sa_sigaction = sigaction_handle;
 
    //sigemptyset(&act.sa_mask);//清空原来集合
	//sigfillset(&act.sa_mask);//将所有信号添加到集合
 
	sigaction(SIGUSR1, &act, &oldact);//捕捉sigqueue发送的SIGUSR1信号
 
	for(i=0; i<30; i++)
	{
		printf("i=%d\n", i);
		sleep(1);
	}
	return 0;
}

发送方  sigqueue.c 

#include <stdio.h>
#include <signal.h>
 
#include <sys/types.h>//getpid()
#include <unistd.h>
 
int main(int argc, const char *argv[])
{
	if(argc < 2)
	{
		printf("arg error\n");
		return -1;
	}
	union sigval value;
	value.sival_int = 123;
 
	int sig_num, pid;
	
	sscanf(argv[1], "%d", &pid);
	sigqueue(pid, SIGUSR1, value);//向指定的pid发送SIGUSR1信号
 
	printf("mypid=%d\n", getpid());//打印当前进程的ID号
 
	return 0;
}

结果 

6、注意事项

异步信号包含: 非实时信号 、实时信号。

实时信号一定会响应,非实时信息号不一定会响应(可能会被忽略,或丢失)。信号一般有以下设定:
 1,捕捉    (收到某个信号,做指定的动作,而不是做默认的)
 2,忽略    (收到某个信号,不做什么动作)
 3,阻塞    (收到某个信号,先做完当前事情,然后在响应信号)
4,按照默认动作

 注:SIGKILL,SIGSTOP不能被捕捉

 

其中SIGKILL,SIGSTOP只能按照系统默认动作执行,不能捕捉,阻塞及忽略

SIGPIPE(13) 管道破损,没有读端的管道写数据,就是那个brokenpipe。默认是杀进程的,所以网络编程中要处理这个信号

前面31个是非实时信号:
1,每一个非实时信号对应一个信号名
2,每一个非实时信号基本上都会对应一个系统事件
3,信号有可能会丢失

后面的31个是实时信号:
1,信号不会丢失
2,优先级比非实时信号要高

 

7、其他产生信号的函数

raise(sig)   函数:给当前进程发送sig信号。
abort()      函数:给当前进程发送SIGABRT信号。
alarm(sec)   函数:sec大于0时表示sec秒后给当前进程发送SIGALRM信号。sec=0表示取消定时器。每个进程只有唯一一个定时器。
setitimer()  函数:周期定时器,which可选:ITIMER_REAL(自然定时,产生SIGALRM信号)、ITIMER_VIRTUAL(虚拟用户空间计时,产生SIGVTALRM信号)、ITIMER_PROF(运行时计时,产生SIGPROF信号)

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值