관련된 이론 분야 : 운영체제론

 

크리티컬 섹션(임계구역) / 뮤텍스(상호배제) / 세마포어

이것들은 한양대 컴공과 3학년 1학기 과정에 있었던 운영체제론의 핵심적인 내용이었습니다.

허신 교수님의 운영체제론 수업을 들었었는데 이론의 맥을 잘 짚어주셨던 기억이 나네요.

 

열심히(?) 공부해서 시험도 보고 성적도 좋았지만

막상 그 개념들을 실제로 적용해볼 기회가 없으니 개념이 좀 두리뭉실해서 한 번 정리해 보았습니다.

 

제가 이해한 내용을 복습하기 위해 정리한 것이라서

틀릴 수도 있으니 많은 성원 바랍니다 (뭔 소리야 ㅋㅋ)

 

모두 동기화 오브젝트에 관련된 이론인데...

이 이야기를 할 때면 어김없이 나오는게 호프집 화장실에서 똥싸는 이야기더군요 - _ -;;

 

오늘의 수업은 이름하여...

 

미쉘린의 호프집 화장실 이론

 

1. 크리티컬 섹션 (임계구역), Critical Section

 

호프집에 화장실이 있습니다.

이 화장실에는 변기가 하나밖에 없습니다.

그래서 한 번에 한 사람씩 들어가야 되지욤...

(화장실 안에 서서 기다리면서 누가 똥싸고 오줌싸는걸 지켜볼 수는 없는 노릇)

 

이 변기가 크리티컬 섹션입니다.

프로그래밍할 때에는 주로 CS (Critical Section)라고 언급됩니다.

MFC 에는 크리티컬 섹션에 대한 클래스가 정의되어 있어서 다음과 같이 사용할 수 있습니다.

 

CCriticalSection RestRoom;

RestRoom.Lock();

용변보기

RestRoom.Unlock();

 

Lock() 과 Unlock() 사이의 구간이 크리티컬 섹션입니다.

스레드에서 이렇게 정의한 부분은

한 번에 하나의 스레드만 접근할 수 있습니다.

(스레드에 대한 이야기는 제 지난 컴퓨터 이론 포스트를 참고하시기를...)

 

호프집에 동수, 일구, 빠박이가 있습니다.

셋이서 신나게 술을 마십니다.

어느순간 세사람 모두 오줌이 마렵습니다.

 

동수일행 프로세스

 

CCriticalSection RestRoom;   // 전역변수

 

동수 스레드

{

  일구, 빠박이랑 술마시기

  if ( 오줌마렵다 )

  {

      RestRoom.Lock();

      용변보기

      RestRoom.Unlock();

  }

}

 

일구 스레드

{

  동수, 빠박이랑 술마시기

  if ( 오줌마렵다 )

  {

      RestRoom.Lock();

      용변보기

      RestRoom.Unlock();

  }

}

 

빠박이 스레드

{

  동수, 일구랑 술마시기

  if ( 오줌마렵다 )

      RestRoom.Lock();

      용변보기

      RestRoom.Unlock();

  }

}

지난 포스트에

하나의 주 프로세스가 여러 개의 스레드를 생성하여

CPU 상에서 여러 개의 스레드가 돌아간다는 이야기를 했습니다.

 

동수, 일구, 빠박이는 모두 각각의 독립적인 스레드입니다.

프로그램이 종료할 때까지 (호프집에서 나갈 때까지)

항상 CPU 상에서 돌아가고 있지요.

 

술마시다가 어느순간 세 사람 모두 오줌이 마렵습니다.

 

동수가 먼저 화장실로 냅다 달립니다.

 

동수 : 일구야 ~ 형 먼저 갈께 ㅜ0ㅜ

일구 : 아 ㅅㅂ~~~

빠박이 : 가지마 ~~~~~

 

동수가 먼저 가서 용변을 봅니다.

화장실은 한 사람 밖에 들어갈 수 없기 때문에

동수가 화장실에 들어가는 순간 RestRoom.Lock() 이 됩니다.

 

