개념 정리

c or linux 2011. 4. 22. 12:47

* 가상화 개념
멀티캐스팅은 task가 cpu에 대한 가상화 (task가 cpu를 가지고 있다.)
메모리가상화는 task가 메모리를 보는 가상화 (task가 memory를 가지고 있다.)


* 커널의 혼동
커널은 프로세스나 쓰레드가 아니다.
그냥 함수이다. 
커널 쓰레드와 커널 자체를 헛갈리면 안된다. 

* 책
process context :  app 단

interrupt context : device driver의 ISR 처리하느냐
                   요주의가 필요하다.



Posted by '김용환'
,
[환경 : linux 2.4 board]

디렉토리는 3개를 사용한다.
하나는 target의 커널 모듈 두는 디렉토리 :    ~/linux/drivers/char/
두번째는 target의 menuconfig 하는 디렉토리 : ~/linux/
다른 하나는 그냥 루트 ~

<zImage에 커널 모듈 등록하기>

커널 모듈 소스 복사
[root@linux2 fnd_rw]# cp fnd.c ~/linux/drivers/char/

menuconfig 보이게 하기.
[root@linux2 linux]# make menuconfig



character deivce->standard/generic
커널에 두거나, 모듈단위로 설치하는 거..
스페이스를 두르면, 이런 모드를 변환할 수 있는 것을 확인한다.


zImage에 넣기 위해서 menuconfig의 메뉴에 항목을 추가한다. 
menuconfig 파일은 Config.in 파일에 영향을 준다. 
[root@linux2 char]# ls -al Config.in
-r--r--r--  1 root root 10230  6월 16  2003 Config.in

소스 수정
[root@linux2 char]# gedit Config.in &


#
# Character device configuration
#
mainmenu_option next_comment
comment 'Character devices'

바로 밑에  이거를 둔다.

tristate 'FND Device Support' CONFIG_FND



그리고 저장한다. 

make menuconfig 한다.

character device로 이동 (스페이스로 선택)



Config.in에 수정된 것을 확인할 수 있다.



 menuconfig 에서 FND Device Support 한 것을 스페이스를 눌러 [*] 모드로 바꾼다.

 


[root@linux2 char]# gedit Makefile

...

obj-$(CONFIG_FND) += fnd.o

// 이런 라인 위에 위의 fnd파일을 적는다.
obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o



소스를 수정한다.


[root@linux2 char]# gedit fnd.c

int fnd_init(void)
-> int __init fnd_init(void)

void fnd_exit(void)
-> void __exit fnd_exit(void)


이유는..
__init은 자동으로 register 하는 역할을 한다. 따로 insmod를 해야 한다.
최신 linux 2.6 후반 커널에서는 __init을 쓰지 않아도 된다.
 __exit 의 의미는 메인 메모리에서 내려버리라는 의미이다.  중간에 rmmod할 필요가 없는다. 쓸데없이 메모리 사용하지 않도록 정리한다.
zImage 코드 사이즈는 영향이 없다.



컴파일 한다. 

dependency를 체크한다.
[root@linux2 linux]# make dep
clean 한다. 
[root@linux2 linux]# make clean
zimage 생성한다.
[root@linux2 linux]# make zImage

그리고, drives/char 디렉토리안에서 제대로 컴파일 되었는지 확인
[root@linux2 char]# ls fnd.o
fnd.o


이미지를 tftp를 통해서 전달해야 하니 다음과 같이 zImage를 복사한다. 

[root@linux2 linux]# cp arch/arm/boot/zImage /tftpboot/zImage_my


이미지 다운로드를 준비한다. 
 
ip선을 꼽고.. bootp에 정상적으로 붙기위해서 테스트한다. 
[root@linux2 linux]# ifconfig eth0 192.168.1.51

미니컴에서 bootp를 실행한다.
XHYPER255B> bootp
Our Ethernet address is 1234 5678 9A00.
        Sending bootp packet...
.
Bootp Packet received.
        Host   (server) Ethernet : 000C 29F2 5EB0
        Host   (server) IP       : 192.168.1.51
        Client (target) Ethernet : 1234 5678 9A00
        Client (target) IP       : 192.168.1.50


XHYPER255B> tftp zImage_my kernel
TFTP Start...
        Host   (server) IP       : 192.168.1.51
        Client (target) IP       : 192.168.1.50
        Loading Filename         : zImage_my
        Save Address             : 0xA0008000

