一旦设备就绪,则主动通知应用程序
,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念
,比较准确的称谓是“信号驱动的异步I/O
”。信号是在软件层次上对中断机制的一种模拟
,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。信号
与处理器收到一个中断请求
可以说是一样的(软中断)。异步的
,一个进程不必通过任何操作来等待信号的到达。内核定义的
,当相应的事件产生时,内核发射
相应的信号。#include
typedef void (*__sighandler_t) (int);
__sighandler_t signal (int __sig, __sighandler_t __handler);
要捕捉的信号
(linux下查看信号:kill -l
,9号SIGKILL信号不能被捕捉 )进程可以通过3种方式
来响应一个信号
SIGKLL-9
和SIGSTOP-19
。接收到了,但是什么都不做。#include
#include
int main()
{
// 忽略SIGINT信号
signal(SIGINT, SIG_IGN);
while(1)
{
printf("hello\n");
sleep(1);
}
return 0;
}
#include
#include
int main()
{
//SIGINT默认操作
signal(SIGINT, SIG_DFL);
while(1)
{
printf("hello\n");
sleep(1);
}
return 0;
}
#include
#include
//typedef void (*sighandler_t)(int);//定义sighandler_t为void *(int)无返回值的函数指针类型
//sighandler_t signal(int signum, sighandler_t handler);//第二个参数是函数指针类型,目的是传入函数地址。函数名相当于指针,存放着指向函数空间的地址。
void handler(int signum)
{
printf("signum is:%d\n",signum);
printf("don't quit\n");
}
int main()
{
signal(SIGINT,handler);//信号处理函数的注册,当Ctrl+C信号出现,内核调用handler函数处理 ,也可以用宏(SIG_IGN)忽略
while(1);
return 0;
}
#include
#include
#include
#include
#include
void handler_sign(int sign)
{
printf("catch a signal:%d\n", sign);
}
int main()
{
//SIGINT定义信号处理函数handler_sign
signal(SIGINT, handler_sign);
while(1)
{
printf("hello\n");
sleep(1);
}
return 0;
}
#include
#include
#include
int main(int argc,int **argv[])
{
int signum;
int pid;
char cmd[128] ={0};
signum = atoi(argv[1]);//字符转换成整形
pid = atoi(argv[2]);
printf("signum:%d,pid:%d\n",signum,pid);
/*发送指令*/
/*方法一*/
/*
kill(pid,signum);//执行命令
printf("send kill\n");
*/
/*方法二*/
sprintf(cmd,"kill -%d %d",signum,pid);//构建字符串
system(cmd);//创建子进程执行
printf("system work\n");
return 0;
}
信号安装函数sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)
的第二个参数是一个指向sigaction
结构的指针(结构体名称与函数名一样,千万别弄混淆了
)。在结构sigaction
的实例中,指定了对特定信号的处理,信号所传递的信息,信号处理函数执行过程中应屏蔽掉哪些函数等。当然,此指针也可以为NULL,进程会以默认方式处理信号。以下就简单介绍一下sigaction结构以及一般的用法。
对于内核头文件
而言,struct sigaction 结构体
定义在kernel/include/asm/signal.h
,此头文件又被kernel/include/linux/signal.h
包含。
对于用户空间的头文件
而言,struct sigaction
定义在 /usr/include/bits/sigaction.h
,此头文件又被/usr/include/signal.h
包含,所以应用程序中如果用到此结构,只要#include
即可。注意内核中的定义和应用程序中的定义是不一样的
,内核空间的sigaction结构只支持函数类型为__sighandler_t的信号处理函数,不能处理信号传递的额外信息
。具体定义如下:
struct sigaction
{
__sighandler_t sa_handler;
unsigned long sa_flags;
void (*sa_restorer)(void);
sigset_t sa_mask; /* mask last for extensibility */
};
sa_handler
的原型是一个参数为int
,返回类型为void
的函数指针。参数即为信号值
,所以信号不能传递除信号值之外的任何信息
;
sa_sigaction
的原型是一个带三个参数
,类型分别为int
,struct siginfo *
,void *
,返回类型为void
的函数指针。第一个参数为信号值
;第二个参数是一个指向struct siginfo结构的指针
,此结构中包含信号携带的数据值,第三个参数没有使用。
sa_mask
指定在信号处理程序执行过程中,哪些信号应当被阻塞。默认当前信号本身被阻塞。
sa_flags
包含了许多标志位,比较重要的一个是SA_SIGINFO
,当设定了该标志位时,表示信号附带的参数可以传递到信号处理函数中。即使sa_sigaction指定信号处理函数,如果不设置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误。
#include
void sig_handler_with_arg(int sig,siginfo_t *sig_info,void *unused){……}
int main(int argc,char **argv)
{
struct sigaction sig_act;
……
sigemptyset(&sig_act.sa_mask);
sig_act.sa_sigaction=sig_handler_with_arg;
sig_act.sa_flags=SA_SIGINFO;
……
}
#include
void sig_handler(int sig){……}
int main(int argc,char **argv)
{
struct sigaction sig_act;
……
sigemptyset(&sig_act.sa_mask);
sig_act.sa_handler=sig_handler;
sig_act.sa_flags=0;
……
}
携带信息
,发信号用sigqueque()
,注册信号函数用sigaction()
。int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction
{
void (*sa_handler)(int); //信号处理程序
void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
sigset_t sa_mask;//阻塞关键字的信号集,在调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
int sa_flags;//SA_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 */
}
发送信号,并携带信息
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
int sival_int;
void *sival_ptr;
};
例程
/*信号处理函数注册*/
#include
#include
//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
/* struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);//与上一个2选1,此可携带信息
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};*/
void handle(int signum, siginfo_t *info, void *content)
{
printf("signum:%d\n",signum);
if(content != NULL)
{
printf("si_pid:%d\n",info->si_pid);
printf("si_int:%d\n",info->si_int);
printf("sival_int: %d\n",info->si_value.sival_int);//读取结构体里面的由sigqueue函数发送的联>
合体si_value里面的数据
}
}
int main()
{
printf("pid:%d\n",getpid());
struct sigaction act;
act.sa_sigaction = handle;//绑定操作函数
act.sa_flags = SA_SIGINFO;//表示能接受数据
sigaction(SIGUSR1, &act, NULL);//注册信号函数,接受该信号后采取结构体act里面的操作,第三个参数做数据备
份,不用则NULL
while(1);
return 0;
}
/*信号发送*/
#include
#include
//int sigqueue(pid_t pid, int sig, const union sigval value);
/* union sigval {
int sival_int;
void *sival_ptr;
};*/
int main(int argc,int **argv)
{
int pid;
int signum;
signum = atoi(argv[1]);
pid = atoi(argv[2]);
union sigval value;
value.sival_int = 10;
sigqueue(pid, signum, value);
printf("pid:%d,done\n",getpid());
return 0;
}
前31
个为传统UNIX支持的信号
,是不可靠信号(非实时的)
,后33
个是后来扩充的
,是可靠信号(实时信号)
。不可靠信号和可靠信号的区别在于前者不支持排队
,可能会造成信号丢失
,而后者不会。信号id | 名称 | 含义 | 备注 |
---|---|---|---|
1 | SIGHUP | 终端关闭会发送 | 进程结束 |
2 | SIGINT | ctrl + c 终止当前进程 | 进程结束 |
3 | SIGQUIT | ctrl + \ 停止当前进程 | 进程结束 |
9 | SIGKILL | 杀死进程 | 进程结束 |
12 | SIGUSR2 | 用户自定信号 | |
14 | SIGALRM | 闹钟信号 | |
19 | SIGSTOP | 进程暂停 | |
20 | SIGTSTP | ctrl+z 挂起进程 | 转换成后台进程 |
信号id | 名称 | 含义 | 备注 |
---|---|---|---|
1 | SIGHUP | 本信号在用户终端连接( 正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一会话控制内各个作业, 这时它们与控制终端不再关联。用户,系统会分配给登录用户一个Session(终端)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session(终端)。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号 |
|
2 | SIGINT | 程序终止(interrupt)、中断信号, 在用户键入INTR字符,通常命令行Ctrl C 时发出,用于通知前台进程组终止进程。 |
|
3 | SIGQUIT | SIGQUIT是其控制终端发送到进程,当用户请求的过程中执行核心转储的信号。但由QUIT字符(通常命令行Ctrl \)来控制 ,进程在因收到SIGQUIT退出时会产生core文件 ,在这个意义上类似于一个程序错误信号。 |
|
4 | SIGILL | 执行了非法指令 , 通常是因为可执行文件本身出现错误 ,或者试图执行数据段, 堆栈溢出 时也有可能产生这个信号。 |
|
5 | SIGTRAP | 由断点指令或其它trap指令 产生,由debugger使用 |
|
6 | SIGABRT | 调用abort函数 生成的信号,程序异常结束,进程终止并产生core文件 |
|
7 | SIGBUS | 非法地址 ,包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数,但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或 |
|
8 | SIGFPE | 出现了致命的算术运算错误 时发出,不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。 |
|
9 | SIGKILL | 用来立即结束程序的运行,本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。 | |
10 | SIGUSR1 | 用户自定义信号 ,终止进程 |
|
11 | SIGSEGV | 试图访问未分配 给自己的内存, 或试图往没有写权限的内存地址写数据. |
|
12 | SIGUSR2 | 用户自定义信号 ,终止进程 |
|
13 | SIGPIPE | 管道破裂 。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开 或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。 |
|
14 | SIGALRM | 有setitimer产生时钟定时信号,计算的是实际的时间或时钟时间, alarm函数使用该信号 。 |
|
15 | SIGTERM | 程序结束(terminate)信号 ,与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,才会用SIGKILL。 |
|
16 | SIGSTKFLT | 协处理器堆栈错误信号 | |
17 | SIGCHLD | 子进程结束 时, 父进程会收到这个信号 ,如果父进程没有处理这个信号 ,也没有等待(wait)子进程 ,子进程 虽然终止,但是还会在内核进程表中占有表项 ,这时的子进程称为僵尸进程 。这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)。 |
|
18 | SIGCONT | 让一个停止(stopped)的进程继续执行, 本信号不能被阻塞,可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符 | |
19 | SIGSTOP | 提供给管理员暂停进程的特权, 所以不能忽略和重定义。停止(stopped)进程的执行, 注意它和terminate以及interrupt的区别:该进程还未结束,只是暂停执行,本信号不能被阻塞, 处理或忽略。 | |
20 | SIGTSTP | 停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z),向前台进程组发送SIGTSTP信号以暂停进程(默认动作), 该信号可以被忽略和重定义。 | |
21 | SIGTTIN | 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号,缺省时这些进程会停止执行。 | |
22 | SIGTTOU | 类似于SIGTTIN, 但在写终端(或修改终端模式)时收到。 | |
23 | SIGURG | 有"紧急"数据或out-of-band数据到达socket时产生。 | |
24 | SIGXCPU | 超过CPU时间资源限制,这个限制可以由getrlimit/setrlimit来读取/改变。 | |
25 | SIGXFSZ | 当进程企图扩大文件以至于超过文件大小资源限制。 | |
26 | SIGVTALRM | 虚拟时钟信号, 类似于SIGALRM, 但是计算的是该进程占用的CPU时间。 | |
27 | SIGPROF | 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间。 | |
28 | SIGWINCH | 窗口大小改变时发出。 | |
29 | SIGIO | 文件描述符准备就绪,可以开始进行输入/输出操作。 | |
30 | SIGPWR | 电源失效/再启动信号 | |
31 | SIGSYS | 非法的系统调用。 | |
34 | SIGRTMIN | ||
35 | SIGRTMIN+1 | ||
36 | SIGRTMIN+2 | ||
37 | SIGRTMIN+3 | ||
38 | SIGRTMIN+4 | ||
39 | SIGRTMIN+5 | ||
40 | SIGRTMIN+6 | ||
41 | SIGRTMIN+7 | ||
42 | SIGRTMIN+8 | ||
43 | SIGRTMIN+9 | ||
44 | SIGRTMIN+10 | ||
45 | SIGRTMIN+11 | ||
46 | SIGRTMIN+12 | ||
47 | SIGRTMIN+13 | ||
48 | SIGRTMIN+14 | ||
49 | SIGRTMIN+15 | ||
50 | SIGRTMAX-14 | ||
51 | SIGRTMAX-13 | ||
52 | SIGRTMAX-12 | ||
53 | SIGRTMAX-11 | ||
54 | SIGRTMAX-10 | ||
55 | SIGRTMAX-9 | ||
56 | SIGRTMAX-8 | ||
57 | SIGRTMAX-7 | ||
58 | SIGRTMAX-6 | ||
59 | SIGRTMAX-5 | ||
60 | SIGRTMAX-4 | ||
61 | SIGRTMAX-3 | ||
62 | SIGRTMAX-2 | ||
63 | SIGRTMAX-1 | ||
64 | SIGRTMAX |
/*
* @Author: fliu
*/
#include
#include
#include
#include
#define FRAME_SIZE ((648*360*2 + 1024)*5)
#define DEVICE_NAME "/dev/pl_irq_drv"
void init_env(int fd, __sighandler_t handler, pid_t pid);
/*
* @Author: fliu
*/
#include "pl2ps_intr.h"
void init_env(int fd, __sighandler_t handler, pid_t pid)
{
int flags = 0;
//https://blog.csdn.net/xiadeliang1111/article/details/84348006
//https://blog.csdn.net/qq_41936794/article/details/105273059
// 实现异步通知机制,用户程序涉及2项工作,指定一个进程作为文件的“属主(owner)”.
// 当应用程序使用fcntl系统调用执行F_SETOWN命令时,属主进程的进程ID号就被保存在filp->f_owner中.
// 这一步是必需的,目的是告诉内核将信号发给谁,也就是发给哪个进程.
// 然后为了真正启动异步通知机制,用户程序还必须在设备中设置FASYNC标志,这通过fcntl的F_SETFL命令完成的,
// 文件打开时,FASYNC标志被默认为是清除的.,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。
// 执行完这两个步骤之后,内核就可以在新数据到达时通过调用kill_fasync()函数请求发送一个SIGIO信号给应用层,
// 该信号被发送到存放在filp->f_owner中的进程(如果是负值就是进程组)
signal(SIGIO, handler);
fcntl(fd, F_SETOWN, pid);
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);
}
/*
* @Author: your name
*/
#include "pl2ps_intr.h"
static int fd = 0;
static void sigio_signal_func(int signum)
{
int err = 0;
char buf[FRAME_SIZE + 1];
FILE* fp = NULL;
printf("-------------in void sigio_signal_func. \n");
#if 1
err = read(fd, buf, FRAME_SIZE);
if(err < 0) {
printf("read device error! \n");
} else {
printf("sigio signal! \n");
}
#endif
}
int main(int argc, char *argv[])
{
int flags = 0;
char *filename;
if (argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\n", filename);
return -1;
}
init_env(fd, sigio_signal_func, getpid());
while(1) {
sleep(2);
}
close(fd);
return 0;
}