RestRoom.Lock() 걸리면 다른 스레드는

RestRoom.Lock() 구간에 서서 Unlock() 이 될 때까지 무작정 기다립니다.

...

일구랑 빠박이는 화장실 문앞에 서서 기다립니다.

 

동수가 용변보고 나오면 RestRoom.Unlock() 이 됩니다.

그럼 이제 일구랑 빠박이중 누군가 먼저 들어간 사람이

RestRoom.Lock() 을 다시 호출하겠지요.

 

요 화장실 RestRoom Lock() ~ Unlock() 구간이 바로 CS ..... 크리티컬 섹션 입니다.

 

2. 뮤텍스, Mutex

 

뮤텍스의 용도는 기본적으로 CS 와 같습니다.

 

간단하게... 어렵지 않게... 헷갈리지 않게...

차이점만 짚어봅시다.

 

CS 는 단일 프로세스의 스레드에 대해서만 동작합니다.

반면에, 뮤텍스는 여러 프로세스의 스레드에 대해서도 동작합니다.

 

동수 일행이 호프집에 들어오는 순간 3 개의 스레드가 생성됩니다.

(동수 / 일구 / 빠박이 스레드)

 

엇 ??

근데 또 다른 일행이 호프집에 들어왔습니다.

봉팔 일행입니다. (봉팔 / 상팔 / 영팔 ) - 이름만 다를 뿐 동수 일행과 똑같은 프로세스 입니다.

(메모리상에 똑같은 프로그램의 프로세스 2 개가 올라왔다고 생각하면 됩니다. 두 번 실행됐다는 말)

얘들이 들어오면서 또 3 개의 스레드가 생성됩니다.

 

메모리상에는 6 개의 스레드가 돌아가고 있습니다.

이제...

( 동수 / 일구 / 빠박이 ) // ( 봉팔 / 상팔 / 영팔 )

각각의 스레드가 동시에 화장실에 가지 못하도록 해야 합니다.

 

이 때, 뮤텍스를 사용합니다.

CS 를 사용하게 되면 중복된 리소스 점유가 발생할 수 있기 때문입니다.

위 코드를 보면

RestRoom 은 동수 일행 프로세스의 전역 변수로 선언되어 있습니다.

그래서 동수 일행끼리는

"동수가 화장실에 갔으니까 일구랑 빠박이는 화장실에 갈 수 없다"

는 사실을 전역변수 CCriticalSection RestRoom 을 통해 명시적으로 알 수 있습니다.

 

하지만 봉팔 일행은 동수 일행의 RestRoom 과는 또다른

그들만의 RestRoom 을 가지고 있으므로

동수 일행중 누군가가 화장실에 있는지 여부는 판단할 수 없습니다.

그래서...

"지금 화장실에 아무도 없으니까 누구든지 가도 된다" 는 결정을 내립니다.

 

봉팔이가 화장실로 달려가는 순간

화장실 문이 열리고

 

동수가 용변보는 모습을 발견합니다 !!

봉팔 : (엇.......!! 아무도 없는줄 알았는데 누가 있잖아 !!)

 

결국 봉팔이는 용변을 못 보고 예기치못한 사태에 멈춰 서 있게 됩니다.

(실제로 이런식으로 프로그래밍을 하면 런타임 오류가 나지 않을까 생각되네요... 실제로 안해봐서 생각만...;;)

 

3. 세마포어, Semaphore

 

복잡하니 쉽게 갑시다...

세마포어는 CS, 뮤텍스가 가지는 특징에 하나를 더 가집니다.

 

세마포어는 특정 영역의 코드를 실행하는 스레드의 최대 개수를 설정할 수 있습니다.

 

지금은 1인용 화장실을 언급하였지만...

만약 호프집에서 화장실을 증축하여...

4인용 화장실이 되었다면...

이제 4명까지 화장실에 들어갈 수 있습니다.

 

이런 카운터를 세마포어로 구현할 수 있고

 

4인용 화장실

CSemaphore semaphore(0, 4);

첫번째 매개변수 : 리소스 카운터의 초기값

