设备文件是用来代表物理设备的。多数物理设备是用来进行输出或输入的,所以必须由某种机制使得内核中的设备驱动从进程中得到输出送给设备。这可以通过打开输出设备文件并且写入做到,就想写入一个普通文件。在下面的例子里,这由device_write实现。 这不是总能奏效的。设想你与一个连向modem的串口(技是你有一个内猫,从CPU看来它也是作为一个串口实现,所以你不需要认为这个设想太困难)。最自然要做的事情就是使用设备文件把内容写到modem上(无论用modem命令还是电话线)或者从modem读信息(同样可以从modem命令回答或者通过电话线)。但是这留下的问题是当你需要和串口本身对话的时候需要怎样做?比如发送数据发送和接收的速率。 Ioctl数对设备主码、ioctl类型、编码、和参数的类型进行编码。Ioctl数通常在头文件由一个宏调用(_IO,_IOR,_IOW或_IOWR——决定于类型)。这个头文件必须包含在使用ioctl(所以它们可以产生正确的ioctls)程序和内核模块(所以它可以理解)中。在下面的例子里,这个头文件是chardev.h,使用它的程序是ioctl.c。 如果你希望在你自己的内核模块中使用ioctls,最好去接受一分正式的ioctl职位,这样你就可以得到别人的ioctls,或者他们得到你,你就可以知道哪里出了错误。如果想得到更多的信息,到documentation/ioctl-number.txt中查看内核源文件树。 ex chardev.c /* chardev.c /* The necessary header files */ /* Standard in kernel modules */ /* Deal with CONFIG_MODVERSIONS */ /* For character devices */ /* The character device definitions are here */ /* A wrapper which does next to nothing at #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) #define SUCCESS 0 /* Is the device open right now? Used to prevent /* The message the device will give when asked */ /* How far did the process reading the message get? /* We dont want to talk to two processes at the /* If this was a process, we would have had to be Device_Open++; /* Initialize the message */ MOD_INC_USE_COUNT; return SUCCESS; /* Were now ready for our next caller */ MOD_DEC_USE_COUNT; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* This function is called whenever a process which #ifdef DEBUG /* If were at the end of the message, return 0 /* Actually put the data into the buffer */ /* Because the buffer is in the user data segment, #ifdef DEBUG /* Read functions are supposed to return the number /* This function is called when somebody tries to #ifdef DEBUG for(i=0; i Message_Ptr = Message; /* Again, return the number of input characters used */ /* This function is called whenever a process tries to /* Switch according to the ioctl called */ /* Get the parameter given to ioctl by the process */ /* Find the length of the message */ /* Dont reinvent the wheel – call device_write */ case IOCTL_GET_MSG: /* Put a zero at the end of the buffer, so it case IOCTL_GET_NTH_BYTE: return SUCCESS; /* Module Declarations *************************** */ /* Register the character device (atleast try) */ /* Negative values signify an error */ printk (“%s The major device number is %d.\n”, return 0; /* Unregister the device */ /* If theres an error, report it */ /* chardev.h – the header file with the ioctl definitions. #ifndef CHARDEV_H #include /* The major device number. We cant rely on dynamic /* Get the message of the device driver */ ex ioctl.c /* ioctl.c – the process to use ioctls to control the /* Copyright (C) 1998 by Ori Pomerantz */ /* Functions for the ioctl calls */ ioctl_set_msg(int file_desc, char *message) ret_val = ioctl(file_desc, IOCTL_SET_MSG, message); if (ret_val < 0) { ioctl_get_msg(int file_desc) /* Warning – this is dangerous because we dont tell if (ret_val < 0) { printf(“get_msg message:%s\n”, message); ioctl_get_nth_byte(int file_desc) printf(“get_nth_byte message:”); i = 0; if (c < 0) { putchar(c); file_desc = open(DEVICE_FILE_NAME, 0); ioctl_get_nth_byte(file_desc); close(file_desc);
回答是Unix使用一个叫做ioctl(input output control的简写)的特殊函数。每个设备都有自己的ioctl命令,这个命令可以是ioctl读的,也可以是写的,也可以是两者都是或都不是。Ioctl函数由三个参数调用:适当设备的描述子,ioctl数,和一个长整型参数,可以赋予一个角色用来传递任何东西。
*
* Create an input/output character device
*/
/* Copyright (C) 1998-99 by Ori Pomerantz */
#include /* Were doing kernel work */
#include /* Specifically, a module */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include
#endif
#include
* at present, but may help for compatibility
* with future versions of Linux */
#include
/* Our own ioctl numbers */
#include “chardev.h”
/* In 2.2.3 /usr/include/linux/version.h includes a
* macro for this, but 2.0.35 doesnt – so I add it
* here if necessary. */
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif
#include /* for get_user and put_user */
#endif
/* Device Declarations ******************************** */
/* The name for our device, as it will appear in
* /proc/devices */
#define DEVICE_NAME “char_dev”
/* The maximum length of the message for the device */
#define BUF_LEN 80
* concurent access into the same device */
static int Device_Open = 0;
static char Message[BUF_LEN];
* Useful if the message is larger than the size of the
* buffer we get to fill in device_read. */
static char *Message_Ptr;
/* This function is called whenever a process attempts
* to open the device file */
static int device_open(struct inode *inode,
struct file *file)
{
#ifdef DEBUG
printk (“device_open(%p)\n”, file);
#endif
* same time */
if (Device_Open)
return -EBUSY;
* more careful here, because one process might have
* checked Device_Open right before the other one
* tried to increment it. However, were in the
* kernel, so were protected against context switches.
*
* This is NOT the right attitude to take, because we
* might be running on an SMP box, but well deal with
* SMP in a later chapter.
*/
Message_Ptr = Message;
}
/* This function is called when a process closes the
* device file. It doesnt have a return value because
* it cannot fail. Regardless of what else happens, you
* should always be able to close a device (in 2.0, a 2.2
* device file could be impossible to close). */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static int device_release(struct inode *inode,
struct file *file)
#else
static void device_release(struct inode *inode,
struct file *file)
#endif
{
#ifdef DEBUG
printk (“device_release(%p,%p)\n”, inode, file);
#endif
Device_Open –;
return 0;
#endif
}
* has already opened the device file attempts to
* read from it. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t device_read(
struct file *file,
char *buffer, /* The buffer to fill with the data */
size_t length, /* The length of the buffer */
loff_t *offset) /* offset to the file */
#else
static int device_read(
struct inode *inode,
struct file *file,
char *buffer, /* The buffer to fill with the data */
int length) /* The length of the buffer
* (mustnt write beyond that!) */
#endif
{
/* Number of bytes actually written to the buffer */
int bytes_read = 0;
printk(“device_read(%p,%p,%d)\n”,
file, buffer, length);
#endif
* (which signifies end of file) */
if (*Message_Ptr == 0)
return 0;
while (length && *Message_Ptr) {
* not the kernel data segment, assignment wouldnt
* work. Instead, we have to use put_user which
* copies data from the kernel data segment to the
* user data segment. */
put_user(*(Message_Ptr++), buffer++);
length –;
bytes_read ++;
}
printk (“Read %d bytes, %d left\n”,
bytes_read, length);
#endif
* of bytes actually inserted into the buffer */
return bytes_read;
}
* write into our device file. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t device_write(struct file *file,
const char *buffer,
size_t length,
loff_t *offset)
#else
static int device_write(struct inode *inode,
struct file *file,
const char *buffer,
int length)
#endif
{
int i;
printk (“device_write(%p,%s,%d)”,
file, buffer, length);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
get_user(Message, buffer+i);
#else
Message = get_user(buffer+i);
#endif
return i;
}
* do an ioctl on our device file. We get two extra
* parameters (additional to the inode and file
* structures, which all device functions get): the number
* of the ioctl called and the parameter given to the
* ioctl function.
*
* If the ioctl is write or read/write (meaning output
* is returned to the calling process), the ioctl call
* returns the output of this function.
*/
int device_ioctl(
struct inode *inode,
struct file *file,
unsigned int ioctl_num,/* The number of the ioctl */
unsigned long ioctl_param) /* The parameter to it */
{
int i;
char *temp;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
char ch;
#endif
switch (ioctl_num) {
case IOCTL_SET_MSG:
/* Receive a pointer to a message (in user space)
* and set that to be the devices message. */
temp = (char *) ioctl_param;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
get_user(ch, temp);
for (i=0; ch && ibr temp++) i++,> get_user(ch, temp);
#else
for (i=0; get_user(temp) && ibr temp++) i++,> ;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
device_write(file, (char *) ioctl_param, i, 0);
#else
device_write(inode, file, (char *) ioctl_param, i);
#endif
break;
/* Give the current message to the calling
* process – the parameter we got is a pointer,
* fill it. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
i = device_read(file, (char *) ioctl_param, 99, 0);
#else
i = device_read(inode, file, (char *) ioctl_param,
99);
#endif
/* Warning – we assume here the buffer length is
* 100. If its less than that we might overflow
* the buffer, causing the process to core dump.
*
* The reason we only allow up to 99 characters is
* that the NULL which terminates the string also
* needs room. */
* will be properly terminated */
put_user(\, (char *) ioctl_param+i);
break;
/* This ioctl is both input (ioctl_param) and
* output (the return value of this function) */
return Message[ioctl_param];
break;
}
}
/* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structure is kept in
* the devices table, it cant be local to
* init_module. NULL is for unimplemented functions. */
struct file_operations Fops = {
NULL, /* seek */
device_read,
device_write,
NULL, /* readdir */
NULL, /* select */
device_ioctl, /* ioctl */
NULL, /* mmap */
device_open,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
NULL, /* flush */
#endif
device_release /* a.k.a. close */
};
/* Initialize the module – Register the character device */
int init_module()
{
int ret_val;
ret_val = module_register_chrdev(MAJOR_NUM,
DEVICE_NAME,
&Fops);
if (ret_val < 0) {
printk (“%s failed with %d\n”,
“Sorry, registering the character device “,
ret_val);
return ret_val;
}
“Registeration is a success”,
MAJOR_NUM);
printk (“If you want to talk to the device driver,\n”);
printk (“youll have to create a device file. \n”);
printk (“We suggest you use:\n”);
printk (“mknod %s c %d 0\n”, DEVICE_FILE_NAME,
MAJOR_NUM);
printk (“The device file name is important, because\n”);
printk (“the ioctl program assumes thats the\n”);
printk (“file youll use.\n”);
}
/* Cleanup – unregister the appropriate file from /proc */
void cleanup_module()
{
int ret;
ret = module_unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
if (ret < 0)
printk(“Error in module_unregister_chrdev: %d\n”, ret);
}
ex chardev.h
*
* The declarations here have to be in a header file,
* because they need to be known both to the kernel
* module (in chardev.c) and the process calling ioctl
* (ioctl.c)
*/
#define CHARDEV_H
* registration any more, because ioctls need to know
* it. */
#define MAJOR_NUM 100
/* Set the message of the device driver */
#define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *)
/* _IOR means that were creating an ioctl command
* number for passing information from a user process
* to the kernel module.
*
* The first arguments, MAJOR_NUM, is the major device
* number were using.
*
* The second argument is the number of the command
* (there could be several with different meanings).
*
* The third argument is the type we want to get from
* the process to the kernel.
*/
#define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *)
/* This IOCTL is used for output, to get the message
* of the device driver. However, we still need the
* buffer to place the message in to be input,
* as it is allocated by the process.
*/
/* Get the nth byte of the message */
#define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int)
/* The IOCTL is used for both input and output. It
* receives from the user a number, n, and returns
* Message[n]. */
/* The name of the device file */
#define DEVICE_FILE_NAME “char_dev”
#endif
* kernel module
*
* Until now we could have used cat for input and
* output. But now we need to do ioctls, which require
* writing our own process.
*/
/* device specifics, such as ioctl numbers and the
* major device file. */
#include “chardev.h”
#include /* open */
#include /* exit */
#include /* ioctl */
{
int ret_val;
printf (“ioctl_set_msg failed:%d\n”, ret_val);
exit(-1);
}
}
{
int ret_val;
char message[100];
* the kernel how far its allowed to write, so it
* might overflow the buffer. In a real production
* program, we would have used two ioctls – one to tell
* the kernel the buffer length and another to give
* it the buffer to fill
*/
ret_val = ioctl(file_desc, IOCTL_GET_MSG, message);
printf (“ioctl_get_msg failed:%d\n”, ret_val);
exit(-1);
}
}
{
int i;
char c;
while (c != 0) {
c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);
printf(
“ioctl_get_nth_byte failed at the %dth byte:\n”, i);
exit(-1);
}
}
putchar(\n);
}
/* Main – Call the ioctl functions */
main()
{
int file_desc, ret_val;
char *msg = “Message passed by ioctl\n”;
if (file_desc < 0) {
printf (“Cant open device file: %s\n”,
DEVICE_FILE_NAME);
exit(-1);
}
ioctl_get_msg(file_desc);
ioctl_set_msg(file_desc, msg);
}
linux操作系统内核和设备文件对话_linux教程
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » linux操作系统内核和设备文件对话_linux教程
相关推荐
-      PHP排队机制,定时程序,linux服务器上运行PHP程序
-      nginx中伪静态的设置方法
-      nginx中给网站做301转向功能
-      让nginx每天自动分割日志文件
-      linux中的查找,删除,修改,移动,复制命令
-      lnmp服务器支持thinkphp5的配置方法
-      Nginx实现负载均衡配置实例
-      nginx中给网站做301转向功能