배우고픈 공돌이
4. 블럭킹 io 본문
센서를 읽는 프로세스가 생성되었을 때, 그 센서의 신호가 비동기적으로(간헐적으로) 동작한다 가정한다.
이 가정에서 계속 프로세스가 Run이 된다면 비효율적이다.
그래서 인터럽트가 되기전에는 잠재우고, 인터럽트 발생 시에 wait queue에서 run queue로 이동시킨다면 효율적인 프로세싱이 된다. (*blocking은 멀티프로세싱(os), RTOS에서 의미가 있다.)
그렇기 위해서 필요한 헤더는 /linux/wait.h로 다음과 같은 타입의 wait 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>
#define SK_IRQ 7
#define SK_MAJOR 240
int result;
static KERNEL_TIMER_MANAGER *ptrmng = NULL;
static DECLARE_WAIT_QUEUE_HEAD(wait_read); //wait queue for read
//wait_read라는 wait queue를 생성하는 메크로 함수.
int condition=0;
static irqreturn_t sk_interrupt(int irq, void *dev_id)
{
printk("Call interrupt handler\n");
wake_up_interruptible(&wait_read);
//인터럽트 동작 시에 wait_read의 queue 안에 있는 프로세스를 깨워 run 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++;
//플래그가 0이 아니면 프로세스가 Run queue에 올라간다.
//**깨어나면 이 다음부터 실행하게 되어있다.
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");
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};
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);
}
condition = 0;
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");