두번째 매개변수 : 리소스 카운터의 최대값

 

다시 돌아가서...

1인용 화징실에 열쇠라는 개념을 도입하여...

 

1인용 화장실

CSemaphore key(0, 1);

 

이런식으로도 활용이 가능합니다.

 

음...

여기에 또 문제가 생기는데

 

만약 누군가 1인용 화장실의 열쇠(세마포어)를 가지고 도망가버리면

아무도 화장실에 들어갈 수 없는 문제가 생깁니다.

 

이건 데드락이라고 하는건데...

 

오늘은 여기까지 !

다음 기회에 ~~~~~~~~~

 

p.s : 제 생각을 정리한 내용이니 틀린 내용이 있을 수 있습니다 ;;;;;;


출 처 : http://blog.naver.com/thx4alice?Redirect=Log&logNo=110022369987

하드웨어 상태를 주기적으로 감시할때 정해진 시간이 초과되면 특정 함수를 수행하는 기능을 한다. 리눅스 커널은 타이머 인터럽트가 발생하며 스케줄링에 필요한 처리를 끝낸 후 커널 타이머 목록이라는 데이터 구조를 검사하며, 이 커널 타이머 목록은 수행할 함수와 처리되어야 할 시간에 대한 정보가 담긴 연결 리스트이다.

커널 타이머 이용시

struct timer_list : 커널 타이머 구조체
init_timer(): 커널 타이머 구초제를 초기화 한다
add_timer(): 커널 타이머에 수행될 함수를 등록한다
del_timer(): 커널 타이머 목록에서 등록된 것을 제거한다.

커널 타이머는 동작 시간이 1/Hz 단위로 1/Hz초 이하의 호출 주기는 사용이 불가능 하다

다음은 커널 2.6 버젼에서의 타이머 동작이다.


struct time_list 변수는 
1. unsigned long expires, 2 unsigned long data, void (*function)(unsigned long)을 일반적으로 지정하여 사용한다.
1. unsigned long expires 는 다음과 같이 초기화 한다
 kerneltime.expires=get_jiffies_64()+(3*HZ/10);
 (현재 jiffies값을 얻어서 0.03초가 지난후로 설정)
2. unsigned long data 는 timer 함수에 전달할 데이터를 참조하기 위한 주소를 리턴한다.
3. void (*function)(unsigned long)는 타이머 시간이 만료하고 수행할 함수이다.

init_timer는 timer 구조체를 초기화 한다. 앞에서 설명한 expires,data,function 필드를 초기화 한다.

add_timer는 커널 타이머에 호출될 timer_list 구조체를 등록한다.

del_timer는 커널 타이머를 제거한다. 일반적으로 커널 타이머가 등록되고 시간이 초과하여 커널 타이머에 등록된 함수가 호출되면 등록된 커널 타이머는 자동적으로 제거된다. 하지만 디바이스 드라이버는 등록된 커널 타이머가 확실하게 후출되어 제거될 것이라는 확신이 있더라도 모듈 형식으로 디바이스 드라이버를 작성할 경우 디바이스 드라이버의 종료 루틴에 del_timer() 함수를 호출하여 모듈이 제거 되었을때 제거된 번지에 속해 있던 함수가 호출되지 않도록 하여야 한다.

커널 타이머 예
1초마다 메시지를 출력한다
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/time.h>
#include <linux/timer.h>
#define   TIME_STEP                       (1*HZ)
struct timer_list  *timer;
static int timertick=0;
void kerneltimer_timeover(unsigned long arg ); <--Timerover 시 실행할 함수
void kerneltimer_registertimer(struct timer_list* ptimer, unsigned long timeover )
{
     init_timer( ptimer );
     ptimer->expires  = get_jiffies_64() + timeover;
     ptimer->data     = NULL;
     ptimer->function = kerneltimer_timeover       ;

     add_timer( ptimer);
}
void kerneltimer_timeover(unsigned long arg )
{
        printk("I am timer tick:%d\n",timertick);
        timertick++;
        kerneltimer_registertimer( timer, TIME_STEP ); <--timerover 하면 재 등록
}
int kerneltimer_init(void)
{
    timer= kmalloc( sizeof( struct timer_list ), GFP_KERNEL );
    if( timer== NULL ) return -ENOMEM;
    memset( timer, 0, sizeof( struct timer_list) );
    kerneltimer_registertimer( timer,TIME_STEP );
    return 0;
}
void kerneltimer_exit(void)
{
    if( timer!= NULL )
    {
        del_timer( timer) ;
        kfree( timer);
    }
}
module_init(kerneltimer_init);
module_exit(kerneltimer_exit);
MODULE_LICENSE("Dual BSD/GPL");



