22

eventfd() を使用して、ユーザー空間プログラムで eventfd インスタンスを作成しました。この作成された eventfd のインスタンスへの参照 (その構造体または pid+fd ペアへのポインター) をカーネル モジュールに渡して、カウンター値を更新できるようにする方法はありますか?

私がやりたいことは次のとおりです。私が書いたカーネル空間モジュールとデータと信号を交換する必要があるユーザー空間プログラムを開発しています。データを転送するために、私はすでに ioctl を使用しています。しかし、新しいデータがioctlを介して消費する準備ができているときはいつでも、カーネルモジュールがユーザー空間プログラムに通知できるようにしたいと考えています。

これを行うために、私のユーザー空間プログラムは、さまざまなスレッドでいくつかの eventfd を作成します。これらのスレッドは、select() を使用してこれらの eventfd を待機し、カーネル モジュールがこれらの eventfd のカウントを更新するたびに、ioctl を介してデータを要求してデータを消費し続けます。

問題は、カーネル空間からこれらの eventfds への「struct file *」ポインターをどのように解決するかです。eventfds へのポインタを取得できるように、カーネル モジュールに送信できる eventfds に関するどのような情報を教えてください。これらのポインターを取得するには、カーネル モジュールでどの関数を使用しますか?

カーネル空間からユーザー空間にイベントを通知するより良い方法はありますか? select() の使用を手放すことはできません。

4

3 に答える 3

20

私は最終的にこれを行う方法を考え出しました。システム上で開いている各ファイルは、それを開いたプロセスの1つのpidと、そのファイルに対応するfd(そのプロセスのコンテキスト内)によって識別できることに気付きました。したがって、カーネル モジュールが pid と fd を認識している場合、プロセスのstruct * task_structを検索し、そこからstruct * ファイルを検索し、最後に fd を使用して、eventfd のstruct * fileへのポインターを取得できます。次に、この最後のポインターを使用して、eventfd のカウンターに書き込むことができます。

以下は、概念を説明するために作成したユーザー空間プログラムとカーネル モジュールのコードです (現在は動作しています)。

ユーザー空間 C コード (efd_us.c):

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>     //Definition of uint64_t
#include <sys/eventfd.h>

int efd; //Eventfd file descriptor
uint64_t eftd_ctr;

int retval;     //for select()
fd_set rfds;        //for select()

int s;

int main() {


    //Create eventfd
    efd = eventfd(0,0);
    if (efd == -1){
        printf("\nUnable to create eventfd! Exiting...\n");
        exit(EXIT_FAILURE);
    }

    printf("\nefd=%d pid=%d",efd,getpid());

    //Watch efd
    FD_ZERO(&rfds);
    FD_SET(efd, &rfds);

    printf("\nNow waiting on select()...");
    fflush(stdout);

    retval = select(efd+1, &rfds, NULL, NULL, NULL);

    if (retval == -1){
        printf("\nselect() error. Exiting...");
        exit(EXIT_FAILURE);
    } else if (retval > 0) {
        printf("\nselect() says data is available now. Exiting...");
        printf("\nreturned from select(), now executing read()...");
        s = read(efd, &eftd_ctr, sizeof(uint64_t));
        if (s != sizeof(uint64_t)){
            printf("\neventfd read error. Exiting...");
        } else {
            printf("\nReturned from read(), value read = %lld",eftd_ctr);
        }
    } else if (retval == 0) {
        printf("\nselect() says that no data was available");
    }

    printf("\nClosing eventfd. Exiting...");
    close(efd);
    printf("\n");
    exit(EXIT_SUCCESS);
}

カーネル モジュール C コード (efd_lkm.c):

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/fdtable.h>
#include <linux/rcupdate.h>
#include <linux/eventfd.h>

//Received from userspace. Process ID and eventfd's File descriptor are enough to uniquely identify an eventfd object.
int pid;
int efd;

//Resolved references...
struct task_struct * userspace_task = NULL; //...to userspace program's task struct
struct file * efd_file = NULL;          //...to eventfd's file struct
struct eventfd_ctx * efd_ctx = NULL;        //...and finally to eventfd context

//Increment Counter by 1
static uint64_t plus_one = 1;

int init_module(void) {
    printk(KERN_ALERT "~~~Received from userspace: pid=%d efd=%d\n",pid,efd);

    userspace_task = pid_task(find_vpid(pid), PIDTYPE_PID);
    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's task struct: %p\n",userspace_task);

    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's files struct: %p\n",userspace_task->files);

    rcu_read_lock();
    efd_file = fcheck_files(userspace_task->files, efd);
    rcu_read_unlock();
    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's file struct: %p\n",efd_file);


    efd_ctx = eventfd_ctx_fileget(efd_file);
    if (!efd_ctx) {
        printk(KERN_ALERT "~~~eventfd_ctx_fileget() Jhol, Bye.\n");
        return -1;
    }
    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's context: %p\n",efd_ctx);

    eventfd_signal(efd_ctx, plus_one);

    printk(KERN_ALERT "~~~Incremented userspace program's eventfd's counter by 1\n");

    eventfd_ctx_put(efd_ctx);

    return 0;
}


void cleanup_module(void) {
    printk(KERN_ALERT "~~~Module Exiting...\n");
}  

MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);

これを実行するには、次の手順を実行します。

  1. ユーザー空間プログラム (efd_us.out) とカーネル モジュール (efd_lkm.ko) をコンパイルします。
  2. ユーザー空間プログラム (./efd_us.out) を実行し、表示される pid と efd の値をメモします。(たとえば、「pid=2803 efd=3」の場合。ユーザー空間プログラムは、select() で際限なく待機します。
  3. 新しいターミナル ウィンドウを開き、pid と efd をパラメーターとして渡すカーネル モジュールを挿入します。sudo insmod efd_lkm.ko pid=2803 efd=3
  4. ユーザー空間プログラム ウィンドウに戻ると、ユーザー空間プログラムが選択から抜け出し、終了したことがわかります。
于 2012-11-29T14:36:02.587 に答える
10

ここでカーネルソースを参照してください:

http://lxr.free-electrons.com/source/fs/eventfd.c

基本的に、によって生成されたユーザースペースファイル記述子を、または他のパスeventfd()を介してモジュールに送信します。ioctl()カーネルから、を呼び出しeventfd_ctx_fdget()てeventfdコンテキストを取得し、次にeventfd_signal()結果のコンテキストを取得します。eventfd_ctx_put()コンテキストが終了したら、忘れないでください。

于 2012-11-28T22:31:53.920 に答える
1

カーネル空間からこれらの eventfds への「struct file *」ポインターを解決するにはどうすればよいですか

これらのポインターを、作成したこのインターフェイスが発行したデータ構造に解決する必要があります (新しい型を作成し、そこから必要なフィールドを読み取りますstruct file)。

カーネル空間からユーザー空間にイベントを通知するより良い方法はありますか?

Netlink ソケットは、カーネルがユーザー空間と通信するもう 1 つの便利な方法です。「より良い」は見る人の目にあります。

于 2012-11-28T15:19:17.270 に答える