Linux内核中断引入用户空间(异步通知机制)

本文介绍如何在Linux中利用SIGIO信号实现内核空间中断到用户空间的异步通知。通过驱动程序发送SIGIO信号并注册回调函数,使得用户程序能够响应内核中断。示例代码展示了具体的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        当linux内核空间发生中断后怎么使用户空间的应用程序运行相应的函数呢,当芯片有数据到来时内核会产生一个中断,但是怎样通知应用程序来取数据,以前这个问题一直困扰我很长时间,后来发现linux中有异步通知机制,在用户程序中用signal注册一个响应SIGIO信号的回调函数,然后在驱动程序中向该进程发出SIGIO信号便完成该功能,下面是该功能具体实施方法:

1.在驱动中定义一个static struct fasync_struct *async;

2.在fasync系统调用中注册fasync_helper(fd, filp, mode, &async);

3.在中断服务程序(顶半部、底半部都可以)发出信号kill_fasync(&async, SIGIO, POLL_IN);

4.在用户应用程序中用signal注册一个响应SIGIO的回调函数signal(SIGIO, sig_handler);

5.通过fcntl(fd, F_SETOWN, getpid())将将进程pid传入内核

6.通过fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC)设置异步通知


驱动部分代码:


#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <asm-generic/siginfo.h>
#include <linux/init.h>
#include <asm/signal.h>
#include <linux/timer.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "mybeep"

volatile unsigned long *GPBCON;
volatile unsigned long *GPBDAT;
volatile unsigned long *GPBUP;
void beep_start(void);
void beep_stop(void);
int  beep_irq_register(void);
unsigned int flag=1;

static struct fasync_struct *async; //声明fasync_struct
struct key_irq_desc {
	unsigned int irq;
	int pin;
	int pin_setting;
	int number;
	char *name;
};

static int beep_fasync(int fd, struct file *filp, int mode)
{
	printk("application  fasync!\n");
	return fasync_helper(fd, filp, mode, &async);         //注册上层调用进程的信息,上层调用fcntl设置FASYNC会调用这个系统调用
}

static struct key_irq_desc key_irqs [] = {
	{IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 0, "KEY1"},
};

static irqreturn_t key_interrupt(int irq, void *dev_id)
{
	kill_fasync(&async, SIGIO, POLL_IN);  //向打开设备文件的进程发出SIGIO信号
	return (IRQ_HANDLED);
}

void beep_gpiob_init(void)
{
	*GPBCON&=~((1<<0)|(1<<1));
	*GPBCON|=(1<<0);
	*GPBUP&=~(1<<0);
}

void beep_start(void)
{
	*GPBDAT|=(1<<0);
}

void beep_stop(void)
{
	*GPBDAT&=~(1<<0);
}

int beep_open(struct inode *inode, struct file *filp)
{
	if(beep_irq_register() != 0)
	{
		printk("Request irq error!\n");
	}
	printk(KERN_ALERT "application  open!\n");
	return 0;
}

ssize_t beep_read(struct file *file, char __user *buff, size_t count, loff_t *offp)
{
	printk("application  read!\n");
	return 0;
}

ssize_t beep_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)
{
	printk("application  write!\n");
	return 0;
}

static int beep_release(struct inode *inode, struct file *file)
{
	disable_irq(key_irqs[0].irq);
	free_irq(key_irqs[0].irq, (void *)&key_irqs[0]);
	printk("application  close!\n");
	return beep_fasync(-1, file, 0);
}

static int beep_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	switch(cmd)
	{
	case 0:
		beep_start();
		break;
	case 1:
		beep_stop();
		break;
	default:
		break;
	}
	return 0;
}

static struct file_operations beep_ops = {
	.owner = THIS_MODULE,
	.open = beep_open,
	.release = beep_release,
	.ioctl = beep_ioctl,
	.read = beep_read,
	.write = beep_write,
	.fasync = beep_fasync,
};

static struct miscdevice beep_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &beep_ops,
};