인터럽트를 처리하기 위해선 Linux에선 3가지 방법이 있습니다.

softirq, tasklet, work queue

가 그것이죠.

인터럽트란 HW적으로 CPU에 interrupt를 만들어 무엇인가 처리해야 할일을 처리하게끔(?) 만드는 역할을 합니다. 키보드를 누를때도 인터럽트가 발생하고, USB에 장치를 연결해도, 심지어 time tick을 처리하기 위해서도 1초에 300번정도(커널설정에 따라 변경 가능(i.e. 1000)) HW interrupt가 발생합니다. HW적으로 인터럽트핀이 CPU를 깨워서 인터럽트가 걸리면 맨앞 핸들러로 뛰어서 어떤인터럽트가 걸렸는지 확인하는 루틴이 실행됩니다. 이 루틴안에서 어떤 인터럽트인지 확인하고 다음처리를 담당하게 되죠. 그러나 주위해야하는점은 어떤 HW 인터럽트가 먼저 걸렸을때엔 다른 HW 인터럽트가 걸리지 않게 됩니다(인터럽트 pin을 SW적으로 내리기 전까진). 즉,응답성을 위해선 인터럽트 핸들러에선 정말 간단하게 어떤 인터럽트인지만 확인하고, 바로 인터럽트를 해제하지 않으면 그사이엔 어떠한 HW 인터럽트가 발생되지 않아 시스템의 응답성이 떨어지게 되겠죠.. 바로 HW인터럽트를 받아서 인터럽트핀을 해제하고 약간 지연되게 처리하는 방법이 위의 3가지라고 보시면 됩니다.

softirq

32가지 미리 정의된 인터럽트들을 정의하고, 인터럽트 핸들러가 종료되서 인터럽트 가능한 상태로 바뀌면 바로 softirq가 실행된다. 가장 높은 우선순위를 가지고 있으며, 고정된 인터럽트들을 처리하기 위해서 사용합니다. 현재 커널은 32가지중 6가지만 사용합니다. 종류는 HI_SOFTIRQ(높은 우선순위), TIMER_SOFTIRQ(타이머), NET_TX_SOFTIRQ(네트워크 패킷송신), NET_RX_SOFTIRQ(네트워크 패킷수신), SCSI_SOFTIRQ(SCSI), TASKLET_SOFTIRQ(태스크릿들)이고, HI_SOFTIRQ가 우선순위가 가장높게 처리됩니다.

tasklet
동적으로 할당된 softirq라고 생각하면 됩니다. 위 softirq의 SCSI_SOFTIRQ가 처리되고나서 TASKLET_SOFTIRQ가 처리되는데 이때엔 동적으로 할당을 받아서 처리하고 싶은 handler를 등록하고 처리하게 됩니다.

work queue
softirq나 tasklet들이 실행중에는 sw인터럽트중이라, hw 인터럽트는 가능하지만, 여전히 sw적으로 우선적으로 처리되는것들입니다. 즉 softirq중에는 user process들이나, context switch등은 처리되지 않고 있겠죠. 모든 sw irq를 softirq로만 처리한다면 user process나 context switch등은 처리안되어 유저입장에선 상당히 반응성이 안좋은 커널이라 생각할수 있습니다. 그래서 context switch등과 동일한 level에서 스케쥴링되는 work queue를 두어 sw interrupt들이지만 상당히 시간을 요하거나 계산해야될게 많은 작업들 혹은 휴면이 필요한 I/O작업이 필요한 일들은 context switch될때 같이 되게 만들어 주어 커널 응답성을 좋게 해줄수 있습니다.

