Linux中字符设备驱动程序的计时器和sysinfo

时间:2017-05-17 10:39:01

标签: c linux timer linux-device-driver interrupt

我正在Linux中编写一个简单的字符设备驱动程序。但我遇到了一些问题。我添加了计时器,但它不正确。我需要1)执行一个计时器; 2)每隔30秒执行一次计时器n次并停止; 3)连续执行定时器,直到用户向驱动器发送STOP命令。并且用户进程必须在每个定时器中断完成结束时通过命令IO接口从设备返回数据(快照)。我可以使用jiffies,tasklet,中断,但我不知道如何在这个驱动程序中实现它们。 然后是关于sysinfo()的。我已经将sysinfo()的代码添加到用户应用程序中,但它必须在内核应用程序代码中,然后将sysinfo()发送回用户应用程序进程并显示在终端中。 这是我的内核和用户应用程序代码。

内核:

/**
 * @file   diasdd.c
 * @date   16 May 2017
 * @version 0.1
 * @brief   This module maps to /dev/diasdd and
 * comes with a helper C program that can be run in Linux user space to communicate with
 * this the LKM.
 */

#include <linux/init.h>           // Macros used to mark up functions e.g. __init __exit
#include <linux/module.h>         // Core header for loading LKMs into the kernel
#include <linux/device.h>         // Header to support the kernel Driver Model
#include <linux/kernel.h>         // Contains types, macros, functions for the kernel
#include <linux/fs.h>             // Header for the Linux file system support
#include <asm/uaccess.h>          // Required for the copy to user function
#include <linux/timer.h>

#define  DEVICE_NAME "diasdd"    ///< The device will appear at /dev/diasdd using this value
#define  CLASS_NAME  "dd"        ///< The device class -- this is a character device driver

MODULE_LICENSE("GPL");            ///< The license type -- this affects available functionality
MODULE_DESCRIPTION("A simple Linux char driver");  ///< The description -- see modinfo
MODULE_VERSION("0.1");            ///< A version number to inform users

static int    majorNumber;                  ///< Stores the device number -- determined automatically
static char   message[256] = {0};           ///< Memory for the string that is passed from user space
static short  size_of_message;              ///< Used to remember the size of the string stored
static int    numberOpens = 0;              ///< Counts the number of times the device is opened
static struct class*  diasddClass  = NULL; ///< The device-driver class struct pointer
static struct device* diasddDevice = NULL; ///< The device-driver device struct pointer

// The prototype functions for the character driver -- must come before the struct definition
static int     dev_open(struct inode *, struct file *);
static int     dev_release(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);

static struct timer_list my_timer;

void my_timer_callback( unsigned long data )
{
  printk( "DiasDD: my_timer_callback called (%ld).\n", jiffies );
}


/** @brief Devices are represented as file structure in the kernel. The file_operations structure from
 *  /linux/fs.h lists the callback functions that we wish to associated with our file operations
 *  using a C99 syntax structure. char devices usually implement open, read, write and release calls
 */
static struct file_operations fops =
{
   .open = dev_open,
   .read = dev_read,
   .write = dev_write,
   .release = dev_release,
};

/** @brief The LKM initialization function
 *  The static keyword restricts the visibility of the function to within this C file. The __init
 *  macro means that for a built-in driver (not a LKM) the function is only used at initialization
 *  time and that it can be discarded and its memory freed up after that point.
 *  @return returns 0 if successful
 */
static int __init diasdd_init(void){
    // Timer starts here
    int ret;

  printk("DiasDD: Timer module installing\n");

  // my_timer.function, my_timer.data
  setup_timer( &my_timer, my_timer_callback, 0 );

  printk( "DiasDD: Starting timer to fire in 300ms (%ld)\n", jiffies );
  ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(300) );
  if (ret) printk("Error in mod_timer\n");  

   printk(KERN_INFO "DiasDD: Initializing the DiasDD LKM\n");

   // Try to dynamically allocate a major number for the device -- more difficult but worth it
   majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
   if (majorNumber<0){
      printk(KERN_ALERT "DiasDD failed to register a major number\n");
      return majorNumber;

   printk(KERN_INFO "DiasDD: registered correctly with major number %d\n", majorNumber);
   }

   // Register the device class
   diasddClass = class_create(THIS_MODULE, CLASS_NAME);
   if (IS_ERR(diasddClass)){                // Check for error and clean up if there is
      unregister_chrdev(majorNumber, DEVICE_NAME);
      printk(KERN_ALERT "Failed to register device class\n");
      return PTR_ERR(diasddClass);          // Correct way to return an error on a pointer
   }
   printk(KERN_INFO "DiasDD: device class registered correctly\n");

   // Register the device driver
   diasddDevice = device_create(diasddClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
   if (IS_ERR(diasddDevice)){               // Clean up if there is an error
      class_destroy(diasddClass);           // Repeated code but the alternative is goto statements
      unregister_chrdev(majorNumber, DEVICE_NAME);
      printk(KERN_ALERT "Failed to create the device\n");
      return PTR_ERR(diasddDevice);
   }
   printk(KERN_INFO "DiasDD: device class created correctly\n"); // Made it! device was initialized
   return 0;
}

/** @brief The LKM cleanup function
 *  Similar to the initialization function, it is static. The __exit macro notifies that if this
 *  code is used for a built-in driver (not a LKM) that this function is not required.
 */
