适合于驱动文件主动向应用程序发出通知
-
路径: arch/xtensa/include/uapi/asm/signal.h ,该文件定义了Linux所支持的所有信号
-
信号值:
#define SIGHUP 1 //终端挂起或控制进程终止 #define SIGINT 2 //终端中断(Ctrl+C组合键) #define SIGQUIT 3 //终端退出(Ctrl+\组合键) #define SIGILL 4 //非法指令 #define SIGTRAP 5 /* debug 使用,有断点指令产生 */ #define SIGABRT 6 /* 由 abort(3)发出的退出指令 */ #define SIGIOT 6 /* IOT 指令 */ #define SIGBUS 7 /* 总线错误 */ #define SIGFPE 8 /* 浮点运算错误 */ #define SIGKILL 9 /* 杀死、终止进程 */ #define SIGUSR1 10 /* 用户自定义信号 1 */ #define SIGSEGV 11 /* 段违例(无效的内存段) */ #define SIGUSR2 12 /* 用户自定义信号 2 */ #define SIGPIPE 13 /* 向非读管道写入数据 */ #define SIGALRM 14 /* 闹钟 */ #define SIGTERM 15 /* 软件终止 */ #define SIGSTKFLT 16 /* 栈异常 */ #define SIGCHLD 17 /* 子进程结束 */ #define SIGCONT 18 /* 进程继续 */ #define SIGSTOP 19 /* 停止进程的执行,只是暂停 */ #define SIGTSTP 20 /* 停止进程的运行(Ctrl+Z 组合键) */ #define SIGTTIN 21 /* 后台进程需要从终端读取数据 */ #define SIGTTOU 22 /* 后台进程需要向终端写数据 */ #define SIGURG 23 /* 有"紧急"数据 */ #define SIGXCPU 24 /* 超过 CPU 资源限制 */ #define SIGXFSZ 25 /* 文件大小超额 */ #define SIGVTALRM 26 /* 虚拟时钟信号 */ #define SIGPROF 27 /* 时钟信号描述 */ #define SIGWINCH 28 /* 窗口大小改变 */ #define SIGIO 29 /* 可以进行输入/输出操作 */ #define SIGPOLL SIGIO /* #define SIGLOS 29 */ #define SIGPWR 30 /* 断点重启 */ #define SIGSYS 31 /* 非法的系统调用 */ #define SIGUNUSED 31 /* 未使用信号 */
在这些信号中,除了 SIGKILL(9) 和 SIGSTOP(19) 这两个信号不能被忽略之外,其他信号都可以忽略。这些信号就相当于中断号,不同的中断号代表不同的中断。不同的中断所做的处理不同,因此,驱动程序可以通过向应用程序发送不同的信号来实现不同的功能。
-
API
-
APP应用层
- 设置信号回调处理函数
typedef void(*sighandler_t)(int sig); sighandler_t signal(int signum, sighandler_t handler) signum: 要设置处理函数的信号 handler: 信号处理函数 返回值: 成功,返回信号的前一个处理函数 失败, SIG_ERR typedef void (*sighandler_t)(int)
- 文件描述符控制接口 fcntl
函数原型: int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock); fcntl函数有5种功能: 1.复制一个现有的描述符 (cmd=F_DUPFD).
2.获得/设置文件描述符标记 (cmd=F_GETFD或F_SETFD). 3.获得/设置文件状态标记 (cmd=F_GETFL或F_SETFL). 4.获得/设置异步I/O所有权 (cmd=F_GETOWN或F_SETOWN). 5.获得/设置记录锁 (cmd=F_GETLK,F_SETLK或F_SETLKW). ```
-
驱动层
- 数据结构
struct fasync_struct { spinlock_t fa_lock; int magic; int fa_fd; struct fasync_struct *fa_next; struct file *fa_file; struct rcu_head fa_rcu; };
驱动层如果要使用异步通知,则需在设备驱动中实现 file_operations 文件操作集合中的 fasync 函数
int (*fasync) (int fd, struct file *filp, int on) //该函数接口用于实现 指定app层进程,即 该驱动的信号发送到哪个进程
- 初始化: 在驱动层定义(实现)该函数时,一般通过调用 fasync_helper 函数来 初始化 驱动层的 fasync_struct 结构体 fasync_helper 原型
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) 传参的前3个参数就是 fasync 函数传参的那3个参数。第4个参数,就是要初始化的 fasync_struct 结构 补充:功能是向谁(哪个进程)发
- 发送
void kill_fasync(struct fasync_struct **fp, int sig, int band) fp: 要操作的 fasync_struct 数据结构 sig: 要发送的信号。 band: 可读时设置为 POLL_IN,可写时设置为 POLL_OUT。 补充:功能是向应用程序发送可读信号
-
场景 当应用程序通过 fcntl(fd, F_SETFL, flags | FASYNC) 来改变 fasync 标记是,驱动的 fasync 函数就会执行
- 驱动demo
static struct fasync_struct * fasync_queque ; static int dev_fasync(int fd, struct file *filp, int on) { struct fasync_struct *p = (struct fasync_struct)filp->private_data; if(fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)) //初始化 { return -EIO ; } return 0 ; //或者 return fasync_helper(fd, filp, on, struct fasync_struct **fapp); } static struct file_operations xxx_ops = { ...... .fasync = dev_fasync, ...... }; //中断回调 void xx_irp_cd() { kill_fasync(&fasync_queque,SIGIO ,POLL_IN); } 假如在 dev_fasync 申请了一些资源,要在 xxx_ops.release 释放掉。初始化的信号不用释放 。 定义的 fasync_struct 数据结构在驱动中,并没有真实成员赋值,都是API的调用
- app demo
- 流程(3次使用的 fcntl )
- 注册信号处理函数
sighandler_t signal(int signum, sighandler_t handler)
- 将本应用程序的进程号告诉给内核
fcntl(fd, F_SETOWN, getpid())
- 开启异步通知
flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */ fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */
- 流程(3次使用的 fcntl )