Linux 中断管理tasklet

tasklets 机制是linux中断处理机制中的软中断延迟机制,在linux中存在硬中断和软中断的区别,但linux接收到硬件中断之后,通过tasklet函数来设定软中断被执行的优先程度从而导致软中断处理函数被优先执行的差异性

tasklet 内核层驱动程序:

mydemodrv_tasklet.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/kfifo.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/poll.h>
#include <linux/interrupt.h>

#define DEMO_NAME "mydemo_dev"
#define MYDEMO_FIFO_SIZE 64

static dev_t dev;
static struct cdev *demo_cdev;
static struct class *mydemo_class;

struct mydemo_device {
    char name[64];
    struct device *dev;
        wait_queue_head_t read_queue;
    wait_queue_head_t write_queue;    
    struct kfifo mydemo_fifo;
    struct fasync_struct *fasync;
    struct mutex lock;
};

struct mydemo_private_data {
    struct mydemo_device *device;
    char name[64];    
    struct tasklet_struct tasklet;
};

#define MYDEMO_MAX_DEVICES  8
static struct mydemo_device *mydemo_device[MYDEMO_MAX_DEVICES]; 

static void do_tasklet(unsigned long data)
{
    struct mydemo_device *device = (struct mydemo_device *)data;

    dev_info(device->dev, "%s: trigger a tasklet\n", __func__);
}

static int demodrv_open(struct inode *inode, struct file *file)
{
    unsigned int minor = iminor(inode);
    struct mydemo_private_data *data;
    struct mydemo_device *device = mydemo_device[minor];

    dev_info(device->dev, "%s: major=%d, minor=%d, device=%s\n", __func__, 
            MAJOR(inode->i_rdev), MINOR(inode->i_rdev), device->name);

    data = kmalloc(sizeof(struct mydemo_private_data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    sprintf(data->name, "private_data_%d", minor);
    tasklet_init(&data->tasklet, do_tasklet, (unsigned long)device);

    data->device = device;
    file->private_data = data;

    return 0;
}

static int demodrv_release(struct inode *inode, struct file *file)
{
    struct mydemo_private_data *data = file->private_data;

    tasklet_kill(&data->tasklet);
    kfree(data);
    
    return 0;
}

static ssize_t
demodrv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    struct mydemo_private_data *data = file->private_data;
    struct mydemo_device *device = data->device;
    int actual_readed;
    int ret;


    if (kfifo_is_empty(&device->mydemo_fifo)) {
        if (file->f_flags & O_NONBLOCK)
            return -EAGAIN;

        dev_info(device->dev, "%s:%s pid=%d, going to sleep, %s\n", __func__, device->name, current->pid, data->name);
        ret = wait_event_interruptible(device->read_queue,
                    !kfifo_is_empty(&device->mydemo_fifo));
        if (ret)
            return ret;
    }

    mutex_lock(&device->lock);
    ret = kfifo_to_user(&device->mydemo_fifo, buf, count, &actual_readed);
    if (ret)
        return -EIO;
    //在这边触发tasklet函数
    tasklet_schedule(&data->tasklet);
    mutex_unlock(&device->lock);

    if (!kfifo_is_full(&device->mydemo_fifo)){
        wake_up_interruptible(&device->write_queue);
        kill_fasync(&device->fasync, SIGIO, POLL_OUT);
    }

    
    dev_info(device->dev, "%s:%s, pid=%d, actual_readed=%d, pos=%lld\n",__func__,
            device->name, current->pid, actual_readed, *ppos);
    return actual_readed;
}

static ssize_t
demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    struct mydemo_private_data *data = file->private_data;
    struct mydemo_device *device = data->device;

    unsigned int actual_write;
    int ret;

    if (kfifo_is_full(&device->mydemo_fifo)){
        if (file->f_flags & O_NONBLOCK)
            return -EAGAIN;

        dev_info(device->dev, "%s:%s pid=%d, going to sleep\n", __func__, device->name, current->pid);
        ret = wait_event_interruptible(device->write_queue,
                !kfifo_is_full(&device->mydemo_fifo));
        if (ret)
            return ret;
    }

    mutex_lock(&device->lock);

    ret = kfifo_from_user(&device->mydemo_fifo, buf, count, &actual_write);
    if (ret)
        return -EIO;
    mutex_unlock(&device->lock);

    if (!kfifo_is_empty(&device->mydemo_fifo)) {
        wake_up_interruptible(&device->read_queue);
        kill_fasync(&device->fasync, SIGIO, POLL_IN);
        printk("%s kill fasync\n", __func__);
    }

    dev_info(device->dev, "%s:%s pid=%d, actual_write =%d, ppos=%lld, ret=%d\n", __func__,
            device->name, current->pid, actual_write, *ppos, ret);

    return actual_write;
}

