سؤال

لذلك أحاول كتابة وحدة kernel تستخدم ملف 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 الخاص بي أنه يجب أن يكون TimerSetup() قابلاً للاستدعاء عبر ioctl().

وسؤالي الثاني أيضاً:لقد تمكنت من إدخال الوحدة النمطية الخاصة بي وكذلك mknod إلى /dev/mytimer بالرقم الرئيسي الصحيح.ولكن عندما حاولت فتحه () حتى أتمكن من الحصول على واصف الملف منه، استمر في إرجاع -1، وهو ما أفترض أنه خطأ.لقد تأكدت من أن الأذونات جيدة (في الواقع، قمت بعملها 777 فقط للتأكد)...لا زال لا يعمل...هل هناك شيء أفتقده؟

هنا هو برنامج المستخدم فقط في حالة:

#include <stdio.h>

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

    return 0;
}
هل كانت مفيدة؟

المحلول

ورمز المثال تحتاج إلى يمكن العثور عليها في drivers/watchdog/softdog.c (من لينكس 2.6.33 في وقت كتابة هذا)، والذي يوضح عمليات الملفات المناسبة وكذلك كيفية السماح يوزرلاند لملء هيكل مع IOCTL ().

وانها في الواقع كبيرة، والبرنامج التعليمي العمل لمن يحتاج لكتابة برامج تشغيل الأجهزة شخصية تافهة.

وأنا تشريح واجهة IOCTL softdog عندما الإجابة بلدي السؤال ، والتي قد تكون مفيدة لك.

وهنا يكمن جوهر ذلك (وإن لم شاملة) ...

في softdog_ioctl() تشاهد التهيئة بسيطة من البنية 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 كونها مجموعة ذات مغزى.

ملحوظة، يتم تعريف طلبات سحرية فعلا في لينكس / 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 يدل بوضوح "الوكالة الدولية للطاقة IOCTL"

ويمكنك أن تأخذ بسهولة أن خطوة أخرى إلى الأمام، وكان السائق الخاص بك تفعل شيئا ووضع نتيجة ذلك شيء في بنية ونسخه إلى مساحة المستخدم. على سبيل المثال، إذا watchdog_info البنية كما كان __u32 result_code الأعضاء. ملاحظة، __u32 هو مجرد نسخة نواة للuint32_t.

ومع IOCTL ()، المستخدم يمر عنوان كائن، سواء كان ذلك الهيكل، وعدد صحيح، مهما كان لنواة تتوقع نواة لكتابة ردها في كائن متطابقة ونسخ النتائج إلى العنوان الذي تم توفيره.

والشيء الثاني كنت بحاجة الى الذهاب الى القيام به هو التأكد جهازك يعرف ما يجب القيام به عندما يفتح شخص ما، يقرأ منه، ويكتب إليها، أو يستخدم ربط مثل IOCTL ()، والتي يمكن أن نرى بسهولة من خلال دراسة softdog.

ومما يثير الاهتمام هو:

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 () هو أقصر الطرق إلى التعقل.

نصائح أخرى

وكنت في عداد المفقودين مؤشر دالة .open في هيكل file_operations لتحديد وظيفة ليتم استدعاؤها عند محاولات عملية لفتح ملف الجهاز. سوف تحتاج إلى تحديد وظيفة مؤشر .ioctl عن وظيفة IOCTL الخاص بك أيضا.

وحاول القراءة من خلال ونواة لينكس وحدة البرمجة دليل ، خصوصا الفصل 4 (حرف جهاز الملفات) و 7 (يتحدث إلى جهاز الملفات).

الفصل 4 يدخل الهيكل file_operations، الذي يحمل مؤشرات إلى الوظائف المحددة من قبل وحدة / سائق أن أداء العمليات المختلفة مثل open أو ioctl.

الفصل 7 يقدم معلومات عن التواصل مع وحدة نمطية / حملة عبر IOCTLS.

برامج تشغيل الأجهزة لينكس، الطبعة الثالثة عبارة الموارد سلعة أخرى.

