배우고픈 공돌이

2. 타이머 본문

디바이스 드라이버

2. 타이머

내 마음 아홉수 2017. 11. 15. 11:34

skeleton.h


#ifndef _SKELETON_H_

#define _SKELETON_H_


#define TIME_STEP (10*HZ / 10)


typedef struct{

struct timer_list timer;

unsigned long work;

}__attribute((packed))KERNEL_TIMER_MANAGER;



void kerneltimer_register(KERNEL_TIMER_MANAGER *ptr,unsigned long time_over);

void kerneltimer_timeover(unsigned long time_over);



#endif




skeleton.c


#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 "skeleton.h"


#define SK_MAJOR 240

static KERNEL_TIMER_MANAGER *ptrmng = NULL;

int result;


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++;

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(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;

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");



드라이버를 코딩함에 있어서 timer_list 형태의 변수가 필요하며,

처음 초기화 때 다음의 3가지 인자는 꼭 정의해줘야한다.


1. 만료 시간(expires)

2. 시간 데이터의 주소(data addr)

3. 타이머 핸들러(timer_handler)


jiffies_64는 64bit 체제에서 system_tick(초당 1000씩 증가하는 시스템 전역변수)

init_timer() : 커널 타이머 구조체를 초기화

add_timer() : 커널 타이머를 구동할 함수 핸들러를 등록

del_timer() : 커널 타이머 목록에서 등록된 것을 제거


위의 소스에서는 커널 구조체를 커널 메모리를 할당받아서 생성하였음으로

모듈이 제거될 때(exit), 할당을 풀도록 kfree()를 사용한다.


위의 타이머의 동작형태는 다음과 같다.


1.driver_init -> 2.kerneltimer_register(KERNEL_TIMER_MANAGER *, unsigned long) 

-> 3.kerneltimer_timeover(unsigned long) -> 2. -> 3. .....

unsigned long에 들어가는 변수는 예약 타미어 시간이며, HZ(1초에 한번 인터럽트)를 따르고 있다.


*확인 : 모듈을 등록하면서부터 타이머는 동작한다. 

(장치등록을 안해도 모듈에 등록된 것으로만해도 커널에서는 동작하고 있다.) 




*참고 : https://elixir.free-electrons.com 에서 궁금한 구조체를 검색하여 확인한다.



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

4. 블럭킹 io  (0) 2017.11.15
3. 인터럽트  (0) 2017.11.15
1. 부번호 처리  (0) 2017.11.15
misc 장치에 등록하기  (0) 2017.09.20
디바이스 드라이버 - APP Open & Read  (0) 2017.09.19
Comments