Loading start...
        0x000E2EB4 (929460) bytes received.
        tftp done.

플래쉬에 굽는다.
XHYPER255B> flash kernel

target 보드를 컸다 키고. 파일시스템으로 들어간다. 

lsmod에는 모듈이 없고.ㅣ /proc/devices에 있는 거 확인 가능 (254 fnd)

 [root@Xhyper255 /root]$lsmod
Module                  Size  Used by
[root@Xhyper255 /root]$cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 ttyS
  5 cua
  7 vcs
 10 misc
 11 ads7843
 14 sound
 29 fb
 90 mtd
128 ptm
136 pts
162 raw
253 pcmcia
254 fnd

Block devices:
 31 mtdblock
241 mmc_block



<zimage가 아닌 동적 모듈로 추가하는 것을 이용한다>



menuconfig ui에서 스페이스를 여러번 눌러 <M>으로 선택
위와 동일하게 컴파일 한다. 

make dep
make clean
make zImage

driver들이 실제 컴파일이 일어났는지 확인한다. 
[root@linux2 char]# ls fnd.o
ls: fnd.o: 그런 파일이나 디렉토리가 없음

그러면 어떻게 모듈을 등록하지.. 바로 커널 모듈 컴파일이 필요하다.

우선 zImage를 tftp를 통해서 복사할 수 있도록 복사한다.
[root@linux2 linux]# cp arch/arm/boot/zImage /tftpboot/zImage_my2

모듈을 컴파일한다. 
[root@linux2 linux]# make modules

모듈이 컴파일 되었다. 
[root@linux2 char]# ls fnd.o
fnd.o

그러면 이걸 어떻게 올리지? 
그것은 따로 target의 file system에 해당 커널 모듈을 올리고 나서, /etc/inittab에 insmod를 추가하면 되는 것이다.


[root@linux2 Filesystem]# ls -al
합계 14740
drwxrwxrwx  2 root root    4096  2월 18  2010 .
drwxrwxrwx  8 root root    4096  2월 19  2010 ..
-rwxrwxrwx  1 root root   42087  4월 25  2008 mkfs.jffs2
-rwxrwxrwx  1 root root      73  4월 25  2008 r.sh
-rwxrwxrwx  1 root root 6620848 10월  6  2009 root_xhyper255.tar.gz
-rw-r--r--  1 root root 8388608  2월 18  2010 rootfs.img
 
[root@linux2 Filesystem]# pwd
/root/DD_MULTI/XHyper255B/Filesystem

target에 들어갈 파일 시스템 에 추가하면 된다.
압축을 풀고.. 
[root@linux2 Filesystem]# tar zxvf root_xhyper255.tar.gz  -C ~
[root@linux2 root_xhyper255]# pwd
/root/root_xhyper255

이것이 target 보드의 filesystem 구조이다. 
[root@linux2 root_xhyper255]# ls -al
합계 68
dr-xr-xr-x  15 root root 4096  4월 25  2008 .
drwxr-x---  22 root root 4096  4월 22 11:05 ..
dr-xr-xr-x   2 root root 4096  4월 25  2008 bin
dr-xr-xr-x   2 root root 4096  4월 25  2008 conf
dr-xr-xr-x   4 root root 4096  4월 25  2008 dev
dr-xr-xr-x   7 root root 4096  4월 25  2008 etc
dr-xr-xr-x   2 root root 4096  4월 25  2008 home
dr-xr-xr-x   3 root root 4096  4월 25  2008 lib
-r-xr-xr-x   1 root root  269  4월 25  2008 linuxrc
dr-xr-xr-x   2 root root 4096  4월 25  2008 lost+found
dr-xr-xr-x   5 root root 4096  4월 25  2008 mnt
dr-xr-xr-x   2 root root 4096  4월 25  2008 proc
dr-xr-xr-x   2 root root 4096  4월 25  2008 rd
dr-xr-xr-x   5 root root 4096  4월 25  2008 root
dr-xr-xr-x   2 root root 4096  4월 25  2008 sbin
lrwxrwxrwx   1 root root    8  4월 22 11:05 tmp -> /etc/tmp
dr-xr-xr-x   7 root root 4096  4월 25  2008 usr
lrwxrwxrwx   1 root root    8  4월 22 11:05 var -> /etc/var


그냥 적절한 곳에다가 둔다.
[root@linux2 char]# cp fnd.o ~/root_xhyper255/etc