poll系统调用操作接口函数

//当程序需要进行对多个文件读写时,如果某个文件没有准备好,则系统就会处于读写阻塞的状态,影响其他文件的读写。为了避免读写阻塞,使用poll函数。
//如果设备无阻塞地读,就返回POLLIN; 通常的数据已经准备好,可以读了,就返回POLLRDNORM。
static unsigned int demodrv_poll(struct file *file, poll_table *wait)
{
    int mask = 0;
    struct mydemo_private_data *data = file->private_data;
    struct mydemo_device *device = data->device;

    mutex_lock(&device->lock);

    poll_wait(file, &device->read_queue, wait);
    poll_wait(file, &device->write_queue, wait);

    if (!kfifo_is_empty(&device->mydemo_fifo))
        mask |= POLLIN | POLLRDNORM;
    if (!kfifo_is_full(&device->mydemo_fifo))
        mask |= POLLOUT | POLLWRNORM;

    mutex_unlock(&device->lock);
    
    return mask;
}


// 应用层fcntl(fd, F_SETFL, flag | FASYNC) 对应此函数
// 设置异步通知
static int demodrv_fasync(int fd, struct file *file, int on)
{
    struct mydemo_private_data *data = file->private_data;
    struct mydemo_device *device = data->device;
    int ret;

    mutex_lock(&device->lock);

    dev_info(device->dev, "%s send SIGIO\n", __func__);
    ret = fasync_helper(fd, file, on, &device->fasync);
    mutex_unlock(&device->lock);

    return ret;
}

static const struct file_operations demodrv_fops = {
    .owner = THIS_MODULE,
    .open = demodrv_open,
    .release = demodrv_release,
    .read = demodrv_read,
    .write = demodrv_write,
    .poll = demodrv_poll,
    .fasync = demodrv_fasync,
};

static int __init simple_char_init(void)
{
    int ret;
    int i;
    struct mydemo_device *device;
    
    ret = alloc_chrdev_region(&dev, 0, MYDEMO_MAX_DEVICES, DEMO_NAME);
    if (ret) {
        printk("failed to allocate char device region");
        return ret;
    }

    demo_cdev = cdev_alloc();
    if (!demo_cdev) {
        printk("cdev_alloc failed\n");
        goto unregister_chrdev;
    }

    cdev_init(demo_cdev, &demodrv_fops);
    
    ret = cdev_add(demo_cdev, dev, MYDEMO_MAX_DEVICES);
    if (ret) {
        printk("cdev_add failed\n");
        goto cdev_fail;
    }
    
    mydemo_class = class_create(THIS_MODULE, "my_class");

    for (i = 0; i < MYDEMO_MAX_DEVICES; i++) {
        device = kzalloc(sizeof(struct mydemo_device), GFP_KERNEL);
        if (!device) {
            ret = -ENOMEM;
            goto free_device;
        }

        sprintf(device->name, "%s%d", DEMO_NAME, i);
        mutex_init(&device->lock);

        device->dev = device_create(mydemo_class, NULL, MKDEV(dev, i), NULL, "mydemo:%d:%d", MAJOR(dev), i);
        dev_info(device->dev, "create device: %d:%d\n", MAJOR(dev), MINOR(i));
        mydemo_device[i] = device;
        init_waitqueue_head(&device->read_queue);
        init_waitqueue_head(&device->write_queue);

        ret = kfifo_alloc(&device->mydemo_fifo,
                MYDEMO_FIFO_SIZE,
                GFP_KERNEL);
        if (ret) {
            ret = -ENOMEM;
            goto free_kfifo;
        }

        printk("mydemo_fifo=%p\n", &device->mydemo_fifo);

    }

    printk("succeeded register char device: %s\n", DEMO_NAME);

    return 0;

free_kfifo:
    for (i =0; i < MYDEMO_MAX_DEVICES; i++)
        if (&device->mydemo_fifo)
             kfifo_free(&device->mydemo_fifo);
free_device:
    for (i =0; i < MYDEMO_MAX_DEVICES; i++)
        if (mydemo_device[i])
            kfree(mydemo_device[i]);
cdev_fail:
    cdev_del(demo_cdev);
unregister_chrdev:
    unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);
    return ret;
}

