배우고픈 공돌이

5.워크 큐 본문

디바이스 드라이버

5.워크 큐

내 마음 아홉수 2017. 11. 16. 10:08

인터럽트와는 다르게 지속적으로 io를 감시할 때, 워크 큐를 사용한다.


*센싱장치 중에서 간헐적으로 얻는게 아닌 항상 감지가 필요한 키 매트릭스 등에 사용하는 기법이다.


사용할 때, 타이머를 사용하면 좋겠지만 그보다 더 빠른 처리, 큰 데이터 처리가 요구되면 워크큐를 생각할 수 있다.


예를 들어 블럭킹 코드를 생각해보자.


ISR 안의 프로세스를 깨우는 동작이 실행되면 디바이스 READ에서 동작을 처리한다.


여기서, ISR의 데이터 처리량이 많아진다면(길어진다면) 그만큼 효율이 떨어진다.


이를 분산하고, 보조하기 위한 장치가 work queue이다.


ISR에서 신호의 값만 추출한 후, 그 신호를 분석하기 위해 work queue를 놓고 READ를 실행하면 이상적일 것이다.




*ISR만 놓는다면, 후에 들어온 신호가 블럭당해 처음의 신호만 해석 후, 그 후 다시 받는다.

*work queue가 있다면, 먼저 들어온 신호가 새로 들어온 신호에 블럭당해, 마지막 신호를 해석한다.

*work queue는 커널의 쓰레드가 담당하는 영역으로 우선순위가 높다.



사용에 필요한 구조, 메크로 함수는 다음과 같다.



소스


#include <linux/module.h>

#include <linux/init.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/slab.h>

#include <linux/sched.h>

#include <asm/uaccess.h>

#include <linux/fdtable.h>

#include <linux/timer.h>

#include <linux/time.h>

#include <linux/interrupt.h>

#include "skeleton.h"

#include <linux/wait.h>

#include <linux/workqueue.h>



#define SK_IRQ 7

#define SK_MAJOR 240


static KERNEL_TIMER_MANAGER *ptrmng = NULL;

static DECLARE_WAIT_QUEUE_HEAD(wait_read); //wait queue for read

static DECLARE_WORK(work_queue, call_workqueue);


int result;

int condition=0; 


void call_workqueue(struct work_struct *work)

{

printk("[WORK_QUEUE] function call\n");

}


static irqreturn_t sk_interrupt(int irq, void *dev_id)

{

printk("Call interrupt handler\n");

wake_up_interruptible(&wait_read);

schedule_work(&work_queue);


return IRQ_HANDLED;

}


void kerneltimer_register(KERNEL_TIMER_MANAGER *ptr,unsigned long time_over)

{

init_timer(&(ptr->timer));

ptr->timer.expires = get_jiffies_64() + time_over;

ptr->timer.data    = (unsigned long)ptr;

ptr->timer.function= kerneltimer_timeover;

add_timer(&(ptr->timer));

}


void kerneltimer_timeover(unsigned long time_over)

{

KERNEL_TIMER_MANAGER *ptr = NULL;

if(time_over)

{

ptr = (KERNEL_TIMER_MANAGER *)time_over;

ptr->work++;

if(ptr->work % 10 == 0)

{

condition++;

sk_interrupt(SK_IRQ,NULL);

//printk("work value is %ld\n",ptr->work);

}

}

kerneltimer_register(ptr,TIME_STEP);

}


/*

    minor1

*/

static int minor1_open(struct inode *inode, struct file *filp)

{

printk("minor1 open success!\n");

schedule_work(&work_queue);

return 0;

}


static int minor1_release(struct inode *inode, struct file *filp)

{

printk("minor1 release success!\n");

return 0;

}


static ssize_t minor1_read(struct file *filp, char * buf,size_t count,loff_t * f_pos)

{

int pid = 0;

unsigned long start_code[2] = {0};

printk("before sleep\n");

schedule_work(&work_queue);

if(!condition)

wait_event_interruptible(wait_read,condition); //don`t wake up and  be in wait queue

condition = 0;

if(count == 4)

{

pid = current->pid;

copy_to_user(buf, &pid, count);

}

else

{

start_code[0] = current->mm->start_code;

start_code[1] = current->mm->end_code;

copy_to_user(buf, start_code, count);

}


return count;

}


/*

    minor2

*/

static int minor2_open(struct inode *inode, struct file *filp)

{

printk("minor2 open success!\n");

return 0;

}


static int minor2_release(struct inode *inode, struct file *filp)