[root@linux2 etc]# ls fnd.o
fnd.o

파일시스템만들어주는 코드들을 복사한다.
[root@linux2 Filesystem]# ls -al
합계 14744
drwxrwxrwx   3 root root    4096  4월 22 11:05 .
drwxrwxrwx   8 root root    4096  2월 19  2010 ..
-rwxrwxrwx   1 root root   42087  4월 25  2008 mkfs.jffs2
-rwxrwxrwx   1 root root      73  4월 25  2008 r.sh
dr-xr-xr-x  15 root root    4096  4월 25  2008 root_xhyper255
-rwxrwxrwx   1 root root 6620848 10월  6  2009 root_xhyper255.tar.gz
-rw-r--r--   1 root root 8388608  2월 18  2010 rootfs.img
[root@linux2 Filesystem]# cp mkfs.jffs2 ~
[root@linux2 Filesystem]# cp r.sh ~

커널 모듈이 추가된 디렉토리를 압축해야 한다. r.sh를 사용한면 된다. 
내용을 보면, mkfs.jiffs2를 사용했다.. 

[root@linux2 ~]# ./r.sh
   내용
   #!/bin/sh
   ./mkfs.jffs2 -o rootfs.img -e 0x40000 -r root_xhyper255 -p -l


[root@linux2 ~]# ls rootfs.img
rootfs.img

이렇게 만든 root fs을 tftp로 복사한다.
[root@linux2 ~]# cp rootfs.img  /tftpboot/rootfs.img.my

target 보드를  컸다킨다.

kernel을 복사하고 굽는다.
XHYPER255B> tftp zImage_my2 kernel
No IP. Bootp start...
Our Ethernet address is 1234 5678 9A00.
        Sending bootp packet...
..
Bootp Packet received.
        Host   (server) Ethernet : 000C 29F2 5EB0
        Host   (server) IP       : 192.168.1.51
        Client (target) Ethernet : 1234 5678 9A00
        Client (target) IP       : 192.168.1.50

TFTP Start...
        Host   (server) IP       : 192.168.1.51
        Client (target) IP       : 192.168.1.50
        Loading Filename         : zImage_my2
        Save Address             : 0xA0008000

Loading start...
        0x000E2BC0 (928704) bytes received.
        tftp done.


XHYPER255B> flash kernel
Saving kernel to flash...
Erase flash blocks from 0x000C0000 to 0x001BFFFF.
        Erase Block at : 0x00180000.
        Done.
Write to flash...
        Writing at 0x001C0000...
        Done
XHYPER255B>

root fs을 복사하고 굽는다.
XHYPER255B> tftp rootfs.img.my root
TFTP Start...
        Host   (server) IP       : 192.168.1.51
        Client (target) IP       : 192.168.1.50
        Loading Filename         : rootfs.img.my
        Save Address             : 0xA0000000

Loading start...
        0x00800000 (8388608) bytes received.
        tftp done.

XHYPER255B> flash root
Saving root to flash...
Erase flash blocks from 0x001C0000 to 0x01FBFFFF.
        Erase Block at : 0x00340000.


참고로..
target 보드에서 리스타트를 하고 shell 접근하면.
/etc/fnd.o가 있는 것을 확인한다. 

보드에서 등록해 본다. 

[root@Xhyper255 /root]$insmod /etc/fnd.o
 fnd Module id Up....
 fnd:register_chrdev() ok! => major_num:253
fnd Module Insert Done!!
[root@Xhyper255 /root]$mknod /dev/FND c 253 0
[root@Xhyper255 /root]$lsmod
Module                  Size  Used by
fnd                     1648   0  (unused)


이 방법을 응용하면, /etc/initab을 이용하면 된다.

(운영체제 초기화 마지막단에 프로세스 작업을 /etc/inittab에서 진행한다.)
 참고로 /etc/inittab은  pid 0을 띄우고, pid 1, 2,3,4를 띄운다.

 
[root@linux2 etc]# pwd
/root/root_xhyper255/etc
[root@linux2 etc]# ls -al inittab
-r--r--r--  1 root root 1660  4월 25  2008 inittab


이미지를 굽기 전에 마지막에 여기에다가 insmod를 추가하면 될 것이다....
 




'c or linux' 카테고리의 다른 글

[공부중] Memory fs 테스트  (0) 2011.04.22
개념 정리  (0) 2011.04.22
[공부중] mmap 예제  (0) 2011.04.21
/proc 커널 모듈 테스트  (0) 2011.04.21
/proc/interrupts  (0) 2011.04.21
Posted by '김용환'
,