출처 : http://kldp.org/node/112272

디바이스 제어 ioctl() 함수의 역할

저수준 파일 입출력 함수인 ioctl()을 디바이스 파일에 적용시키면 디바이스 파일에 연결된 디바이스 드라이버의 파일 오퍼레이션 구조체의 ioctl 필드에 선언된 함수가 호출된다. ioctl() 함수는 디바이스 파일 이외에는 사용할 수 없는 디바이스 파일 전용 함수이므로 각 디바이스마다 고유하게 선언하여 사용한다. 그래서 read(), write()함수와 같이 정형화된 형태를 기본적으로 유지하지만, 사용 방법은 디바이스마다 모두 다르다. 저수준 입출력 함수인 ioctl() 함수와 디바이스 드라이버에 선언되는 ioctl() 함수의 매개변수 전달 관계는 아래와 같다.


 

위 그림과 같이 request는 응용 프로그램이 디바이스 드라이버에게 요구하는 명령어고, 이 request에 대입된 값은 디바이스 드라이버의 매개변수 cmd에 그대로 전달된다. 이 값은 디바이스 드라이버 작성자가 임의로 결정하는 사항이다. 하드웨어와 구현되는 디바이스 드라이버에 따라서 값이 전혀 다를 수 있다. 또 동일한 값에 대해 디바이스 드라이버마다 다르게 해석할 수도 있다. 매개변수 argp는 request에 따라서 선언해도 되고 선언하지 않아도 되는 가변 인자 매개변수다. 이 값은 request에 종속되어 디바이스에 전달한 정보를 담은 구조체 변수의 선두 주소를 전달하거나 디바이스 드라이버에서 얻고자 하는 정보를 담아올 수 있는 구조체 변수의 선두 주소를 전달한다. 특별한 경우에는 단순하게 상수값으로 사용할 수도 있다. argp 역시 어떻게 사용될 것인지는 디바이스 드라이버 작성자의 결정에 따라 달라진다.

 

ioctl() 함수의 특징

   read(), write() 함수와 같이 쓰기와 읽기 처리가 가능

   하드웨어의 제어나 상태를 얻기 위해 사용

   응용 프로그램의 명령에 따라 디바이스 드라이버의 매개변수 해석이 달라짐

 

 

ioctl() 함수의 일반적인 형태


ioctl() 동작 개념

 

응용 프로그램에서 ioctl()함수를 이용하여 하드웨어를 제어하거나 상태를 읽기 위해서는 디바이스 파일을 제어하는 디바이스 드라이버에서 해석 가능한 명령과 구조체를 사용해야 한다. 프로그램을 구현하기 위해 ioctl() 함수를 사용할 때는 공통된 헤더 파일을 사용하게 되는데, 이 헤더 파일에는 ioctl에 전달되는 명령에 대한 선언과 명령을 처리하는 보조적인 정보를 주고받기 위한 구조체가 선언되어 있어야 한다. 디바이스 드라이버의 ioctl() 함수는 파일 오퍼레이션 구조체에 선언된 ioctl()함수가 호출되며, 이때 전달되는 매개변수값은 응용프로그램의 ioctl에서 전달하는 값을 그대로 전달받는다. 응용 프로그램에서 사용하는 매개변수는 응용 프로그램의 request와 argp에 해당하는 값만 전달 받는다. 디바이스 드라이버의 ioctl() 함수는 가장 먼저 전달된 cmd 명령이 유효한가를 확인한다. 전달된 명령이 유효한지 아닌지를 확인하기 위해 _IOC_NR과 _IOC_TYPE이라는 매크로 함수를 사용한다. 전달된 명령이 더 이상 유효하지 않으면, 즉 처리할 수 없는 명령일 경우엔 EINVAL이라는 음수값을 반환한다. 이와는 반대로 전달된 명령어가 정상적인 명령일 경우에는 명령이 읽기를 요구하는지 쓰기를 요구하는지를 검사한다. 이 검사는 _IOC_DIR이라는 매크로를 이용한다. 이 검사를 하는 주된 목적은 사용자 모드에서 메모리의 유효성을 검사하기 위해서이다. 이렇게 기본적인 검사가 끝났다면 switch문을 사용하여 명령을 구분하고 각 명령에 따른 처리를 구현하면 된다.

 

