我正在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