[공부중] mmap 예제

c or linux 2011. 4. 21. 16:54


개념


#include <sys/mman.h>

#define FND_SIZE (0x1000)
#define FND_PHYS_ADDR (0x14000000)   // physical 주소

void fnd_write(int val) 
{
*((unsigned char *)fnd_addr) = fnd_val[val];
*((unsigned char *)fnd_addr+1) = fnd_val[val];
printf("FND_Write : %d done!!\n", val);
}


...
fd = open("/dev/mem", O_RDWR|O_SYNC);
fnd_addr = mmap((void *)0,FND_SIZE,PROT_WRITE|PROT_READ,MAP_SHARED,fd,FND_PHYS_ADDR);
printf("FND I/O Address : %p\n", fnd_addr);
fnd_write(val);
munmap(fnd_addr, FND_SIZE);
close(fd);

...



(void *)0 : 가상주소는 알아서 해줘.
fnd_addr: 가상주소

코드

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <fcntl.h>


#include <sys/mman.h>


#define FND_PHYS_ADDR0 (0x14000000) 

#define FND_PHYS_ADDR1 (0x14100000) 

#define FND_PHYS_ADDR2 (0x14200000) 

#define FND_PHYS_ADDR3 (0x14300000) 


#define FND_SIZE (0x1000)


//#define FND (*((volatile unsigned short *)0xF1000000)) 


void * fnd_addr;


static char fnd_val[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x27,0x7f,0x67};


void fnd_write(int val) 

{

*((unsigned char *)fnd_addr) = fnd_val[val];

*((unsigned char *)fnd_addr+1) = fnd_val[val];

printf("FND_Write(%d) done!!\n", val);

}


int main(void)

{

    int retn;

    int fd;


    int flag = 0;

    int type;

int val=0;

int i;

off_t addr[] = {FND_PHYS_ADDR0, FND_PHYS_ADDR1,

FND_PHYS_ADDR2, FND_PHYS_ADDR3};

off_t FND_PHYS_ADDR;

    

    while(1)

    {

        printf("\n\n===== FND Test Program ====\n");

        printf(" 0. FND0 Test\n");

        printf(" 1. FND1 Test\n");

        printf(" 2. FND2 Test\n");

        printf(" 3. FND3 Test\n");

        printf(" 4. Program Quit\n");

        do {

            printf(" Select Menu => ");

            scanf("%d", &type);

        } while(type<0 || type>4);

        if(type == 4) break;


        while(1)

        {

            printf("\nFND_%02d Test...\n", type-1);

            do {

                printf("Select FND Write Value(0~9,10.Quit) => ");

                scanf("%d", &val);

            } while(val<0 || val>10);

            if(val == 10) break;


            //fd = open("/dev/FND", O_RDWR);

            fd = open("/dev/mem", O_RDWR|O_SYNC);

            if (fd<0) {

                perror("/dev/mem");

                exit(-1);

            }

            else printf("/dev/mem detected(fd:%d)...\n", fd);


FND_PHYS_ADDR = (off_t)addr[type];

fnd_addr = mmap((void *)0,

FND_SIZE,

PROT_WRITE|PROT_READ,

MAP_SHARED,

fd,

FND_PHYS_ADDR);

if(fnd_addr == NULL) {

                perror("mmap fail!!");

exit(-2);

}

printf("FND I/O Address : %p\n", fnd_addr);

    

            fnd_write(val);

munmap(fnd_addr, FND_SIZE);


            close(fd);

        }

    }

    

    return 0;

}





Posted by '김용환'
,


struct proc_dir_entry * my_proc_entry, create_proc_entry()를 사용하면, /proc에 넣을 수 있다.
proc_dir_entry에 read_proc, write_proc 함수 포인터 타입을 지정할 수 있다.


소스


#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

#include <linux/proc_fs.h>

int my_proc_read(char *page, char **start, off_t off,int count,int *eof, void *data_unused)
{
    int len;

    len = sprintf(page, "Hi~ I'm my_proc!!!\n");
    return len;
}

static int __init my_proc_init(void)
{
    struct proc_dir_entry * my_proc_entry;

    printk("MY_PROC Module Loading!!\n");

    if((my_proc_entry = create_proc_entry("my_proc", S_IRUGO , NULL)) == NULL)
        return -EACCES;

    my_proc_entry->read_proc = my_proc_read;

    return 0;
}

