Linux API-信号:signal、sigaction、sigqueue

信号特征

  1. 信号本质——是软件层次上对中断的一种模拟
  2. 信号来源——
    (1)程序错误,如非法访问内存
    (2)外部信号,如按下了CTRL+C
    (3)通过kill或sigqueue向另外一个进程发送信号
  3. 信号没有优先级,所有的信号都是平等的
  4. 进程可对信号有不同的响应
  5. 通过kill -l可查看所有信号

信号API

  1. signal——捕捉信号并作出指定反应
  2. sigaction——接收信号函数
  3. sigqueue——发送信号函数

一、signal——捕捉信号并作出指定反应

表头文件
#include 
定义函数
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

函数说明——设置信号处理方式
signum——收到的指令
handler——执行的命令(执行名为handler的函数)
返回值——成功返回前一次调用signal函数时传入的函数指针,如果是第一次调用则返回SIG_DFL。出错返回SIG_ERR并设置errno

2.示例

#include 
#include 

void handler(int signum)
{
        printf("get signum: %d\n",signum);
        printf("never quit\n");
}

int main()
{

        signal(SIGINT,handler);

        while(1);
        return 0;
}

运行结果:但按下ctrl+c时无法终止进程
get signum: 2
never quit

二、sigaction——接收信号函数

表头文件
 #include 
定义函数
 int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

函数说明——检查或修改与指定信号相关联的处理动作(可同时两种操作)。当接收到signum信号,程序会进入act.sa_handler或act.sa_sigaction这个函数进行工作
signum——捕捉到的信号。产生信号函数必须和此信号相同才能正常响应信号
act——指定新的信号处理的数据
oldact —— 之前处理过的信息的处理的数据
返回值——成功返回0。失败返回-1,错误原因置于errno中

2.参数取值
const struct sigaction默认如下

struct sigaction {
           void (*sa_handler)(int);//信号处理程序接受1个参数:
           //void handler(int sig)

           void (*sa_sigaction)(int, siginfo_t *, void *);//信号处理程序接受3个参数:
           //void handler(int sig, siginfo_t *info, void *ucontext)

           sigset_t   sa_mask;
           int        sa_flags;//一般为SA_SIGINFO:信号处理程序接受3个参数
           void     (*sa_restorer)(void);
       };

在这其中siginfo_t 结构体默认如下

siginfo_t {
            int si_signo;/*信号数*/
            int si_errno;/*一个errno值*/
            int si_code;/*信号代码*/
            int si_trapno;/*触发的陷阱数
                                      来自硬件信号
                                     ( 未使用的大多数架构)*/
            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;/*定时器超时计数;POSIX。1 b定时器*/
            int si_timerid;/*定时器标识;POSIX.1b计时器*/
            void * si_addr;/*导致错误的内存位置*/
            long si_band;/* Band event (was int in
                                     glibc 2.3.2 and earlier) */
            int si_fd;/*文件描述符*/
            short si_addr_lsb;/*地址的最小有效位
                                     ( Linux 2.6.32版本后)*/
            void * si_lower;/*地址违规时的下界
                                      (Linux 3.19版本后)*/
            void * si_upper;/*地址违规的上界
                                      (Linux 3.19)*/
            int si_pkey;/* PTE上的保护键导致错误(Linux 4.6)*/
            void * si_call_addr;/*系统调用指令的地址
                                     (Linux 3.5)*/
            int si_syscall;/*系统呼叫试图数
                                     (Linux 3.5)*/
            unsigned int si_arch;/*系统调用尝试的体系结构(Linux 3.5)*/
}

3.示例

1个参数:
struct sigaction act;
act.sa_handler=handler;//接收到信号后程序进入handler函数
act.sa_flags=0;//信号处理程序接受1个参数
sigaction(SIGUSR1,&act,NULL);//有信号来进入handler函数
//void handler(int sig);

3个参数:
struct sigaction act;
act.sa_sigaction=handler;//接收到信号后程序进入handler函数
act.sa_flags=SA_SIGINFO;//信号处理程序接受3个参数
sigaction(SIGUSR1,&act,NULL);//有信号来进入handler函数
//void handler(int sig, siginfo_t *info, void *ucontext);

三、sigqueue——产生信号

表头文件
#include 
定义函数
int sigqueue(pid_t pid, int sig, const union sigval value);

函数说明——向指定进程产生一个指定信号
pid——接收信号程序的进程号
sig——传递的信号。必须和signum配置的信号相同sigaction才能正常响应此信号
value——传递的值,默认如下
返回值——成功返回0,表示信号成功地排队到接收端的过程。否错误返回-1,并设置errno来指示错误

//const union sigval结构体默认如下
union sigval {
           int   sival_int;
           void *sival_ptr;
       };

2.示例
最后信号例程演示

信号例程演示

接收到信号工作的程序1:

#include 
#include 
#include 
#include 

void handler(int sig, siginfo_t *info, void *ucontext)
{
	printf("sig=%d\n",sig);
	if(ucontext!=NULL)
	{
		printf("get data %d\n",info->si_int);
                printf("get data %d\n",info->si_value.sival_int);
//		printf("get data %p\n",(char *)(info->si_value.sival_ptr));
        	//接触到信号后,结构体里面的数据确实有ptr了,但是你只能输出这个地址,不能读,不能写,因为在这个进程中认为这个地址没有读写权限,直接读和写是违法操作
	        printf("from:%d\n",info->si_pid);
	}
}

int main()
{
	printf("getpid=%d\n",getpid());

	struct sigaction act;
	act.sa_sigaction=handler;//接收到信号后程序进入handler函数
	act.sa_flags=SA_SIGINFO;//信号处理程序接受3个参数

	sigaction(SIGUSR1,&act,NULL);//有信号会进行act相关函数进行工作,产生信号程序产生的信号**必须**为SIGUSR1(10)sigaction才能正确响应

	while(1);

	return 0;
}

产生信号程序2:

#include 
#include 
#include 
#include 

int main(int argc,char **argv)
{
	int pid=atoi(argv[1]);//响应进程ID号
	int sigunm=atoi(argv[2]);//给响应进程一个信号

	union sigval value;
        value.sival_int = 100;//响应程序接收到**正确**信号后可以读这个值
	
//	value.sival_ptr="HE";//字符串的首地址赋值给ptr

//	printf("%d,%p\n",value.sival_int,(char *)(value.sival_ptr));
	sigqueue(pid,sigunm,value);//产生一个信号
	
	printf("pid=%d\n",getpid());

	return 0;
}

运行结果:先运行程序1等待接受信号,再运行程序2产生信号
程序1:运行命令:./demo1
程序2:运行命令:./demo2 9585 10

程序2:输出信息:pid=9587
程序1:输出信息:
sig=10
get data 100
get data 100
from:9625

大家不妨试一试代码执行效果

你可能感兴趣的:(Linux,API进程间通信,linux,c语言)