进程之间发送信号
这里我们使用 kill 命令,在进程与进程之间进行传输信号。
比如杀死一个进程也是通过进程之间发送信号:
#kill -9 pid
相对应的如果要发送信号,则要注册信号处理函数(和注册中断服务函数类似),并且编写一个信号服务函数,然后写一个一直休眠的函数就ok了。应用程序 signal.c 代码如下:
#include #include void mysignal_fun(int signum){ static int count = 0; printf("signal = %d, %d timesn", signum, count++); } int main(int argc, char **argv){ signal(SIGUSR1, mysignal_fun);// input: "kill -10(-USR1) pid" while(1) sleep(1000); return 0; } 通过“&”在后台运行之后,利用进程kill对后台运行的signal进行传递信号。 #ps //获得PID #kill -USR1 signal_pid //从ps中获得的pid 从以上可以看出在这种方法中还得手动获取pid号,在后续的实验中会使用函数获取pid号。 运行结果为: # kill -USR1 752 signal = 10, 0 times # kill -USR1 752 signal = 10, 1 times # kill -10 752 signal = 10, 2 times # kill -USR1 752 signal = 10, 3 times # kill -10 752 signal = 10, 4 times 驱动向应用程序发送信号 要点: 注册信号函数 使用signal 函数在应用程序中国注册信号处理函数 谁发送信号函数 从驱动中发送 这个信号发送给哪个函数 发送给app app要告诉驱动pid 怎么发kill_fasyn 函数发送 按下按键时驱动程序通知应用程序 驱动向应用程序发送信号的方法和中断类似,有以下四块问题: 注册信号函数 初始化信号函数 由驱动发送信号函数 这个信号由驱动发送给应用程序,即应用程序应该告诉信号进程的PID号 step1: 注册信号处理函数 这一步是通过signal()函数进行对信号处理函数进行初始化,其功能为设置某一信号的对应动作。对signal函数的声明如下: #include typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t sig_handler); signum的取值如下表所示: handler描述了与信号关联的动作,它可以取以下三种值: SIG_IGN 这个符号表示忽略该信号。例如: #include #include int main(int argc, char *argv[]){ signal(SIGINT, SIG_IGN); while(1) sleep(1000); return 0; } SIGINT信号代表由InterruptKey产生,通常是CTRL +C 或者是DELETE 。执行上述代码时,按下CTRL + C程序没有反应。这就对了,如果我们想结束该程序可以按下CTRL +来结束,当我们按下CTRL +组合键时,产生了SIGQUIT信号,此信号并没有被忽略。 SIG_DFL 这个符号表示恢复对信号的系统默认处理。不写此处理函数默认也是执行系统默认操作。例如: #include #include int main(int argc, char *argv[]){ signal(SIGINT, SIG_DFL); while(1) sleep(1000); return 0; } 这时就可以按下CTRL +C 来终止该进程了。把signal(SIGINT, SIG_DFL);这句去掉,效果是一样的 sighandler_t类型的函数指针,即我们的信号处理函数。照着上两个例子: #include #include typedef void (*signal_handler)(int); void signal_handler_fun(int signum) { printf("catch signal %dn", signum); } int main(int argc, char *argv[]) { signal(SIGINT, signal_hander_fun); while(1) sleep(1000); return 0; } 这样我们就能在信号来的时候执行特定的函数。次程序在输入ctrl+c时会执行信号处理函数,输出: catch signal 2 由于使用的是异步通信,所以信号选择SIGIO,并且在信号服务函数中进行read操作(虽然我的按键值的打印在中断函数中完成了,这里就打印一个read走走过场): int fd; void my_fasyn_signal_fun(int signum){ unsigned char key_val; read(fd, &key_val, 1); printf("read!n"); } int main(int argc, char **argv) { signal(SIGIO, my_fasyn_signal_fun); fd = open("/dev/buttons", O_RDWR); while(1) sleep(1000); return 0; } step2: 驱动发送信号函数 驱动通过kill_fasync(&async, SIGIO, POLL_IN); 发SIGIO信号给应用程序,应用程序就调用自己安装的响应函数去处理。其中async为信号序列结构体,需要进行定义以及初始化(初始化部分在step3中操作,这里假设已经初始化完毕)。POLL_IN表示的是可以写数据。 所以驱动在发送部分的程序的修改很简单。只需要在中断函数中进行通信即可: static irqreturn_t button_irq(int irq, void *dev_id) { struct pin_desc *pindesc = (struct pin_desc *)dev_id; key_val = ((*(pindesc -> key_Register)) & (1<<(pindesc -> key_pin))) ? 0 : 1; if(key_val){ // printk("%d", pindesc -> key_num); } printk("%d ",key_val); printk("%dn",pindesc->key_num); ev_press = 1; /* 表示中断发生了 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync(&button_async_queue, SIGIO, POLL_IN); // async signal to APP return IRQ_HANDLED; } step3: 初始化 这个是异步通信中最重要的一步。 初始化由两个部分组成: button_async_queue的初始化(step2中的坑) 需要告诉内核应用程序的PID 总的来说一下三行命令就可以概括这两个部分: fcntl(fd, F_SETOWN, getpid()); // acquire the PID & tell kernal the PID oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); // change fasync_flag to call frv_fasync 对于fcntl()函数的功能为根据文件描述词来操作文件的特性。其定义如下: #include #include int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock); fcntl()针对(文件)描述符提供控制。参数fd是被参数cmd操作的描述符。针对cmd的值,fcntl能够接受第三个参数int arg。 cmd值可以取为以下宏定义: 录锁是fcntl的一个重要的功能!这里使用的是异步通信的设置只是获取设置文件状态的标志。 了解了fcntl函数之后,就不难分析这三行分别的作用,以下图所示: button_async_queue 的初始化 这个结构体的定义如下: static struct fasync_struct *button_async_queue; 内核中有专门的函数对其进行初始化:fasync_helper()。这个函数作用包括分配内存和设置属性,最后在驱动的release里把fasync_helper初始化的东西free掉。 由上文分析,初始化时会调用驱动中的drv_fasync()函数,所以对于button_async_queue 的初始化的操作放在此函数进行。对应于文件结构体中的 .fasync 进行执行,所以驱动文件改写如下: int button_drv_fasync(struct file *fd, struct file *filp, int on){ printk("dirver: Initialize fasyncn"); return fasync_helper(fd, filp, on, &button_async_queue); // Initialize button_async_queue } static struct file_operations button_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = button_drv_open, .read = button_drv_read, .release = EINTkey_close, .poll = botton_drv_poll, .fasync = button_drv_fasync, }; 驱动向应用程序发送信号总代码 驱动程序 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #define DEV_NAME "button_drv" // device name static struct class *buttondrv_class; static struct class_device *buttondrv_class_dev; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */ static volatile int ev_press = 0; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; static struct fasync_struct *button_async_queue; volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; volatile unsigned long *gpgcon = NULL; volatile unsigned long *gpgdat = NULL; struct pin_desc{ volatile unsigned long *key_Register; int key_pin; int key_num; }; struct pin_desc pins_desc[4] = { {0x01, 0, 1}, {0x02, 2, 2}, {0x03, 3, 3}, {0x04, 11, 4}, }; static irqreturn_t button_irq(int irq, void *dev_id) { struct pin_desc *pindesc = (struct pin_desc *)dev_id; key_val = ((*(pindesc -> key_Register)) & (1<<(pindesc -> key_pin))) ? 0 : 1; if(key_val) { // printk("%d", pindesc -> key_num); } printk("%d ",key_val); printk("%dn",pindesc->key_num); ev_press = 1; /* 表示中断发生了 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync(&button_async_queue, SIGIO, POLL_IN); // asyn signal to APP return IRQ_HANDLED; } static int button_drv_open(struct inode *inode, struct file *file) { pins_desc[0].key_Register = gpfdat; pins_desc[1].key_Register = gpfdat; pins_desc[2].key_Register = gpgdat; pins_desc[3].key_Register = gpgdat; request_irq(IRQ_EINT0, button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); request_irq(IRQ_EINT2, button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); request_irq(IRQ_EINT11, button_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); request_irq(IRQ_EINT19, button_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); return 0; } ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { if(size == -1) return -EINVAL; /* 如果没有按键动作, 休眠 */ wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */ copy_to_user(buf, &key_val, 1); ev_press = 0; return 1; } static int EINTkey_close(struct inode *inode, struct file *file) { // FREE IRQ free_irq(IRQ_EINT0, &pins_desc[0]); free_irq(IRQ_EINT2, &pins_desc[1]); free_irq(IRQ_EINT11, &pins_desc[2]); free_irq(IRQ_EINT19, &pins_desc[3]); return 0; } unsigned int botton_drv_poll(struct file *fd, struct poll_table_struct *wait) { unsigned int mask = 0; poll_wait(fd, &button_waitq, wait); // 不会立即休眠 if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } int button_drv_fasync(struct file *fd, struct file *filp, int on) { printk("dirver: Initialize fasyncn"); return fasync_helper(fd, filp, on, &button_async_queue); // Initialize button_async_queue
上一篇:S3C2440 开发板实战(11):同步互斥
下一篇:S3C2440 开发板实战(9):poll机制
推荐阅读最新更新时间:2024-11-11 10:41
- 热门资源推荐
- 热门放大器推荐
设计资源 培训 开发板 精华推荐
- DM320205、SAML11 Xplained Pro 评估套件
- LTM9009-14 演示板,14 位,80Msps,1.8V 八路串行 ADC,DC <艾因< 70MHz
- 【训练营】仿生机械狗
- LT3420EMS 演示板,选通电容器充电器,高压电源,输入 = 1.8V 至 16V,Vout = 300V
- DER-949 - 使用 InnoSwitch4-CZ PowiGaN 和 ClampZero 的 70 W 墙壁插座电源,具有 USB Type-C/A 端口
- DC1747A-A,使用 LTM2882-3 双路隔离 RS232 模块收发器 + 电源的演示板
- LTC1503 的典型应用 - 高效无电感降压型 DC/DC 转换器
- 使用 ROHM Semiconductor 的 BD4954 的参考设计
- 具有 PowerPath 的 LTC4162EUFD-SADM 9V 至 35V、3.2A 降压型开关电池充电器的典型应用
- AP3417C 1.5MHz同步降压DC-DC转换器ADJ版典型应用