2012-03-15 4 views
14

문자 버퍼를 읽고 쓰는 장치 드라이버를 설계하고 있습니다. 그러나 내 질문은 file_operations 구조의 두 함수에 관한 것입니다. readwrite입니다. 나는 진정으로 loff_t *offp이 무엇인지 이해하지 못합니다. 읽기 및 쓰기 작업 모두에 대해 *offp은 파일의 현재 읽기/쓰기 위치를 의미하는 파일 오프셋이지만, 장치 파일에 쓰거나 읽는 것이 무엇을 의미하는지조차 확실하지 않습니다.file_operations에 대한 loff_t * offp에 대한 이해

내가 수집 한 것에서 이것은 내 장치에서 쓰고 읽는 방법입니다. 나는 my_char_struct이라고 부르는 내 장치를 나타내는 구조를 만듭니다. 아래 그림을 참조하십시오.

struct my_char_structure{ 
    struct cdev my_cdev; 
    struct semaphore sem; 
    char *data; 
    ssize_t data_size; 
    unsigned int access_key; 
    unsigned long size; 
}; 

이 초기화 내 드라이버가 같은 insmod 때 가리키는 정적 구조입니다.

static dev_t dev_num; 
static struct my_char_structure Dev; 

int start_mod(void){ 
    //Because we are dealing with a fictitious device, I want 
    //the driver to create my two devices with arbitrarily 
    //assigned major numbers. 
    struct my_char_structure *my_dev = &Dev; 
    int err; 

    alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); 

    sema_init(&(my_dev->sem),1); 

    cdev_init(&(my_dev->my_cdev), &fops); 
    my_dev->my_cdev.owner = THIS_MODULE; 
    my_dev->my_cdev.ops = &fops;// fops is my file operations struct 

    err = cdev_add(&my_dev->my_cdev, dev_num, COUNT); 
    if(err<0) 
     printk(KERN_ALERT "There was an error %d.",err); 
    printk(KERN_ALERT " insmod to major number %d",MAJOR(dev_num)); 

    return 0; 
} 

module_init(start_mod); 
내 장치가 열려있을 때, 난 그냥 같은 module_init(start_mod) 동안 설정 한 정적 구조를 가리 키도록 열려있는 파일에 대한 포인터를 만들

...

int dev_open(struct inode *in_node, struct file *filp){ 
    static struct my_char_structure *my_dev; 
    my_dev = container_of(in_node->i_cdev, struct my_char_structure, my_cdev); 
    printk(KERN_ALERT "The device number is %d",iminor(in_node)); 
    if(!my_dev) 
     printk(KERN_ALERT "something didn't work. my_dev not initialized."); 
    filp->private_data = my_dev; 
    return 0; 
} 

어떤 내 읽기 및 쓰기 메소드는 열린 파일을 가리키며 초기 구조 Dev를 수정합니다. 무엇이든지간에 내 구조의 copy_to_user은 사용자가 장치에 기록한 것으로 간주하고 무엇이든지간에 나는 사용자가 자신이 쓰고 있다고 생각하는 것이 무엇이든간에 copy_from_user이라고 생각합니다. 그러나 초기 구조 Dev를 변경하는 것보다 파일 위치 또는 오프셋에 대한 아이디어는 임의의 구조 또는 유형에 대해 커널 내에서 버퍼링 된 메모리에 대한 포인터를 언급하지 않는 한 의미가 없습니다. 그게 내가 파일 오프셋에 대한 유일한 해석이 맞습니까? 여기에 loff_t *offp은 무엇입니까?

write(struct file *filp, const char __user *buff, size_t count, loff_t *offp) 
read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 

* offp을 처음으로 설정 loff_t 있는지, 어떤 file_operation 등이 읽기/쓰기로라고 나는 개인적으로 *offp을 설정하지 않은 경우 (주어진 나의 이해는 올바른)?

마지막 file_operation에서 offp = some_arbitrary_address (내가 그렇게 말했기 때문에)라면이 작업을 다시 호출 할 때 offp가 설정되는 것입니까?

다른 file_opens 작업을 실행하면 어떻게됩니까? 마지막 file_operation이 남긴 작업으로 설정되거나 사용 된 file_open 작업의 탭을 유지하고 * offp를 file_open의 작업 탭으로 대체합니까?

문자 장치의 개념은 장치 자체가 파일처럼 정보를 저장하지 않는 것처럼 보이지만 정보를 저장하는 드라이버라고 생각하면 너무 추상적입니다. 나는 나의 fogginess를 설명했기를 바란다. 그리고 나는 내가 모호하게 보인 무엇이라도 깨끗하게 할 것이다.

답변

19