{

printk("minor2 release success!\n");

return 0;

}


static ssize_t minor2_write(struct file *filp, const char * buf, size_t count, loff_t * f_pos)

{

char data[30] = {0};

copy_from_user(data, buf, count);

printk("write >>> %s\n", data);


return count;

}


/*

    minor3

*/


static int minor3_open(struct inode *inode, struct file *filp)

{

printk("minor3 open success!\n");

return 0;

}


static int minor3_release(struct inode *inode, struct file *filp)

{

printk("minor3 release success!\n");

return 0;

}


static long minor3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

int pid, next_fd, f_flags;

unsigned long start_address[2] = {0};

unsigned long i_ino;


switch(cmd)

{

case 'a':

pid = current->pid;

copy_to_user((int *)arg, &pid,4);

break;

case 'b':

start_address[0] = current->mm->start_code;

start_address[1] = current->mm->end_code;

copy_to_user((long *)arg, start_address, 16);

break;

case 'c':

next_fd = current->files->next_fd;

copy_to_user((long *)arg,&next_fd,sizeof(next_fd)); 

printk("netx fd is %d\n", next_fd);

break;

case 'd':

f_flags = filp->f_flags;

copy_to_user((int *)arg,&f_flags,sizeof(f_flags));

printk("f_flags is %d\n", f_flags);

break;

case 'e':

i_ino = filp->f_path.dentry->d_inode->i_ino;

copy_to_user((unsigned long *)arg, &i_ino,sizeof(i_ino));

printk("i_ino is %ld\n", i_ino);

break;

}

return 0;

}



struct file_operations minor1_fops =

{

.owner = THIS_MODULE,

.open = minor1_open,

.release = minor1_release,

.read = minor1_read,

};


struct file_operations minor2_fops =

{

.owner = THIS_MODULE,

.open = minor2_open,

.release = minor2_release,

.write = minor2_write,

};


struct file_operations minor3_fops =

{

.owner = THIS_MODULE,

.open = minor3_open,

.release = minor3_release,

.unlocked_ioctl = minor3_ioctl,

};



static int master_open(struct inode *inode, struct file *filp)

{

printk("master open success\n");


switch(MINOR(inode->i_rdev))

{

case 1:

filp->f_op = &minor1_fops;

break;

case 2:

filp->f_op = &minor2_fops;

break;

case 3:

filp->f_op = &minor3_fops;

break;

default:

return -ENXIO;

}

if(filp->f_op && filp->f_op->open)

return filp->f_op->open(inode,filp);

return 0;

}


struct file_operations master_fops =

{

.owner = THIS_MODULE,

.open = master_open,

};


static int __init skeleton_init(void)

{

int ret;

printk("skeleton init success\n");

ret = register_chrdev(SK_MAJOR,"skeleton",&master_fops);

if(ret <0)

return ret;

request_irq(SK_IRQ,sk_interrupt,IRQF_SHARED,"skriq",NULL); 

//request_irq(id, function, flag, name, argument)


ptrmng = kmalloc(sizeof(KERNEL_TIMER_MANAGER),GFP_KERNEL);

if(!ptrmng)

return -ENOMEM;

else

memset(ptrmng,0,sizeof(KERNEL_TIMER_MANAGER));


kerneltimer_register(ptrmng,TIME_STEP);

return 0;

}

static void __exit skeleton_exit(void)

{

printk("skeleton exit success\n");

unregister_chrdev(SK_MAJOR,"skeleton");

if(ptrmng)

{

del_timer(&(ptrmng->timer));

kfree(ptrmng);

}

}


module_init(skeleton_init);

module_exit(skeleton_exit);


MODULE_LICENSE("GPL");




work queue의 사용 시점 : 스케쥴 전환(스케줄링)을 할 때 call 됨. (schedule_work(&work_struct);)


1. ISR에서 프로세스를 깨울 때    

: IRQ 내에 schedule_work


2. user mode에서 open( )-> kernel mode에서 sys_open( ) -> D/D open( ) -> !!!! -> usermode 복귀 할 때 (스케줄 전환이 일어난다.)

: D/D OPEN( ) 내에 schedule_work


3. 프로세스를 잠재울 때

: D/D READ( ) 내에 schedule_work




'디바이스 드라이버' 카테고리의 다른 글

예제.  (0) 2017.11.20
6. 다중화 입출력  (0) 2017.11.16
4. 블럭킹 io  (0) 2017.11.15
3. 인터럽트  (0) 2017.11.15
2. 타이머  (0) 2017.11.15
Comments