ioctl()에 전달되는 cmd와 관련 매크로 함수

cmd의 구성

디바이스 드라이버의 ioctl() 함수에 전달되는 매개변수 cmd는 응용 프로그램이 디바이스 드라이버에게 요구한 처리를 구별하기 위한 구별값이다. cmd에는 단순한 구별 숫자 이외에 처리에 도움을 주는 몇 가지 정보를 포함한 형태로 구성된다. cmd의 크기는 32비트로, 비트 구성은 다음과 같다.


 

매크로 함수

cmd에는 여러 가지 필드가 있다. 리눅스 커널은 이런 필드 형식에 맞춰 cmd 상수값을 만드는 매크로 함수와 cmd 상수값에서 필요한 필드값을 추출하는 매크로 함수를 제공한다.

 

cmd 명령을 만드는 매크로 함수

   _IO : 부가적인 데이터가 없는 명령을 만드는 매크로

   _IOR : 디바이스 드라이버에서 데이터를 읽어오기(R) 위한 명령을 만드는 매크로

   _IOW : 디바이스 드라이버에서 데이터를 써넣기(W) 위한 명령을 만드는 매크로

   _IOWR : 디바이스 드라이버에서 데이터를 읽고(R), 쓰기(W)를 수행하기 위한 명령을 만드는 매크로

 

이 매크로의 형태는 다음과 같은 형식의 값을 입력하도록 되어 있다.

   _IO(매직번호, 구분번호)

   _IOR(매직번호, 구분번호, 변수형)

   _IOW(매직번호, 구분번호, 변수형)

   _IOWR(매직번호, 구분번호, 변수형)

 

cmd 명령을 해석하는 매크로 함수

   _IOC_NR : 구분 번호 필드값을 읽는 매크로

   _IOC_TYPE : 매직 번호 필드값을 읽는 매크로

   _IOC_SIZE : 데이터의 크기 필드값을 읽는 매크로

   _IOC_DIR : 읽기와 쓰기 속성 필드값을 읽는 매크로

 

 

매직 번호

매직 번호값의 범위는 0~255 사이다. 보통 영문자 'A'~'Z' 또는 'a'~'z'를 넣는다. 이 매직 번호가 명령을 만들 때 특별한 의미가 있는 것은 아니다. 단지 디바이스 드라이버에서 이 매직 번호를 명령에서 추출하여 자신이 처리하는 매직 번호와 같은지를 비교하여 다르면 처리를 거부한다. 매직 번호는 잘못된 사용을 막는 초보적인 보안 장치다. 디바이스 드라이버에서 매직 번호를 추출하려면 다음과 같은 매크로를 사용한다.

 _IOC_TYPE(명령)

매직 번호는 가급적 다른 디바이스 드라이버와 다르게 하는 것이 좋다. 그렇다고 무조건 다르게 해야 한다는 말은 아니다. 다른 디바이스 드라이버에서 사용한 매직 번호를 사용해도 상관 없다.

 

 

구분 번호

구분 번호는 각 명령을 구분하기 위해 사용한다. 보통 0부터 순서대로 붙여나간다. 이 값은 같은 디바이스 드라이버에서는 중복해서 사용해도 된다. 예를 들어, 읽기와 쓰기 각각의 명령에 같은 구분 번호를 사용해도 디바이스 드라이버에서 명령을 구분할 때 switch문을 사용하면서 명령 매개변수인 cmd값을 그대로 사용하기 때문이다. 명령을 만드는 매크로에서 생성되는 값은 여러 필드를 조합하기 때문에 같은 구분 번호라 해도 다른 명령으로 인식할 수 있기 때문이다.