"loff_t"는 off_t, off64_t 등의 미친 다양성을 통합하는 탐색 위치입니다. 따라서 드라이버는 loff_t를 사용하고 걱정할 필요가 없습니다.

드라이버에 들어가는 시점에서 포인터 자체는 사용자가 제공 한 오프셋을 가리 킵니다 (드라이버를 액세스하는 사용자 코드가 기술적으로는 커널이 자체적으로 제공 할 수 있다고 가정하지만 사용자 사례는 생각하기를) lseek 또는 llseek 또는 lseek64 등을 통해 수행 한 다음 일반 읽기 및 쓰기 작업을 수행합니다.일반적인 디스크상의 파일을 생각해보십시오 : 먼저 파일을 쓸 때 open 일 때, 사용자 (사용자)는 파일에서 현재 위치를 추적하는 데이터 구조를 제공하는 커널을 얻습니다. 따라서 read 또는 write 몇 바이트, 다음 read 또는 write은 중단 한 부분부터 나타납니다.

또한, 당신 dup 파일 기술자 경우, 또는 일련의 명령을 실행하는 측면에서 (예를 들어) forkexec으로 해당 작업을 수행 시크 위치는 모두 상속 프로세스에 의해 공유됩니다. 따라서, 쉘 프롬프트에서 명령 :

(prog1; prog2; prog3) > outputfile 

prog2이 쓰는 그 출력은 즉시 prog1의 출력 후 파일로 전환, 그래서, 다음, dup의 세 가지 프로그램에 대한 설명 출력 파일을 생성하고, 세 개의 개별 프로세스 모두 동일한 내부 커널 loff_t을 가진 기본 커널 데이터 구조를 공유하기 때문에 prog3의 출력은 다른 두 개의 all을 따릅니다.

동일한 내용이 장치 드라이버 파일에 적용됩니다. 읽기 및 쓰기 함수가 호출되면 사용자가 제공 한대로 "현재 오프셋"을 받고 필요에 따라이를 업데이트 할 수 있습니다. 필요하다고 가정합니다 (예 : 사용자에게 읽기 및 쓰기시 오프셋을 이동한다는 사실을 포함하여 일반 파일의 모양). 장치에 시크 오프셋의 논리 응용 프로그램이 있다면 여기에서 사용할 수 있습니다.

물론 장치 드라이버에는 더 많은 것이 있습니다.이 때문에이 책 전체에 책장이 있습니다 (q.v.). :-)

+1

오프셋을 + bytes_read/write로 변경하면 사용자 포인터가 변경되지만 자동으로 수행하지 않습니까? 나는 이것이 나를 위해 몇 가지를 정리한다고 생각한다. 나는 리눅스 디바이스 드라이버 세번째 버전의 책을 읽고 있었고, 다른 사람들은 소개로 읽었으며 오프셋 포인터가 파일 위치라고 불리는 이상한 커널 추상화 (더 나은 단어가 없음)를 참조하는 다이어그램을 가지고 있었다. 도움을 주셔서 감사합니다, 이런 종류의 물건을 지워 요. –

+2

예. (아마도'* offp + = nbytes'를 의미합니다.) 변경하려는 변수는 실제로 커널 공간 일 뿐이지 만 * 사용자의 탐색 오프셋을 나타냅니다. (또는 어떤 경우에는'pread' 또는'pwrite' 호출에 제공되는 오프셋이나, 다른 무엇보다 사용자의'lseek' 오프셋이 가장 많습니다.) "이상한 커널 추상화"라고 부르면, '(prog1; prog2)> output'을 만드는 것입니다. 덧붙여 말하자면 * BSD 커널에는'user I/O offset '을 자동으로 업데이트하는'uiomove'라는 함수가 있습니다; 리눅스는 다른 방향으로 나아갔습니다. – torek

0

Torek's answer 우수합니다. 이전의 리눅스 커널 (2.6.28)에서, 시스템 호출에서 사용되는 오프셋의 예가 여기에 있습니다.이 오프셋은 사용자 공간에서 임시 변수로 오프셋을 복사합니다. 커널 드라이버 호출 메커니즘에 넣은 다음 다시 사용자 파일에 복사합니다. 이것은 드라이버가 보는 오프셋이 사용자보기와 분리되어 시스템 호출에서 NULL을 오프셋하는 상황을 용이하게하므로 SEGVIO가 발생하지 않습니다.

SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count) 
{ 
    loff_t pos; 
    ssize_t ret; 

    if (offset) { 
     if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t)))) 
      return -EFAULT; 
     ret = do_sendfile(out_fd, in_fd, &pos, count, 0); 
     if (unlikely(put_user(pos, offset))) 
      return -EFAULT; 
     return ret; 
    } 

    return do_sendfile(out_fd, in_fd, NULL, count, 0); 
}