الحد الأدنى من الأمثلة القابلة للتشغيل

تم اختباره في بيئة QEMU + Buildroot القابلة للتكرار بالكامل، لذلك قد يساعد الآخرين في الحصول على ioctl عمل.جيثب المنبع:وحدة النواة | رأس مشترك | com.userland.

الجزء الأكثر إزعاجًا هو فهم أن بعض المعرفات المنخفضة قد تم اختطافها: لا يتم استدعاء ioctl إذا كان cmd = 2 ، عليك أن تستخدم _IOx وحدات الماكرو.

وحدة النواة:

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/printk.h> /* printk */

#include "ioctl.h"

MODULE_LICENSE("GPL");

static struct dentry *dir;

static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp)
{
    void __user *arg_user;
    union {
        int i;
        lkmc_ioctl_struct s;
    } arg_kernel;

    arg_user = (void __user *)argp;
    pr_info("cmd = %x\n", cmd);
    switch (cmd) {
        case LKMC_IOCTL_INC:
            if (copy_from_user(&arg_kernel.i, arg_user, sizeof(arg_kernel.i))) {
                return -EFAULT;
            }
            pr_info("0 arg = %d\n", arg_kernel.i);
            arg_kernel.i += 1;
            if (copy_to_user(arg_user, &arg_kernel.i, sizeof(arg_kernel.i))) {
                return -EFAULT;
            }
        break;
        case LKMC_IOCTL_INC_DEC:
            if (copy_from_user(&arg_kernel.s, arg_user, sizeof(arg_kernel.s))) {
                return -EFAULT;
            }
            pr_info("1 arg = %d %d\n", arg_kernel.s.i, arg_kernel.s.j);
            arg_kernel.s.i += 1;
            arg_kernel.s.j -= 1;
            if (copy_to_user(arg_user, &arg_kernel.s, sizeof(arg_kernel.s))) {
                return -EFAULT;
            }
        break;
        default:
            return -EINVAL;
        break;
    }
    return 0;
}

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = unlocked_ioctl
};

static int myinit(void)
{
    dir = debugfs_create_dir("lkmc_ioctl", 0);
    /* ioctl permissions are not automatically restricted by rwx as for read / write,
     * but we could of course implement that ourselves:
     * https://stackoverflow.com/questions/29891803/user-permission-check-on-ioctl-command */
    debugfs_create_file("f", 0, dir, NULL, &fops);
    return 0;
}

static void myexit(void)
{
    debugfs_remove_recursive(dir);
}

module_init(myinit)
module_exit(myexit)

رأس مشترك:

#ifndef IOCTL_H
#define IOCTL_H

#include <linux/ioctl.h>

typedef struct {
    int i;
    int j;
} lkmc_ioctl_struct;
#define LKMC_IOCTL_MAGIC 0x33
#define LKMC_IOCTL_INC     _IOWR(LKMC_IOCTL_MAGIC, 0, int)
#define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct)

#endif

أرض المستخدم:

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "../ioctl.h"

int main(int argc, char **argv)
{
    int fd, arg_int, ret;
    lkmc_ioctl_struct arg_struct;

    if (argc < 2) {
        puts("Usage: ./prog <ioctl-file>");
        return EXIT_FAILURE;
    }
    fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }
    /* 0 */
    {
        arg_int = 1;
        ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int);
        if (ret == -1) {
            perror("ioctl");
            return EXIT_FAILURE;
        }
        printf("arg = %d\n", arg_int);
        printf("ret = %d\n", ret);
        printf("errno = %d\n", errno);
    }
    puts("");
    /* 1 */
    {
        arg_struct.i = 1;
        arg_struct.j = 1;
        ret = ioctl(fd, LKMC_IOCTL_INC_DEC, &arg_struct);
        if (ret == -1) {
            perror("ioctl");
            return EXIT_FAILURE;
        }
        printf("arg = %d %d\n", arg_struct.i, arg_struct.j);
        printf("ret = %d\n", ret);
        printf("errno = %d\n", errno);
    }
    close(fd);
    return EXIT_SUCCESS;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top