Linux 문자 장치 드라이버 문제

Linux 문자 장치 드라이버 문제

문자 장치 드라이버를 만들기 위해 LKM을 작성 중입니다.

Linux 커널: VirtualBox의 4.4.0-93-일반, 2GB RAM, SWAP은 300Kb

질문 1

처리할 C 프로그램을 작성하면FDdev_write에서는 모든 것이 정상입니다. 그래야 하는 것처럼 읽히지만, 사용하려고 하면헤더 -n 1 < /dev/opsysmem아무것도 출력하지 않습니다.

장치에서 코드 읽기:

int main()
{
    int ret, fd;
    char stringToSend[BUFFER_LENGTH];
    printf("Starting device test code example...\n");
    fd = open("/dev/opsysmem", O_RDWR); // Open the device with read/write access
    if (fd < 0)
    {
        perror("Failed to open the device...");
        return errno;
    }

    printf("Press ENTER to read back from the device...\n");
    getchar();

    printf("Reading from the device...\n");
    ret = read(fd, receive, BUFFER_LENGTH); // Read the response from the LKM
    if (ret < 0)
    {
        perror("Failed to read the message from the device.");
        return errno;
    }
    printf("The received message is: [%s]\n", receive);

    return 0;
}

질문 2

충분히 큰 메시지를 반복해서 보내면 모든 것이 정상이며 2MiB 버퍼가 가득 차고 다음 메시지가 삭제됩니다. 그러나 메시지가 더 작은 경우(예: 메시지당 1자) 약 10000 노드 후에 중지됩니다. 이것은 내 연결 목록 구현의 문제입니까, 알려진 Linux 문제입니까, 아니면 내 코드에 내가 관찰하지 못하는 뭔가가 있는 것입니까?

문제 2가 발생하면 vCPU가 정현파 방식으로 조절됩니다.

읽기 및 쓰기 기능은 다음과 같습니다.

static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
    Node *msg;
    int ret_val;
    char* message;
    unsigned int message_length;

    // Entering critical section
    down(&sem); //wait state

    msg = pop(&l, 0);

    // No message? No wait!
    if(!msg) {
        up(&sem);
        return -EAGAIN;
    }

    if(len < msg->length) {
        up(&sem);
        return -EINVAL;
    }

    // Since we have a message, let's send it!
    current_size -= message_length;

    // copy_to_user has the format ( * to, *from, size) and returns 0 on success
    ret_val = copy_to_user(buffer, msg->string, message_length);

    if (!ret_val) {
        remove_element(&l, 0);
        up(&sem);
        return ret_val;
    } else {
        up(&sem);
        return -EFAULT; // Failed
    }
}


static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) {
    Node *n;

    // buffer larger than 2 * 1024 bytes
    if(len > MAX_MESSAGE_SIZE || len == 0) {
        return -EINVAL;
    }

    n = kmalloc(sizeof(Node), GFP_KERNEL);

    if(!n) { 
        return -EAGAIN;
    }

    n->string = (char*) kmalloc(len, GFP_KERNEL);
    n->length = len;

    copy_from_user(n->string, buffer, len);

    // Enter critical section
    down(&sem); //wait state
    if(SLEEP) msleep(100);

    // buffer is larger than the total list memory (2MiB)
    if(current_size + len > MAX_LIST_SIZE) {
        up(&sem);
        return -EAGAIN;
    }

    current_size += len;

    push(&l, n);

    up(&sem);
    // Exit critical section

    return len;
}

이것은 내 연결 목록과 그 기능입니다.

typedef struct Node {
    unsigned int length;
    char* string;
    struct Node *next;
} Node;

typedef struct list{
    struct Node *node;
} list;

static void init(list * l){
    l->node = NULL;
}

static void destroyNode(Node *n) {
    if(n) {
        destroyNode(n->next);
        kfree(n->string);
        n->string = NULL;
        kfree(n);
        n = NULL;
    }
}

static void destroy(list *l){
    if(l) {
        destroyNode(l->node);
    }
}

static Node* pop(list *l, unsigned int index) {
    struct Node *_current = l->node;

    // Cut down index until reaching the desired position
    while(_current) {
        if(index) {
            _current = _current->next;
            index--;
        } else { return _current; }
    }

    // If you are here, the node does not exist
    return NULL;
}

static int push(list * l, Node *n) {
    if(!n) { return -1; }

    // Initialize the string

    // Do we have a node in the list?
    if (l->node) { 
        // Virtually add it as a head
        n->next = l->node;
    } else {
        n->next = NULL;
    } // Otherwise prepare the list to have no tail

    // Now make the list point to the head
    l->node = n;

    return 0;
}