int beep_irq_register(void)
{
	int err;
	err = request_irq(key_irqs[0].irq, key_interrupt, 0, key_irqs[0].name, (void *)&key_irqs[0]);
	set_irq_type(key_irqs[0].irq, IRQ_TYPE_EDGE_RISING);
	if(err)
	{
		disable_irq(key_irqs[0].irq);
		free_irq(key_irqs[0].irq, (void *)&key_irqs[0]);
		return -EBUSY;
	}
	return 0;
}

static int __init beep_init(void)
{
	int ret;
	ret=misc_register(&beep_misc);
	if(ret <0)
	{
		printk("register miscdevice error code:%d\n",ret);
		return ret;
	}
	printk("beep device create!\n");
	GPBCON=(volatile unsigned long *)ioremap(0x56000010,12);
	GPBDAT=GPBCON+1;
	GPBUP=GPBCON+2;
	beep_gpiob_init();
	return 0;
}

static void __exit beep_exit(void)
{
	iounmap(GPBCON);
	misc_deregister(&beep_misc);
	printk("beep device delete!\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kingdragonfly");
module_init(beep_init);
module_exit(beep_exit);


用户应用程序代码:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>

void sig_handler(int sig)
{
	if(sig == SIGIO)
	{
		printf("Receive io signal from kernel!\n");
	}
}

int main(void)
{
	int fd;
	signal(SIGIO, sig_handler);
	fd = open("/dev/mybeep",O_RDWR);
	fcntl(fd, F_SETOWN, getpid());
	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);
	printf("waiting key interrupt:\n");
	while(1)
	{
	}
}

当内核里发生中断时在中断服务程序中发出SIGIO信号从而自动调用相应的回调函数,在回调函数中可以进行相应处理。

上面程序在mini2440开发板实现了按K1键,用户程序自动调用void sig_handler(int sig)功能

### 如何在Ubuntu 20.04中安全地重新安装双系统 #### 准备工作 为了确保Windows和Ubuntu双系统的顺利重装,建议先备份重要数据。这不仅限于个人文件,还包括任何重要的应用程序配置或开发环境设置。 #### 创建启动介质 创建一个可引导的USB驱动器来安装Ubuntu是非常必要的。可以使用Rufus工具或其他类似的软件完成此操作[^1]。 #### 备份现有数据 考虑到可能的数据丢失风险,在进行任何形式的操作之前,请务必做好充分准备并保存所有必需的信息。对于Linux用户来说,可以通过命令行实现快速备份: ```bash tar czf /path/to/backup.tar.gz --one-file-system / ``` 这条命令会压缩根目录下的所有内容至指定路径下名为`backup.tar.gz`的文件里,但不会跨过其他挂载点。 #### 进入BIOS调整启动顺序 重启计算机进入BIOS界面(通常通过按Del键或F2/F12键),将UEFI USB设备设为首选项以加载Live CD模式中的Ubuntu安装程序[^3]。 #### 开始安装流程 当成功从USB启动后,选择“Install Ubuntu”。在这个过程中需要注意几个关键选项的选择: - **安装类型**:如果计划保留现有的Windows操作系统,则应选择“Something else”,以便手动定义各分区的位置与大小; - **EFI System Partition (ESP)**:鉴于先前的经验教训,最好把ESP放置在同一物理磁盘上作为已有的Windows ESP共享同一位置;不过这不是绝对的要求,也可以单独划分新的EFI区段给Ubuntu使用; - **Root (/) and Home (/home) Partitions**:根据实际需求规划剩余可用空间的比例分配给root以及home目录所在卷组。 #### 配置GRUB引导菜单 安装完成后,默认情况下GRUB将会接管控制权,并提供选择要运行哪个操作系统的机会。如果有特殊需要修改默认超时时间或是隐藏不必要的条目,可以在 `/etc/default/grub` 文件内编辑相应参数后再执行 `sudo update-grub` 命令刷新配置生效[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值