[태그:] workqueue

  • workqueue 실습

    workqueue 실습

    리눅스가 work queue를 다양하게 사용한다. 나는 초짜라 DECLARE_WORK와 INIT_WORK를 구분할 수 없었다. DECLARE_WORK가 work struct를 전역 변수로 선언한다. 여러 work로 같은 데이터에 접근할 수 있다. workqueue funtion() 파라미터로 work struct를 넣는데, 여기로 work를 전달하면 된다. 코드가 넝마조각이 되고 있다.

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/irq.h>
    #include <linux/interrupt.h>
    #include <linux/gpio.h>     //GPIO
    
    
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/uaccess.h>
    #include <linux/fcntl.h>
    
    
    #include <linux/workqueue.h>
    
    #define GPIO_10_OUT (10)
    #define DEVICE_DATA_MAX 256
    
    unsigned int GPIO_irqNumber;
    
    /*\uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \ubd80\ubd84*/
    static struct my_device_data{
    	struct cdev cdev;
    	int index;
    	char my_string[DEVICE_DATA_MAX];
    	struct work_struct mywork;
    	struct work_struct work_read;
    } my_data_global;
    
    struct my_device_data *work_ptr;
    struct my_device_data *work_ptr_read;
    struct my_device_data *my_data;
    
    /*workque initiate*/
    
    void workqueue_fn(struct work_struct *work);
    
    //defien workqueue_fn//
    
    void workqueue_fn(struct work_struct *work_ptr)
    {
    	/*work_ptr\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \ub370\uc774\ud130 \uc811\uadfc*/
    	struct my_device_data *my_pointer;
    	//container of \ub85c \uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \uc811\uadfc.
    	my_pointer = container_of(work_ptr, struct my_device_data, mywork);
    	my_pointer->index++;
    	pr_info("Executing workqueue function\n");
    	pr_info("index is %d\n",my_pointer->index);
    
    }
    
    
    void workqueue_read(struct work_struct *work_ptr)
    {
    	/*work_ptr\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \ub370\uc774\ud130 \uc811\uadfc*/
    	struct my_device_data *my_pointer;
    	//container of \ub85c \uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \uc811\uadfc.
    	my_pointer = container_of(work_ptr, struct my_device_data, work_read);
    	my_pointer->index++;
    	pr_info("opened, index is %d\n", my_pointer->index);
    
    }
    
    static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    {
    	/*Scheduling work queue*/
    	schedule_work(&work_ptr->mywork);
    	pr_info("workqueue scheduled \n");
    	return IRQ_HANDLED;
    
    }
    
    
    //device driver \uc791\uc131 \ubd80\ubd84.
    /*************\ub4dc\ub77c\uc774\ubc84 \ud568\uc218 ******************/
    static int mydriver_open(struct inode *inode, struct file *file);
    static int mydriver_release(struct inode *inode, struct file *file);
    static ssize_t mydriver_read(struct file *flip,
    		char *buf, size_t len, loff_t *off);
    static ssize_t mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off);
    /********************************************/
    
    
    
    //file operation structure
    
    static struct file_operations fops = 
    {
    	.owner = THIS_MODULE,
    	.read = mydriver_read,
    	.write = mydriver_write,
    	.open = mydriver_open,
    	.release = mydriver_release,
    };
    
    static int mydriver_open(struct inode *inode, struct file *file)
    {
    	pr_info("Deviced file was opend.\n");
    	//my_data_global.index++;
    	
    	return 0;
    
    }
    
    static int mydriver_release(struct inode *inode, struct file *file)
    {
    	pr_info("Deviced file was closed.\n");
    	return 0;
    }
    
    
    static int mydriver_read(struct file *file,
    		char *buf, size_t len, loff_t *off)
    {
    	struct my_device_data *my_data;
    	size_t datalen, read_len;
    	my_data = (struct my_device_data*)&my_data_global;
    	datalen = strlen(my_data->my_string);
    	//\ucd5c\ub300\uac12\uc73c\ub85c \uac15\uc81c \uc124\uc815
    	if(len > datalen)
    	{
    		len = datalen;
    	}
    	read_len = len - *off;
    	if (read_len <= 0)
    		return 0;
    	pr_info("start %p, offset is %lld, read_len is %d\n", my_data->my_string, *off, read_len);
    	if(copy_to_user(buf, my_data->my_string+*off, read_len))
    		return -EFAULT;
    	//read_len = datalen-*off;
    	*off += read_len;
    	pr_info("kernel has %d, read %d characters from kernel\n", datalen, read_len);
    	//zero\ub97c \ubc18\ud658\ud560 \ub54c\uae4c\uc9c0 \ubc18\ubcf5.
    	schedule_work(&work_ptr->work_read);
    	return read_len;
    }
    
    static int mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off)
    {
    	size_t datalen, write_len, start_pos;
    	my_data = &my_data_global;
    	start_pos = strlen(my_data->my_string);
    	datalen = strlen(my_data->my_string);
    	if(len > datalen)
    	{
    		len = DEVICE_DATA_MAX;
    	}
    
    	write_len = len - *off;
    
    	if(copy_from_user(my_data->my_string + start_pos + *off, buf, write_len))
    		return -EFAULT;
    	*off += write_len;
    	return write_len;
    }
    
    dev_t dev = 0;
    static struct cdev my_cdev;
    static struct class *dev_class;
    static int __init init_hw(void)
    {
    	//\ub514\ubc14\uc774\uc2a4 \ub4f1\ub85d
    	if(( alloc_chrdev_region(&dev, 0, 1, "test_device") < 0))
    	{
    		pr_err("[!]character device was not allocated\n");
    		goto r_unreg;
    
    	}
    	pr_info("[=]%d-%d, was allocated\n", MAJOR(dev), MINOR(dev));
    
    
    	//\ucd08\uae30\ud654
    	cdev_init(&my_cdev, &fops);
    	pr_info("[=]driver was initialized\n");
    
    
    	//\uc2dc\uc2a4\ud15c\uc5d0 \ucd94\uac00
    	if((cdev_add(&my_cdev, dev, 1)) < 0)
    	{
    		pr_err("[!]cannot add device to kernel\n");
    		goto r_del;
    
    	}
    
    
    	//class \ub9cc\ub4e6.
    	if((dev_class=class_create(THIS_MODULE, "my_class")) == NULL)
    	{
    		pr_err("[!]cannot add class\n");
    		goto r_class;
    	}
    
    
    	if((device_create(dev_class, NULL, dev, NULL, "my_device")) == NULL)
    	{
    
    		pr_err("[!]cannot create device\n");
    		goto r_device;
    	}
    	work_ptr = kmalloc(sizeof(*work_ptr), GFP_KERNEL);
    	if(work_ptr == NULL)
    	{
    		pr_err("[!]cannot allocate memory\n");
    		goto r_work;
    	}
    	INIT_WORK(&work_ptr->mywork, workqueue_fn);
    
    
    
    	//work_ptr_read = kmalloc(sizeof(*work_ptr), GFP_KERNEL);
    	INIT_WORK(&work_ptr->work_read, workqueue_read);
    
    	//gpio 10\ubc88\uc744 \uc0ac\uc6a9.
    	//export\ud558\uc5ec \uac04\ub2e8\ud788 \uc0ac\uc6a9.
    	//\uc785\ub825\uc740 \uac12\uc744 \uc368 \ub123\uc744 \uc218 \uc5c6\uc74c. \ucd9c\ub825\uc73c\ub85c \uc124\uc815.
    	GPIO_irqNumber = gpio_to_irq(GPIO_10_OUT);
    	pr_info("[=]irq %d was assinged\n",GPIO_irqNumber);
    
    	//interrupt \ub4f1\ub85d \ud544\uc694
    	if (request_irq(GPIO_irqNumber,
    				(void*)gpio_irq_handler,
    				IRQF_TRIGGER_RISING,
    				"my_device",
    				NULL))
    	{
    		pr_err("[!]my_device: cannot register IRQ\n");
    		goto r_gpio;
    	}
    	pr_info("[=]module was installed\n");
    	return 0;
    r_gpio:
    	gpio_free(GPIO_10_OUT);
    r_device:
    	device_destroy(dev_class,dev);
    
    r_class:
    	class_destroy(dev_class);
    r_del:
    	cdev_del(&my_cdev);
    
    r_unreg:
    	unregister_chrdev_region(dev,1);
    r_work:
    	kfree(work_ptr);
    
    	return -1;
    }
    
    static void __exit exit_hw(void) {
    	free_irq(GPIO_irqNumber, NULL);
    	gpio_free(GPIO_10_OUT);
    	//flush_work(struct work_struct *work);
    	device_destroy(dev_class,dev);
    	//class_unregister(dev_class);
    	class_destroy(dev_class);
    	cdev_del(&my_cdev);
    	unregister_chrdev_region(dev,1);
    	flush_work(&work_ptr->mywork);
    	kfree(work_ptr);
    	printk(KERN_INFO "module was removed\n");
    }
    
    
    module_init(init_hw);
    module_exit(exit_hw);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("noname");
    MODULE_DESCRIPTION("Hello, world!");
    

    모듈을 로딩하고 인터럽트를 만들면 schedule_work로 등록한 workqueue_fn을 실행한다.

    [   52.456926] [=]236-0, was allocated
    [   52.456935] [=]driver was initialized
    [   52.457621] [=]irq 54 was assinged
    [   52.457693] [=]module was installed
    [   68.542830] workqueue scheduled 
    [   68.542855] Executing workqueue function
    [   68.542866] index is 1
    [   69.549436] workqueue scheduled 
    [   69.549460] Executing workqueue function
    [   69.549466] index is 2
    [   70.552638] workqueue scheduled 
    [   70.552659] Executing workqueue function
    [   70.552663] index is 3

    파일을 오픈하면 같은 데이터를 다른 work로 index를 접근할 수 있다. schedule_work(&work_ptr->work_read)를 디바이스를 읽을 때마다 실행한다.

    [   86.495196] Deviced file was opend.
    [   86.495242] Deviced file was closed.
    [   91.476276] Deviced file was opend.
    [   91.476318] start 9f3ac859, offset is 0, read_len is 5
    [   91.476327] kernel has 5, read 5 characters from kernel
    [   91.476367] opened, index is 4
    [   91.476430] Deviced file was closed.
    [  105.664089] Deviced file was opend.
    [  105.664138] start 9f3ac859, offset is 0, read_len is 5
    [  105.664149] kernel has 5, read 5 characters from kernel
    [  105.664197] opened, index is 5
    [  105.664275] Deviced file was closed.

    https://www.programmersought.com/article/61671813037/

    https://stackoverflow.com/questions/7937245/how-to-use-linux-work-queue/7938990

    https://github.com/fervagar/kernel_modules/blob/master/workQueue.c