static void __exit simple_char_exit(void)
{
    int i;
    printk("removing device\n");

    if (demo_cdev)
        cdev_del(demo_cdev);

    unregister_chrdev_region(dev, MYDEMO_MAX_DEVICES);

    for (i =0; i < MYDEMO_MAX_DEVICES; i++) {
        if (mydemo_device[i]) {
            device_destroy(mydemo_class, MKDEV(dev, i));
            kfree(mydemo_device[i]);    

        }
    }
    class_destroy(mydemo_class);
}

module_init(simple_char_init);
module_exit(simple_char_exit);

MODULE_AUTHOR("Benshushu");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("simpe character device");

应用层测试程序

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>

static int fd;

void my_signal_fun(int signum, siginfo_t *siginfo, void *act)
{
    int ret;
    char buf[64];

    if (signum == SIGIO) {
        if (siginfo->si_band & POLLIN) {
            printf("FIFO is not empty\n");
            if ((ret = read(fd, buf, sizeof(buf))) != -1) {
                buf[ret] = '\0';
                puts(buf);
            }
        }
        if (siginfo->si_band & POLLOUT)
            printf("FIFO is not full\n");
    }
}

int main(int argc, char *argv[])
{
    int ret;
    int flag;
    struct sigaction act, oldact;

    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, SIGIO);
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = my_signal_fun;
    if (sigaction(SIGIO, &act, &oldact) == -1)
        goto fail;

    fd = open("/dev/mydemo0", O_RDWR);
    if (fd < 0) 
        goto fail;

    /*设置异步IO所有权*/
    if (fcntl(fd, F_SETOWN, getpid()) == -1)
        goto fail;
    
    /*将当前进程PID设置为fd文件所对应驱动程序将要发送SIGIO,SIGUSR信号进程PID*/
    if (fcntl(fd, F_SETSIG, SIGIO) == -1)
        goto fail;
    
    /*获取文件flags*/
    if ((flag = fcntl(fd, F_GETFL)) == -1)
        goto fail;
    
    /*设置文件flags, 设置FASYNC,支持异步通知*/
    if (fcntl(fd, F_SETFL, flag | FASYNC) == -1)
        goto fail;

    while (1)
        sleep(1);

fail:
    perror("fasync test");
    exit(EXIT_FAILURE);
}

Makefile

BASEINCLUDE ?= /lib/modules/`uname -r`/build

mydemo_tasklet-objs := mydemodrv_tasklet.o 

obj-m    :=   mydemo_tasklet.o
all : 
    $(MAKE) -C $(BASEINCLUDE) M=$(PWD) modules;

clean:
    $(MAKE) -C $(BASEINCLUDE) M=$(PWD) clean;
    rm -f *.ko;

测试:

在/sys/class/my_class/目录下面看到这些设备

可以看到主设备号为249的设备,mknod /dev/mydemo0 c 249 0

执行应用层程序./test &

echo "i am study runninglinuxkernel" > /dev/mydemo0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水火汪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值