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()不明です。

bufferそしてlen引数を保存し、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 ゲストプロパティの設定 "/V​​irtualBox/GuestAdd/VBoxService/--timesync-set-threshold" 1000

答え3

質問1

私のコミット履歴を検索した結果、「Working test.sh」というコミットメッセージが見つかりました。これは、次のように書いてecho読んでいることを意味します。head -n 1

このコミットでは、私のdev_read関数はメッセージの長さを返します。

これは、上記のCプログラムでバッファリングされた読み取りで機能するようですhead -n 1cat

私はここで答えを見つけました。 https://www.tldp.org/LDP/lkmpg/2.4/html/c577.htm

関連情報