static void __exit my_proc_exit(void)
{
    printk("MY_PROC Module UnLoading!!\n");
    remove_proc_entry("my_proc", NULL);
}

module_init(my_proc_init);
module_exit(my_proc_exit);


실행


[root@Xhyper255 /root]$ls /proc

1    4   9        execdomains  kcore    mmc         slabinfo  version

11   43  bus      fb           kmsg     modules     stat

12   44  cmdline  filesystems  ksyms    mounts      swaps

2    5   cpu      fs           loadavg  mtd         sys

21   55  cpuinfo  interrupts   locks    net         sysvipc

223  6   devices  iomem        meminfo  partitions  tty

34   7   driver   ioports      misc     self        uptime

[root@Xhyper255 /root]$insmod my_proc.o

MY_PROC Module Loading!!

[root@Xhyper255 /root]$ls /proc

1    4   9        execdomains  kcore    mmc         self      uptime

11   43  bus      fb           kmsg     modules     slabinfo  version

12   44  cmdline  filesystems  ksyms    mounts      stat

2    5   cpu      fs           loadavg  mtd         swaps

21   55  cpuinfo  interrupts   locks    my_proc     sys

225  6   devices  iomem        meminfo  net         sysvipc

34   7   driver   ioports      misc     partitions  tty


[root@Xhyper255 /root]$cat /proc/my_proc

Hi~ I'm my_proc!!!






다른 예제


#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <asm/uaccess.h> // copy_from_user
#include <linux/proc_fs.h>

struct proc_dir_entry * time_dir, *my_dir;

    
int my_proc_read(char *page, char **start, off_t off,int count,int *eof, void *data_unused) {
    int len1, len2;
    len1 = sprintf(page, "Hi~ read!!!\n");
    len2 = sprintf(page + len1, "Hello!!!\n");
    return len1 + len2;
}


int my_proc_write(struct file *file, const char *buffer, unsigned long count, void *data) 
{
  char input;
copy_from_user(&input, buffer, count);
    printk("User data : %c\n", input);
    return count;

}


static int __init my_proc_init(void)
{

struct proc_dir_entry *my_proc_entry;
    printk("my_proc loading!!\n");

time_dir = proc_mkdir("time", NULL); // directory
    if((my_proc_entry = create_proc_entry("my", 0644 , time_dir)) == NULL) {//file
        return -EACCES;
    }

    my_proc_entry->read_proc = my_proc_read;
my_proc_entry->write_proc = my_proc_write;

    return 0;
}

static void __exit my_proc_exit(void)
{
    printk("MY_PROC Module UnLoading!!\n");
    remove_proc_entry("my", time_dir); // file
    remove_proc_entry("time", NULL); // directory 
}

module_init(my_proc_init);
module_exit(my_proc_exit);


실행


[root@Xhyper255 /root]$insmod my_proc.o

my_proc loading!!

[root@Xhyper255 /root]$echo 1 > /proc/time/my

User data : 1

[root@Xhyper255 /root]$cat /proc/time/my

Hi~ read!!!

Hello!!!





* 주의점
 

int my_proc_read(char *page, char **start, off_t off,int count,int *eof, void *data_unused) {
    int len;
    len = sprintf(page, "Hi~ read!!!\n");     
    len = sprintf(page, "Hello!!!\n");
    return len;
}

이렇게 하면 override되기 때문에 문제가 된다.

int my_proc_read(char *page, char **start, off_t off,int count,int *eof, void *data_unused) {
    int len1, len2;
    len1 = sprintf(page, "Hi~ read!!!\n");
    len2 = sprintf(page + len1, "Hello!!!\n");
    
    return len1 + len2;
}

이렇게 고쳐야 한다.

Posted by '김용환'
,

/proc/interrupts

c or linux 2011. 4. 21. 14:54



[root@Xhyper255 /root]$cat /proc/interrupts
  0:          0   eth0
  2:          0   GPIO 2-80
  3:          0   PXA USB core
  6:         42   AC97
 14:      15160   serial
 15:          9   MMC controller
 17:          0   DMA
 18:    2070369   timer
 19:          0   fnd
 25:          0   PXA PCMCIA CD
 27:          0   ads7843
 39:          0   PXA CF CD
Err:          0

