📄 8.html
字号:
<html><head><title>黄金书屋</title><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><link rel="stylesheet" href="/goldnets.css"></head><body bgcolor="#E4EBF1"><center><a href="http://ad.myrice.com/RealMedia/ads/click_nx.ads/goldnets.myrice.com/banner1@Top" target=_blanck ><script language=JavaScript><!---todayd = new Date();var seconds = todayd.getTime();document.write("<img src=\"http://ad.myrice.com/RealMedia/ads/adstream_nx.ads/goldnets.myrice.com/banner1@Top?dd=seconds\" border=0 width=468 height=60>");//--></script></a></center><br><table width="756" border="0" cellspacing="0" cellpadding="0" align="center" bgcolor="#E4EBF1"> <tr> <td colspan="2" valign="top" align="center"> <div align="center"> <table width="100%" border="0" cellspacing="0" cellpadding="0" height="52"> <tr> <td valign="top"><br> <div align="center"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td valign="bottom"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td><a href="/index.html">首页</a>>> <font color="#CC0000"><a href="/book/152/1015178.html">Linux内核编程</a></font></td> <td width="22%"> <a href="/index.html">[ 点此回首页 ]</a></td> </tr> <tr> <td colspan="2"><img src="/image/1x1.gif" width="1" height="2"></td> </tr> <tr bgcolor="#FFCC00"> <td colspan="2"><img src="/image/1x1.gif" width="1" height="1"></td> </tr> <tr> <td colspan="2"><img src="/image/1x1.gif" width="1" height="6"></td> </tr> </table> </td> </tr> </table> <br> <table width="590" border="0" cellspacing="0" cellpadding="0"> <tr> <td><center><a href='7.html'>上一页 </a>||<a href='9.html'>下一页</a></center><br><hr><div style=font-size:12pt><pre> 4.使用/proc进行输入
现在我们已经有了两种方法从内核模块中产生输出:注册一个设备驱动并且mknod一
个设备文件,或者创建一个/proc文件。这可以使内核告诉我们任何信息。现在的问题是我
们没有办法回答给内核。我们象内核输入的第一种方法是写给/proc文件。
因为proc文件系统主要是为满足内核向进程报告其状态的,没有为输入留出特别的规
定。数据结构proc_dir_entry没有包含一个指向某个输入函数的指针,就象指向输出函数那
样。如果我们要向一个/proc文件写入,我们需要使用标准文件系统机制。
在Linux里有一个文件系统注册的标准机制。每个文件系统都有自己的函数来处理索引
节点和文件操作,所以就有一个特殊的机构来存放指向所有函数的指针,struct
inode_operations,它有一个指向struct file_operations的指针。在/proc里,无论何时我们注
册一个新文件,我们就被允许指定用inod_operations访问哪个结构。这就是我们要用的机制,
一个inode_operations,包括一个指向file_operations的指针,file_operations里包含我们的
module_input和module_output函数。
必须指出标准的读写角色在内核中被倒置了,读函数用来输出,而写函数用来输入。
这是因为读和写是在用户的观点看,如果一个进程从内核中读取一些内容,那么内核就必须
输出处理。而进程要写入内核,内核就要接受输入。
另一个有趣的地方是module_permission函数。这个函数每当进程试图对/proc文件进行
处理时调用,它可以决定是否允许访问。目前这个函数只定义在操作和当前使用的uid(当
前可用的是一个指针指向一个当前运行进程的信息的结构)的基础上,但是它可以在我们希
望的任何事物的基础上定义,比如其他进程正在对文件做的操作,日期时间或者接收到的最
后一个输入。
使用put_usr和get_user的原因是Linux的内存是分段的(在Intel结构下,其他系列的
处理器下可能不同)。这意味着一个指针本身不代表内存中的一个唯一地址,而是段中的一
个地址,所以你还需要知道哪一个段可以使用它。内核占有一个段,每个进程都各占有一个
段。
一个进程可以访问的唯一的段就是它自己拥有的那个,所以当你写作为进程运行的程
序时可以不用关心段的问题。如果你要写内核模块,一般你希望访问内核的段,这由系统自
动处理。然而,如果内存缓冲区的内容需要在当前运行的进程和内核之间传递时,内核函数
会接到在此进程段里的指向内存缓冲区的一个指针。Put_user和get_user允许你访问那块内
存。
ex procfs.c
/* procfs.c - create a \"file\" in /proc, which allows
* both input and output. */
/* Copyright (C) 1998-1999 by Ori Pomerantz */
/* The necessary header files */
/* Standard in kernel modules */
#include <linux/kernel.h> /* We\'re doing kernel work */
#include <linux/module.h> /* Specifically, a module */
/* Deal with CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif
/* Necessary because we use proc fs */
#include <linux/proc_fs.h>
/* In 2.2.3 /usr/include/linux/version.h includes a
* macro for this, but 2.0.35 doesn\'t - 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 <asm/uaccess.h> /* for get_user and put_user */
#endif
/* The module\'s file functions ********************** */
/* Here we keep the last message received, to prove
* that we can process our input */
#define MESSAGE_LENGTH 80
static char Message[MESSAGE_LENGTH];
/* Since we use the file operations struct, we can\'t
* use the special proc output provisions - we have to
* use a standard read function, which is this function */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t module_output(
struct file *file, /* The file read */
char *buf, /* The buffer to put data to (in the
* user segment) */
size_t len, /* The length of the buffer */
loff_t *offset) /* Offset in the file - ignore */
#else
static int module_output(
struct inode *inode, /* The inode read */
struct file *file, /* The file read */
char *buf, /* The buffer to put data to (in the
* user segment) */
int len) /* The length of the buffer */
#endif
{
static int finished = 0;
int i;
char message[MESSAGE_LENGTH+30];
/* We return 0 to indicate end of file, that we have
* no more information. Otherwise, processes will
* continue to read from us in an endless loop. */
if (finished) {
finished = 0;
return 0;
}
/* We use put_user to copy the string from the kernel\'s
* memory segment to the memory segment of the process
* that called us. get_user, BTW, is
* used for the reverse. */
sprintf(message, \"Last input:%s\", Message);
for(i=0; i<len && message[i]; i++)
put_user(message[i], buf+i);
/* Notice, we assume here that the size of the message
* is below len, or it will be received cut. In a real
* life situation, if the size of the message is less
* than len then we\'d return len and on the second call
* start filling the buffer with the len+1\'th byte of
* the message. */
finished = 1;
return i; /* Return the number of bytes \"read\" */
}
/* This function receives input from the user when the
* user writes to the /proc file. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t module_input(
struct file *file, /* The file itself */
const char *buf, /* The buffer with input */
size_t length, /* The buffer\'s length */
loff_t *offset) /* offset to file - ignore */
#else
static int module_input(
struct inode *inode, /* The file\'s inode */
struct file *file, /* The file itself */
const char *buf, /* The buffer with the input */
int length) /* The buffer\'s length */
#endif
{
int i;
/* Put the input into Message, where module_output
* will later be able to use it */
for(i=0; i<MESSAGE_LENGTH-1 && i<length; i++)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
get_user(Message[i], buf+i);
/* In version 2.2 the semantics of get_user changed,
* it not longer returns a character, but expects a
* variable to fill up as its first argument and a
* user segment pointer to fill it from as the its
* second.
*
* The reason for this change is that the version 2.2
* get_user can also read an short or an int. The way
* it knows the type of the variable it should read
* is by using sizeof, and for that it needs the
* variable itself.
*/
#else
Message[i] = get_user(buf+i);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -