리눅스가 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