첫번째 컬럼 : 내부에서 관리하는 unique 번호
두번째 컬럼 : 호출된 빈두수
세번째 컬럼  : 커널 모듈에서 register한 등록된 이름

'c or linux' 카테고리의 다른 글

[공부중] mmap 예제  (0) 2011.04.21
/proc 커널 모듈 테스트  (0) 2011.04.21
[공부중] bottomhalf & work queue  (0) 2011.04.21
[공부중] queue_task 함수에서 tq_immediate 사용하기  (0) 2011.04.21
[공부중] queue_task 예제  (0) 2011.04.21
Posted by '김용환'
,


운영체제에 사용.
interrupt handler 에서 너무 많은 일을 하지 않게 하기 위해서 사용.
즉, 순차적인 구간 중에.. copy 뜨는 부분을 지연하고 싶은 경우가 있다.
이를  bottomhalf라고 한다.

지연하지 않아도 되는 것은 top half 라고 한다.

 linux 2.6에서는 2.4의 bottomhalf 기능을 없애버렸다.

대신 work queue를 쓰면 된다. 우선 순위 높힌 커널 thread로 실행한다.


DECLARE_WORK(work_queue, call_workqueuefunc, NULL);

void call_workqueuefunc(void *data) {
}

workqueeu_interrupt(..) {

   schedule_work(&work_queue);
   wake_up_interruptible(&waitqueue);
}




'c or linux' 카테고리의 다른 글

/proc 커널 모듈 테스트  (0) 2011.04.21
/proc/interrupts  (0) 2011.04.21
[공부중] queue_task 함수에서 tq_immediate 사용하기  (0) 2011.04.21
[공부중] queue_task 예제  (0) 2011.04.21
[공부중] poll 함수 예제  (0) 2011.04.21
Posted by '김용환'
,

<linux 2.4>

queue_task 함수에서 tq_immediate를 쓸려면, mark_bh 라는 bottomhalf 함수를 호출해야 한다.
강제적으로 sw int를 생성하는 함수이다.

void ost1_interrupt(int irq, void *dev_id, struct pt_regs *regs)

{

OST1_TIMER_OFF;

    printk("OST1 Int enter ...\n");

    flag_ost1Int = 1;

tq_ost1.routine = tq_timer1;

tq_ost1.data = (void *) 10;

queue_task(&tq_ost1, &tq_immediate);

mark_bh(IMMEDIATE_BH); 

    printk("OST1 Int exit...\n");

}


'c or linux' 카테고리의 다른 글

/proc/interrupts  (0) 2011.04.21
[공부중] bottomhalf & work queue  (0) 2011.04.21
[공부중] queue_task 예제  (0) 2011.04.21
[공부중] poll 함수 예제  (0) 2011.04.21
[공부중] blocking io 와 waiting queue  (0) 2011.04.21
Posted by '김용환'
,

<linux 2.4>

Os Timer 보다 더 빠른 처리가 필요하다. tick interrupt를 사용해야 하는 게 좋다.

----
arm의 경우 interrupt의 우선순위를 본다.
reset : 1
s/w interrupt : 6  (시스템 콜) <- app
irq : 4


struct tq_struct 을 이용해서  int queue_task()를 사용한다.

tq_timer와 tq_immediate 가 크게 사용되는데. tq_timer를 많이 사용한다.
tq_immediate는 언제 수행될지 모른다. 즉 시스템콜을 사용하지 않으면  수행을 보장 받을 수 없어서. tq_timer를 많이 사용한다.





DECLARE_WAIT_QUEUE_HEAD(WaitQueue_Read);


struct tq_struct tq_ost1;
 

// 여기서는 block을 해도 된다. sleep을 써야 한다.
void tq_timer1(void * data)
{
       int i ;
for (i = 1 ; i< (int) data ; i++) {
  printk("TaskQueue Funcion_ %d..\n", i);
                msleep(100);
}
wake_up_interruptible(&WaitQueue_Read);
}

// 여기서는 block을 하면 안된다. sleep 함수를 쓸 수 없다. 대신 delay를 쓰면 된다.
void ost1_interrupt(int irq, void *dev_id, struct pt_regs *regs) {

    OST1_TIMER_OFF;
    printk("OST1 Int enter ...\n");
    flag_ost1Int = 1;
    tq_ost1.routine = tq_timer1;
    tq_ost1.data = (void *) 10;
    queue_task(&tq_ost1, &tq_timer);
    printk("OST1 Int exit...\n");
}