static int remove_element(list * l, unsigned int index){
    // Get the reference for head
    struct Node *previous;
    struct Node *_current;

    previous = NULL;
    _current = (Node*) l->node;

    // Swap _current until index
    while(_current) {
        // Is the index !0 and we have more nodes?
        if(index) {
            previous = _current;
            _current = _current->next;
            index--;
        } else {
            if(previous) {
                previous->next = _current->next;
            } else {
                l->node = _current->next;
            }
            // Free memory, assign NULL pointer
            kfree(_current->string);
            _current-> string = NULL;
            kfree(_current);
            _current = NULL;

            // Return success
            return 0;
        }
    }

    // No _current? No problem!
    return -1;
}

__ 질문 2에 대한 업데이트 __ 다양한 크기의 입력 문자열을 시도한 결과 장치 드라이버(크기 3.3k)에 대해 약 650번 호출한 후 메시지 목록 크기가 4MiB(최대값)가 되었습니다. 장치를 몇 번 더 호출하면 커널이 정지됩니다.

편집 1:댓글을 기반으로 te dev_write를 업데이트하고 디버그 코드를 제거했습니다. 편집 2:더 많은 기능 추가: 푸시/팝/파괴 편집 3:버퍼 길이와 메시지 길이를 확인했습니다.

답변1

제 생각에는질문 1head줄 끝 문자(예: 개행 문자 '\n')가 표시되지 않거나 검색 시스템 호출을 사용하고 및 함수의 인수를 무시하기 때문일 수 있습니다 offset. 즉, 올바르게 이해하면 검색이 작동하지 않습니다. ).. . 확인하다dev_read()dev_write()이것out - head는 검색을 사용하여 항목을 최적화하려고 시도하지만 귀하의 경우에는 작동할지 확실하지 않습니다.

당신의 대답도 확실하지 않습니다질문 2동기화되지 않은 시간으로 인해 발생하는 문제는 정확합니다(관련이 없는 한 msleep())... 제 추측으로는 메모리 할당 문제나 경쟁 조건이지만 소스 코드를 표시하지 않으셨기 push()때문에 pop()알 수 없습니다.

bufferlen인수를 저장한 dev_write()다음 이를 사용하여 ... dev_read()에 전달하는 것 같습니다. copy_to_user()해당 버퍼의 데이터는 여전히 사용자 공간에 있으므로 아마도 사용자 공간에서 사용자 공간으로 복사하려고 할 것입니다. 읽다이것도움이 될 수도 있습니다.

push()and...의 코드로 질문을 업데이트해야 합니다. pop()하지만 최소한 push()연결된 목록 요소가 목록에 삽입될 메모리와 기록된 데이터를 저장할 버퍼를 할당해야 합니다. 이 버퍼는 copy_from_user()데이터를 가져오는 데 사용됩니다. 사용자 공간과 커널 버퍼 영역... 그런 다음 msgin 작업이 끝나면 및 그 자체 dev_read()에 포함된 커널 버퍼를 해제해야 합니다 .msgmsg

여기에서 많은 복사가 일어나고 있다는 것을 알고 있지만 이를 방지하려면 가상 메모리 시스템과 코드 설계에 매우 열심히 노력해야 합니다(예:제로 카피구현하다).

또 하나의 사소하지만 매우 중요한 점은 dev_read()버퍼에 메시지를 위한 충분한 공간이 있는지 확인하지 않는다는 것입니다 message_length. <= len예를 들어, 코드에 따라 드라이버가 사용 가능한 공간보다 큰 메시지를 복사하려고 할 수 있습니다. copy_to_user()그걸 잡아야 하는데, 그러다가 다시 말하지만, 어쩌면 그게 당신이 온 곳일 수도 있습니다질문 2.

답변2

나는 그것을 해결했다질문 2. 이는 virtualbox가 호스트 시스템과 동기화되지 않았기 때문에 발생합니다.

여기서 해결책을 찾았습니다 https://stackoverflow.com/questions/5308492/virtualbox-synchronization-problems

이것은:

VBoxManage 게스트 속성 설정 "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold" 1000

답변3

질문 1

내 커밋 기록을 검색한 결과 "Working test.sh"라는 커밋 메시지를 발견했습니다. 이는 다음을 통해 쓰고 echo읽고 있음을 의미합니다.head -n 1

이 커밋에서 내 dev_read 함수는 메시지 길이를 반환합니다.

이것은 위의 C 프로그램에서 버퍼링된 읽기에 작동하는 것 같습니다 head -n 1.cat

나는 여기서 답을 찾았습니다. https://www.tldp.org/LDP/lkmpg/2.4/html/c577.htm

관련 정보