15

そこで、linux/timer.h ファイルを使用するカーネル モジュールを作成しようとしています。モジュール内だけで動作するようになりましたが、現在はユーザープログラムから動作させようとしています。

これが私のカーネルモジュールです:

//Necessary Includes For Device Drivers.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/ioctl.h>

#define DEVICE_NAME "mytimer"
#define DEVICE_FILE_NAME "mytimer"
#define MAJOR_NUM 61
#define MINOR_NUM 0

MODULE_LICENSE("Dual BSD/GPL");

static struct timer_list my_timer;

struct file_operations FileOps = 
{
    //No File Operations for this timer.
};

//Function to perform when timer expires.
void TimerExpire(int data)
{
    printk("Timer Data: %d\n", data);
}

//Function to set up timers.
void TimerSetup(void)
{
    setup_timer(&my_timer, TimerExpire, 5678);
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(5000));
}

//Module Init and Exit Functions.
int init_module(void)
{
    int initResult = register_chrdev(MAJOR_NUM, "mytimer", &FileOps);

    if (initResult < 0)
    {
        printk("Cannot obtain major number %d\n", MAJOR_NUM);

        return initResult;
    }

printk("Loading MyTimer Kernel Module...\n");


return 0;
}
void cleanup_module(void)
{
    unregister_chrdev(MAJOR_NUM, "mytimer");
    printk("Unloading MyTimer Kernel Module...\n");
}

具体的には、ユーザー プログラムで TimerSetup() 関数を呼び出す必要があります。ioctl() を使用する必要があることはわかっていますが、MODULE FILE で TimerSetup() を ioctl() 経由で呼び出せるように指定する方法がわかりません。

また、私の 2 番目の質問: モジュールを insmod でき、正しいメジャー番号で /dev/mytimer に mknod することもできました。しかし、ファイル記述子を取得できるようにopen()しようとすると、-1が返され続けました。これは間違っていると思います。パーミッションが適切であることを確認しました (実際、念のため 777 にしました)... それでも機能しません... 何か足りないものはありますか?

念のため、ユーザープログラムを次に示します。

#include <stdio.h>

int main(int argc, char* argv[])
{
    int fd = open("/dev/mytimer", "r");
    printf("fd: %d\n", fd);

    return 0;
}
4

3 に答える 3

22

必要なサンプル コードはdrivers/watchdog/softdog.c(これが書かれた時点で Linux 2.6.33 から) にあります。これは、適切なファイル操作と、ユーザーランドが ioctl() で構造体を満たすことを許可する方法を示しています。

これは、簡単なキャラクター デバイス ドライバーを作成する必要がある人にとっては、実際に役立つ優れたチュートリアルです。

自分の質問に答えるときに、softdog の ioctl インターフェイスを分析しました。

その要点は次のとおりです(網羅的ではありませんが)...

softdog_ioctl()機能、バージョン、およびデバイス情報をアドバタイズする struct watchdog_info の単純な初期化が表示されます。

    static const struct watchdog_info ident = {
            .options =              WDIOF_SETTIMEOUT |
                                    WDIOF_KEEPALIVEPING |
                                    WDIOF_MAGICCLOSE,
            .firmware_version =     0,
            .identity =             "Software Watchdog",
    };

次に、ユーザーがこれらの機能を取得したいだけの単純なケースを見ていきます。

    switch (cmd) {
    case WDIOC_GETSUPPORT:
            return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;

...もちろん、対応するユーザー空間のwatchdog_infoに上記の初期化された値が入力されます。copy_to_user() が失敗すると、-EFAULT が返され、対応するユーザー空間 ioctl() 呼び出しが -1 を返し、意味のある errno が設定されます。

マジック リクエストは実際には linux/watchdog.h で定義されているため、カーネルとユーザー空間がそれらを共有することに注意してください。

#define WDIOC_GETSUPPORT        _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_GETSTATUS         _IOR(WATCHDOG_IOCTL_BASE, 1, int)
#define WDIOC_GETBOOTSTATUS     _IOR(WATCHDOG_IOCTL_BASE, 2, int)
#define WDIOC_GETTEMP           _IOR(WATCHDOG_IOCTL_BASE, 3, int)
#define WDIOC_SETOPTIONS        _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE         _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT     _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT     _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT       _IOR(WATCHDOG_IOCTL_BASE, 10, int)

WDIOC は明らかに「Watchdog ioctl」を意味します

さらに一歩進んで、ドライバーに何かを実行させ、その結果を構造体に配置し、それをユーザー空間にコピーすることも簡単にできます。たとえば、struct watchdog_info にも member があるとし__u32 result_codeます。注、__u32はカーネルのバージョンのuint32_t.

ioctl() を使用すると、ユーザーはオブジェクトのアドレスをカーネルに渡します。これは、構造体であれ整数であれ、カーネルが応答を同一のオブジェクトに書き込み、結果を提供されたアドレスにコピーすることを期待しています。

2 番目に行う必要があるのは、誰かがデバイスを開いたり、読み込んだり、書き込んだり、ioctl() などのフックを使用したときに、何をすべきかをデバイスが認識していることを確認することです。

興味深いのは次のとおりです。

static const struct file_operations softdog_fops = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
        .write          = softdog_write,
        .unlocked_ioctl = softdog_ioctl,
        .open           = softdog_open,
        .release        = softdog_release,
};

unlocked_ioctl ハンドラーがどこに行くのか... ご想像のとおり、softdog_ioctl() です。

ioctl() を扱うときに実際には存在しない複雑なレイヤーを並置している可能性があると思いますが、それは本当に単純です。同じ理由で、ほとんどのカーネル開発者は、絶対に必要でない限り、新しい ioctl インターフェイスが追加されることに眉をひそめています。ioctl() が埋めるタイプとそれを行うために使用する魔法のタイプを見失うのはあまりにも簡単です。これが、copy_to_user() が失敗する主な理由であり、多くのユーザー空間プロセスがスタックしてカーネルが腐敗します。ディスクスリープ。

タイマーについては、ioctl() が健全性への最短経路であることに同意します。

于 2010-02-15T07:16:34.917 に答える
8

プロセスがデバイス ファイルを開こうとしたときに呼び出される関数を指定するための.open関数ポインタが構造体にありません。ioctl 関数の関数ポインタもfile_operations指定する必要があります。.ioctl

The Linux Kernel Module Programming Guide、特に第 4 章 (Character Device Files) と第 7 章 (Talking to Device Files) を読んでみてください。

第 4 章では、やfile_operationsなどのさまざまな操作を実行するモジュール/ドライバーによって定義された関数へのポインターを保持する構造体を紹介します。openioctl

第 7 章では、ioctl を介したモジュール/ドライブとの通信に関する情報を提供します。

Linux Device Drivers, Third Editionも優れたリソースです。

于 2010-02-15T07:13:53.103 に答える