* 참고
void * 형 타입에 int 값을 지정하고 파라미터로 넘길 때... 유의할 점이 있다.
struct tq_strcut 의 data 의 값은 void * 이다.  여기서 int로 넘겨본다.
 
1. 먼저 casting 한다.  
tq_ost1.data = (void *) 10;

2. 이 값으로  내용을 파라미터로 받아서 , 원래 받는 쪽에서 사용하면 된다.
int a = (int) data ;


* 참고
sleep 은 blocking 함수
delay은 nob-blocking 함수

Posted by '김용환'
,


여러개의 fd를 사용하는데, 둘 이상 의 fd에서 io에서 일어나면 처리되는 것
int poll 함수를  입출력 다중화 함수라고 한다.


write를 하려고 할때, 데이터가 모든 것이 올때까지 기다린 다음에 write를 해야 한다.
이때 poll을 쓰는 것이다.


어플 코드


   struct pollfd Events[2];

    Events[0].fd = fd_fnd;
    Events[0].events = POLLIN; //waiting read
    Events[1].fd = fd_dot;
    Events[1].events = POLLOUT; //waiting write


    retval = poll(Events, 2, 7000);    // Event waiting


    ioctl(fd_fnd, 3, timerPeriod);
    ioctl(fd_dot, 2, timerPeriod);

    if(retval < 0) {
            perror("Poll_Test");
            exit(EXIT_FAILURE);
        }
        if(retval == 0) {
            printf("[APP]Wakeup_Poll_Event:No Event!!\n");
            continue;
            //exit(EXIT_FAILURE);
        }


// compare expected event with returned event
        if(Events[0].revents & POLLIN)
        {
            if(read(Events[0].fd, (void *)&data, sizeof(data)) > 0) 
                printf("[APP]Wakeup_Poll_Event:Event_FND(%x)!!\n", data);
            else 
                printf("[APP]Wakeup_Poll_Event:Event_FND(Data Read Error)!!\n");
        }

        if(Events[1].revents & POLLOUT)
        {
            dot_data = 1;    // dot_show_no
            if(write(Events[1].fd, (void *)&dot_data, sizeof(dot_data)) > 0) 
                printf("[APP]Wakeup_Poll_Event:Event_DOT!!\n");
            else 
                printf("[APP]Wakeup_Poll_Event:Event_DOT(Data Write Error)!!\n");
        }





커널모듈 소스


ssize_t fnd_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
    short data;
    printk("OST1 Int. Waiting(%d)...\n", flag_ost1Int);

    if(!flag_ost1Int) {
        if(!(filp->f_flags & O_NONBLOCK)) {
            interruptible_sleep_on(&WaitQueue_Read); // waiting..
            printk("[DD]FND_Read_WakeUp!!\n");
        } else {
            return -EAGAIN;
        }
    }

data = FND_CS0;

        printk("OST1 Int Happened(%04x)...\n", data);
copy_to_user((void *)buf, (void *)&data, count);
flag_ost1Int = 0;
return(count);
}




DECLARE_WAIT_QUEUE_HEAD(WaitQueue_Read);

void ost1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    OST1_TIMER_OFF;
    flag_ost1Int = 1;
    wake_up_interruptible(&WaitQueue_Read); // wakeup..
}



unsigned int fnd_poll(struct file *filp, struct poll_table_struct *pt) {
int mask;
poll_wait(filp, &WaitQueue_Read, pt); // interruptible_sleep_on..
if (flag_ost1Int == 1) {
mask |= POLLIN | POLLRDNORM;
}
return mask;
}



그러면, OS_TIMER는 언제 넣지??
app에서 사용될 경우를 살펴본다.
ioctl 3으로 쓰고, poll 함수를 쓴다.


        ioctl(fd_fnd, 3, timerPeriod);
        retval = poll(Events, 2, 7000);    // Event waiting




따라서,  ioctl cmd를 3을 넘겼을 때, 처리할 OST1_TIMER_ON을 사용하도록 한다.



int fnd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
... 
switch(cmd)
{
 
        case 3:  
            printk("\ncmd = 3\n");
            timerPeriod = arg;
            timerClkVal = timerPeriod*SEC_CLK;
            OST1_TIMER_ON;
            break;
}
... 
}





*참고


아래 두 코드는 동일하다..


if (flag_dotInt) {
printk("                              [DD]DOT_Write_Wait...\n");
     retstate = interruptible_sleep_on(&WaitQueue_Write);
     if(retstate) return retstate;
     printk("                              [DD]DOT_Write_Wakeup...\n");
}

  