static void __exit diasdd_exit(void){
    // Timer ends here
    int ret;

  ret = del_timer( &my_timer );
  if (ret) printk("DiasDD: The timer is still in use...\n");

  printk("DiasDD: Timer module uninstalling\n");

   device_destroy(diasddClass, MKDEV(majorNumber, 0));     // remove the device
   class_unregister(diasddClass);                          // unregister the device class
   class_destroy(diasddClass);                             // remove the device class
   unregister_chrdev(majorNumber, DEVICE_NAME);             // unregister the major number
   printk(KERN_INFO "DiasDD: Goodbye from the LKM!\n");
}

/** @brief The device open function that is called each time the device is opened
 *  This will only increment the numberOpens counter in this case.
 *  @param inodep A pointer to an inode object (defined in linux/fs.h)
 *  @param filep A pointer to a file object (defined in linux/fs.h)
 */
static int dev_open(struct inode *inodep, struct file *filep){
   numberOpens++;
   printk(KERN_INFO "DiasDD: Device has been opened %d time(s)\n", numberOpens);
   return 0;
}

/** @brief This function is called whenever device is being read from user space i.e. data is
 *  being sent from the device to the user. In this case it uses the copy_to_user() function to
 *  send the buffer string to the user and captures any errors.
 *  @param filep A pointer to a file object (defined in linux/fs.h)
 *  @param buffer The pointer to the buffer to which this function writes the data
 *  @param len The length of the b
 *  @param offset The offset if required
 */
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
   int error_count = 0;
   // copy_to_user has the format ( * to, *from, size) and returns 0 on success
   error_count = copy_to_user(buffer, message, size_of_message);

   if (error_count==0){            // if true then have success
      printk(KERN_INFO "DiasDD: Sent %d characters to the user\n", size_of_message);
      return (size_of_message=0);  // clear the position to the start and return 0
   }
   else {
      printk(KERN_INFO "DiasDD: Failed to send %d characters to the user\n", error_count);
      return -EFAULT;              // Failed -- return a bad address message (i.e. -14)
   }
}

/** @brief This function is called whenever the device is being written to from user space i.e.
 *  data is sent to the device from the user. The data is copied to the message[] array in this
 *  LKM using the sprintf() function along with the length of the string.
 *  @param filep A pointer to a file object
 *  @param buffer The buffer to that contains the string to write to the device
 *  @param len The length of the array of data that is being passed in the const char buffer
 *  @param offset The offset if required
 */
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
   sprintf(message, "%s(%zu letters)", buffer, len);   // appending received string with its length
   size_of_message = strlen(message);                 // store the length of the stored message
   printk(KERN_INFO "DiasDD: Received %zu characters from the user\n", len);
   printk(KERN_INFO "DiasDD: Received message: %s\n", buffer);
   return len;
}

/** @brief The device release function that is called whenever the device is closed/released by
 *  the user space program
 *  @param inodep A pointer to an inode object (defined in linux/fs.h)
 *  @param filep A pointer to a file object (defined in linux/fs.h)
 */
static int dev_release(struct inode *inodep, struct file *filep){
   printk(KERN_INFO "DiasDD: Device successfully closed\n");
   return 0;
}

/** @brief A module must use the module_init() module_exit() macros from linux/init.h, which
 *  identify the initialization function at insertion time and the cleanup function (as

 *  listed above)
 */
module_init(diasdd_init);
module_exit(diasdd_exit);

用户流程:

/**
 * @file   testdiasdd.c
 * @date   16 May 2017
 * @version 0.1
 * @brief  A Linux user space program that communicates with the diasdd.c LKM. It passes a
 * string to the LKM and reads the response from the LKM. For this example to work the device
 * must be called /dev/diasdd.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#include <sys/sysinfo.h>    // sysinfo
#include <unistd.h>     // sysconf

#define BUFFER_LENGTH 256               ///< The buffer length (crude but fine)
static char receive[BUFFER_LENGTH];     ///< The receive buffer from the LKM

int main(){
   int ret, fd;
   char stringToSend[BUFFER_LENGTH];
   printf("Starting device test code example...\n");
   fd = open("/dev/diasdd", O_RDWR);             // Open the device with read/write access
   if (fd < 0){
      perror("Failed to open the device...");
      return errno;
   }
   printf("Type in a short string to send to the kernel module:\n");
   scanf("%[^\n]%*c", stringToSend);                // Read in a string (with spaces)
   printf("Writing message to the device [%s].\n", stringToSend);
   ret = write(fd, stringToSend, strlen(stringToSend)); // Send the string to the LKM
   if (ret < 0){
      perror("Failed to write the message to 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);
   printf("End of the program\n");

   struct sysinfo info;

     if (sysinfo(&info) != 0)

    printf("\n");
    printf("Current system info:\n");
    printf("Uptime: %ld:%ld:%ld\n", info.uptime/3600, info.uptime%3600/60, info.uptime%60);
    printf("Total RAM: %ld MB\n", info.totalram/1024/1024);
    printf("Free RAM: %ld MB\n", (info.totalram-info.freeram)/1024/1024);
    printf("Shared RAM: %ld MB\n", info.sharedram/1024/1024);
    printf("Memory used by buffers: %ld MB\n", info.bufferram/1024/1024);
    printf("Free swap space size: %ld bytes\n", info.freeswap);
    printf("Process count: %d\n", info.procs);

   return 0;
 }

这是我的Makefile:

obj-m := diasdd.o 

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
        $(CC) testdiasdd.c -o test
clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
        rm test

0 个答案:

没有答案