萬盛學電腦網

 萬盛學電腦網 >> Linux教程 >> Linux操作系統內核和設備文件對話

Linux操作系統內核和設備文件對話

class="21442">

  設備文件是用來代表物理設備的。多數物理設備是用來進行輸出或輸入的,所以必須由某種機制使得內核中的設備驅動從進程中得到輸出送給設備。這可以通過打開輸出設備文件並且寫入做到,就想寫入一個普通文件。在下面的例子裡,這由device_write實現。

  這不是總能奏效的。設想你與一個連向modem的串口(技是你有一個內貓,從CPU看來它也是作為一個串口實現,所以你不需要認為這個設想太困難)。最自然要做的事情就是使用設備文件把內容寫到modem上(無論用modem命令還是電話線)或者從modem讀信息(同樣可以從modem命令回答或者通過電話線)。但是這留下的問題是當你需要和串口本身對話的時候需要怎樣做?比如發送數據發送和接收的速率。
  回答是Unix使用一個叫做ioctl(input output control的簡寫)的特殊函數。每個設備都有自己的ioctl命令,這個命令可以是ioctl讀的,也可以是寫的,也可以是兩者都是或都不是。Ioctl函數由三個參數調用:適當設備的描述子,ioctl數,和一個長整型參數,可以賦予一個角色用來傳遞任何東西。

  Ioctl數對設備主碼、ioctl類型、編碼、和參數的類型進行編碼。Ioctl數通常在頭文件由一個宏調用(_IO,_IOR,_IOW或_IOWR——決定於類型)。這個頭文件必須包含在使用ioctl(所以它們可以產生正確的ioctl's)程序和內核模塊(所以它可以理解)中。在下面的例子裡,這個頭文件是chardev.h,使用它的程序是ioctl.c。

  如果你希望在你自己的內核模塊中使用ioctl's,最好去接受一分正式的ioctl職位,這樣你就可以得到別人的ioctl's,或者他們得到你,你就可以知道哪裡出了錯誤。如果想得到更多的信息,到'documentation/ioctl-number.txt'中查看內核源文件樹。

ex chardev.c;

/* chardev.c;
*;
* Create an input/output character device;
*/;


/* Copyright (C) 1998-99 by Ori Pomerantz */;

/* The necessary header files */;

/* Standard in kernel modules */;
#include /* Were doing kernel work */;
#include /* Specifically, a module */;

/* Deal with CONFIG_MODVERSIONS */;
#if CONFIG_MODVERSIONS==1;
#define MODVERSIONS;
#include;
#endif;

/* For character devices */;

/* The character device definitions are here */;
#include;

/* A wrapper which does next to nothing at;
* 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;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0);
#include /* for get_user and put_user */;
#endif;

#define SUCCESS 0;


/* 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;

/* Is the device open right now? Used to prevent;
* concurent access into the same device */;
static int Device_Open = 0

/* The message the device will give when asked */;
static char Message[BUF_LEN]

/* How far did the process reading the message get?;
* 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;

/* We dont want to talk to two processes at the;
* same time */;
if (Device_Open);
return -EBUSY

/* If this was a process, we would have had to be;
* 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.;
*/;

Device_Open++

/* Initialize the message */;
Message_Ptr = Message

MOD_INC_USE_COUNT

return SUCCESS
};


/* 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;

/* Were now ready for our next caller */;
Device_Open --

MOD_DEC_USE_COUNT

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0);
return 0
#endif;
}

  

/* This function is called whenever a process which;
* 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

#ifdef DEBUG;
printk("device_read(%p,%p,%d)n",;
file, buffer, length)
#endif;

/* If were at the end of the message, return 0;
* (which signifies end of file) */;
if (*Message_Ptr == 0);
return 0

/* Actually put the data into the buffer */;
while (length && *Message_Ptr) {;

/* Because the buffer is in the user data segment,;
* 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 ++
};

#ifdef DEBUG;
printk ("Read %d bytes, %d leftn",;
bytes_read, length)
#endif;

/* Read functions are supposed to return the number;
* of bytes actually inserted into the buffer */;
return bytes_read
}

  

/* This function is called when somebody tries to;
* 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

#ifdef DEBUG;
printk ("device_write(%p,%s,%d)",;
file, buffer, length)
#endif;

for(i=0; i;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0);
get_user(Message, buffer+i)
#else;
Message = get_user(buffer+i)
#endif;

Message_Ptr = Message

/* Again, return the number of input characters used */;
return i
}

  

/* This function is called whenever a process tries to;
* 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

copyright © 萬盛學電腦網 all rights reserved