wait_event_interruptible 함수는 sleep을 시킬 수 있는, 조건문이 있는 매크로이다


printk("                              [DD]DOT_Write_Wait...\n");
retstate = wait_event_interruptible(WaitQueue_Write, flag_dotInt);
if(retstate) return retstate;
     printk("                              [DD]DOT_Write_Wakeup...\n");



 


Posted by '김용환'
,


interrupt handler 안에서는 blocking을 유발하는 함수를 쓰지 않는다. 빠르게 처리되어야 한다.
다른 곳까지 영향을 미칠 수 있다. 예를 들어 kmalloc 함수 호출시 block이 된다.

즉, 아래와 같은 코드 예제에서.. interrupt가 실행되어 특정 변수에 그 값을 저장하고, read에서는 계속 while 문으로 돌아가게 하는 것은 block되게 한다는 의미이다. 


volatile unsigned int flag_ost1_int = 0;

void ost1_interrupt(int irq, void *dev_id, struct pt_regs *regs) {
printk("os timer1 interrupt\n");
OST1_TIMER_OFF;
flag_ost1_int = 1;
}


ssize_t fnd_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
short data;

OST1_TIMER_ON;
while (!flag_ost1_int) {
;
}
}



문제를 해결하는 방법은 두가지가 있다. 하나는 application에서 nonblock 모드로 open하는 경우,
다른 두번째는 blocking io를 쓰더라도 다른 프로세스에 영향을 주지 않도록 대기큐를 이용해서 sleep 과 wakeup 을 사용해야 하는 방법이다.

첫번째 방법 nonblock 모드를 쓰는 경우이다.

sleep 때문에 영향을 주기 때문에 application 에서 noblock에서 써서 1초간격으로 sleep되게 할 수 있다..

어플 코드
 

        //fd = open("/dev/FND", O_RDWR);
      ->   fd = open("/dev/FND", O_RDWR|O_NONBLOCK);



            //if(read(fd, (void *)&data, sizeof(data))) break;
        ->    if(read(fd, (void *)&data, sizeof(data)) > 0) break;
               sleep(1);


 

커널 모듈 코드


ssize_t fnd_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
short data;

    printk("OST Int. Waiting(%d)...\n", flag_ostInt);


OST_TIMER_ON;

    //while(!flag_ostInt) {
    // ;
    //}
// -> 
    while(!flag_ostInt) {
        if(filp->f_flags & O_NONBLOCK) {
            printk("O_NONBLOCK(%d)!\n", -EAGAIN);
            return -EAGAIN;
        }
    
   }


        
두번째 방법이다.  application이 아닌 내부에서 처리가 되게 해야 한다.

어플 코드 

fd = open("/dev/FND", O_RDWR);



커널 모듈 소스
대기 큐를 하나 생성하고 block 모드일때는 sleep 시킨다.



#include <linux/wait.h>
DECLARE_WAIT_QUEUE_HEAD(wq_read);

ssize_t fnd_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
short data;

    printk("OST Int. Waiting(%d)...\n", flag_ostInt);


OST_TIMER_ON;

    //while(!flag_ostInt) {
    // ;
    //}
// -> 
    while(!flag_ostInt) {
        if(filp->f_flags & O_NONBLOCK) {
            printk("O_NONBLOCK(%d)!\n", -EAGAIN);
            return -EAGAIN;
        }
        
        // if not nonblock, use waiting queue
        interruptible_sleep_on(&wq_read);  // cpu를 놓아둔다.
    }

    data = FND_CS0;
    
    printk("OST Int Happened(%04x)...\n", data);
    copy_to_user(buf, &data, count);


     flag_ostInt = 0;
return(count);
}



그리고, interrupt 코드안에 wake_up코드를 추가한다.

void ost_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
printk("OST Int...\n");
OST_TIMER_OFF;
    flag_ostInt = 1;
    wake_up_interruptible(&wq_read);
}



'c or linux' 카테고리의 다른 글

[공부중] queue_task 예제  (0) 2011.04.21
[공부중] poll 함수 예제  (0) 2011.04.21
[공부중] header 파일 -irqs.h  (0) 2011.04.20
CPU 구성요소  (0) 2011.04.20
[공부중] OS Timer 예제 - interrupt  (0) 2011.04.20
Posted by '김용환'
,