디바이스 드라이버가 명령에서 이 구분 번호를 추출하려면 다음 매크로 함수를 사용한다.

 _IOC_NR(명령)

하지만 switch문에서 각각의 case에 적용되는 값은 일반적으로 명령 자체를 쓴다.

 

 

변수형

변수형은 arg 매개변수가 가리키는 데이터의 전달 크기를 지정하는 것을 사용하는데, 숫자를 직접 대입하는 것이 아니고 변수형을 넣는다. 왜냐하면 명령을 만드는 매크로에 크기를 인식하는 sizeof란 컴파일 명령이 포함되어 있기 때문이다. ioctl을 사용할 때는 보통 구조체를 이용하여 구조체 주소를 arg에 넘기기 때문에 구조체형을 넣는 것이 일반적이다.

디바이스 드라이버에 전달된 명령에서 크기값을 추출하려면 다음과 같이 사용한다.

 _IOC_SIZE(명령)

 

 

_IO 매크로 함수

이 매크로는 전달되는 매개변수가 없고, 단순히 명령만 전달할 때 사용된다.

예를 들면, 다음과 같이 선언한다.

   #define TEST_DRV_RESET     _IO('Q', 0)

이때는 응용 프로그램에서 전달되는 arg 매개변수를 생량하거나 0을 대입한다. 이 매크로를 응용 프로그램에서 다음과 같이 사용할 수 있다.

   ioctl(dev, TEST_DRV_RESET, 0); 또는 ioctl(dev, TEST_DRV_RESET);

이는 매개변수의 마지막 인자가 가변형 인자기 때문에 가능한 것이다.

 

이렇게 명령으로만 사용할 경우에는 데이터가 디바이스에 출력될 것인지 입력될 것인지에 대한 구분이 필요 없으므로 디바이스 파일의 열기 옵션에 대한 처리를 디바이스 드라이버에서 수행할 필요가 없다.

 

 

_IOR 매크로 함수

이 함수는 디바이스 드라이버에서 데이터를 읽어오는 명령을 만들 때 사용한다. 예를 들어, 다음과 같이 선언한다.

   #define   TEST_DRV_READ     _IOR('Q', 1, int)

이는 디바이스에서 응용 프로그램이 읽어올 데이터의 크기가 int크기 만큼이라는 의미다. 디바이스 드라이버에 전달된 cmd 명령값에서 읽기용인지 쓰기용인지를 알아내기 위해 사용하는 매크로는 다음과 같다.

 _IOC_DIR(명령)

이 매크로를 수행했을 때 반환되는 값의 종류는 다음과 같다.

   _IOC_NONE : 속성이 없다.

   _IOC_READ : 읽기 속성이다.

   _IOC_WRITE : 쓰기 속성이다.

   _IOC_READ|_IOC_WRITE : 읽기, 쓰기 속성이다.

일반적으로 이 명령을 사용할 때, 응용 프로그램에서 ioctl()함수의 arg매개변수값은 디바이스 드라이버에서 데이터를 읽기 위한 데이터 버터(구조체)의 주소를 지정한다.

 

 

_IOW 매크로 함수

디바이스에 데이터를 쓸 명령을 만들 때 사용한다. 그 외의 내용은 _IOR과 같다. 일반적으로 이 명령을 사용할 때 ioctl()의 arg 매개변수값은 디바이스 드라이버에 데이터를 쓰기 위한 버터(구조체)의 주소를 지정한다.

 

 

_IOWR매크로 함수

디바이스에 데이터를 쓰고 읽기 위한 명령을 만들 때 사용한다. 그 외의 내용은 _IOR과 같다. 일반적으로 이 명령을 사용할 때 ioctl()의 arg매개변수값은 디바이스 드라이버에서 데이터를 쓰고, 읽기 위한 데이터 버퍼(구조체)의 주소를 지정한다.


출처 : http://blog.naver.com/luis8282?Redirect=Log&logNo=20086600288

[출처] 디바이스 제어|작성자 piao

+ Recent posts