在LDD3(Linux设备驱动程序第三版)scullpipe
中,static int scull_read_p_mem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
中的参数是什么意思?具体来说,我不了解start
,page
和offset
之间的区别。
我对实际的实现本身有很多疑问(可以在下面找到):
struct scull_pipe {
wait_queue_head_t inq, outq; /* read and write queues */
char *buffer, *end; /* begin of buf, end of buf */
int buffersize; /* used in pointer arithmetic */
char *rp, *wp; /* where to read, where to write */
int nreaders, nwriters; /* number of openings for r/w */
struct fasync_struct *async_queue; /* asynchronous readers */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
int scull_p_nr_devs; /* the number of scull_pipe devices */
scull_pipe *scull_p_devices; /* scull_pipe devices to be malloc'ed */
/* ...... */
/* our proc read implementation */
static int scull_read_p_mem(char *buf, char **start, off_t offset, int count,
int *eof, void *data)
{
int i, len;
struct scull_pipe *p;
#define LIMIT (PAGE_SIZE-200) /* don't print any more after this size */
*start = buf;
len = sprintf(buf, "Default buffersize is %i\n", scull_p_buffer);
for(i = 0; i < scull_p_nr_devs && len <= LIMIT; i++) {
p = &scull_p_devices[i];
if (down_interruptible(&p->sem))
return -ERESTARTSYS;
len += sprintf(buf+len, "\nDevice %i: %p\n", i, p);
len += sprintf(buf+len, " Buffer: %p to %p (%i bytes)\n",
p->buffer, p->end, p->buffersize);
len += sprintf(buf+len, " rp %p wp %p\n", p->rp, p->wp);
len += sprintf(buf+len, " readers %i writers %i\n",
p->nreaders, p->nwriters);
up(&p->sem);
scullp_proc_offset(buf, start, &offset, &len);
}
*eof = (len <= LIMIT);
return len;
}
static void scullp_proc_offset(char *buf, char **start, off_t *offset, int *len)
{
/* QUESTION: what does this function do? */
if (*offset == 0)
return;
if (*offset >= *len) {
*offset -= *len; /* QUESTION: what is the purpose of this? */
*len = 0;
}
else {
*start = buf + *offset; /* QUESTION: why do you need to change "start"? */
*offset = 0;
}
}
答案 0 :(得分:1)
函数scull_read_p_mem用于通过here函数创建proc条目create_proc_read_entry。 5分钟的Google搜索给出了this page,它解释了传递给create_proc_read_entry
函数的函数指针中的参数。使用固定格式的sais:
参数:
* buf:内核为尝试读取proc条目的任何进程分配一个内存页。页面指针指向该缓冲区 写入数据的内存大小。
** start:当proc文件的读取不应该从文件的开头而是一定的偏移量开始时,使用此指针。对于小读,通常将其设置为NULL。
off:文件指针当前指向的从文件开头的偏移量
count:要读取的数据字节数
data:从create_read_proc_entry函数调用传递的数据。
eof:设置为1表示文件结束
但是过了一会儿,我还在kenel fs/proc/generic.c中找到了一些文档。它有点长,但是我认为它是总结start
参数的唯一来源:
/*
* How to be a proc read function
* ------------------------------
* Prototype:
* int f(char *buffer, char **start, off_t offset,
* int count, int *peof, void *dat)
*
* Assume that the buffer is "count" bytes in size.
*
* If you know you have supplied all the data you
* have, set *peof.
*
* You have three ways to return data:
* 0) Leave *start = NULL. (This is the default.)
* Put the data of the requested offset at that
* offset within the buffer. Return the number (n)
* of bytes there are from the beginning of the
* buffer up to the last byte of data. If the
* number of supplied bytes (= n - offset) is
* greater than zero and you didn't signal eof
* and the reader is prepared to take more data
* you will be called again with the requested
* offset advanced by the number of bytes
* absorbed. This interface is useful for files
* no larger than the buffer.
* 1) Set *start = an unsigned long value less than
* the buffer address but greater than zero.
* Put the data of the requested offset at the
* beginning of the buffer. Return the number of
* bytes of data placed there. If this number is
* greater than zero and you didn't signal eof
* and the reader is prepared to take more data
* you will be called again with the requested
* offset advanced by *start. This interface is
* useful when you have a large file consisting
* of a series of blocks which you want to count
* and return as wholes.
* (Hack by Paul.Russell@rustcorp.com.au)
* 2) Set *start = an address within the buffer.
* Put the data of the requested offset at *start.
* Return the number of bytes of data placed there.
* If this number is greater than zero and you
* didn't signal eof and the reader is prepared to
* take more data you will be called again with the
* requested offset advanced by the number of bytes
* absorbed.
*/
我们可以see start
之后在copy_to_user
内使用-该参数用于优化biiiig文件上的proc
条目读取。用户可以传递很小的count
变量,但是您需要读取biig文件。因此,您可以使用*start
参数从proc读取函数返回该文件的大小,该参数表示要读取多少字节。这样,内核甚至可以传递count=0
,但是proc_read函数可以像5000
一样返回带有有效*start
地址的地址,稍后将在copy_to_user
调用中使用它来加快处理速度。阅读。
所以:
静态int scull_read_p_mem(char * buf,char ** start,off_t偏移量,int count,int * eof,void * data)中的参数是什么意思?
buf
-将结果复制到的目标缓冲区start
-上面的注释中解释的魔术指针用于加快proc读取速度。offset
-要读取的文件中的偏移量count
-要读取的字节数eof
-指向int的指针,需要将其设置为非零,以防读取整个文件data
-用户上下文,作为create_proc_entry
函数中的最后一个参数传递。我对实际的实现本身有很多疑问(可以在下面找到):
scullp_proc_offset
操纵len
缓冲区内的buf
偏移量。如果为offset != 0
,则scull_read_p_mem
不需要从第一个字节读取,而需要从某个字节offset
读取。由于它是延迟写入的,因此无论如何都会执行snprintf
调用,因此您需要“有点移位”缓冲区。
what does this function do?
-实际上,我发现这是一种很有趣的方法,它可以计算要/需要复制到用户的字节数。
what is the purpose of this?
-不知道。看起来很麻烦,因为*offset
会变成负数。函数/* FIXME this should use seq_file */
上方的注释指出,尚有一些问题需要解决。我认为想法是在一次调用中准确返回有关一个scull_p_devices[i]
的信息。
why do you need to change "start"?
-涉及到这一点。如果*offset
不等于0,并且还有一些字节要读取,则应该返回一个指向buf + offset
的指针,以使内核知道从何处读取。请注意,*start = buf
已被初始化,因此内核将执行copy_to_user(... *start, len)
。
答案 1 :(得分:0)
buf
指向存储页面的底部,应该在其中存储读取结果。
offset
建议从虚拟文件中开始读取。
start
作为返回值可能意味着几件事:
if start == null
,我们已将结果写入buf + offset if start == some int <
buf地址,我们已将结果写入buf if start == some
地址在buf中,我们已经写出要开始的结果该函数应解释如下。
// context:
// 1. We have set start to be the beginning of buf
// *start = buf;
// 2. We have fill some string into the buf and increment len
static void scullp_proc_offset(char *buf, char **start, off_t *offset, int *len)
{
// If offset is zero, we are filling a new buf.
// (Or we have finished catching up as shown below)
// We can continue to do so until we reach buf size
if (*offset == 0)
return;
// Otherwise, we have some offset to catch up to.
if (*offset >= *len) {
// The offset might be still ahead given we have filled len.
// So, we reduce offset(meaning that we have done some catch up)
// and reset len(because we are still working on old data)
*offset -= *len;
*len = 0;
}
else {
// Or, the offset might be already behind us
// So, we set start to indicate that the write starts from this address
// (case 2 in fs/proc/generic.c#L76)
// and reset offset (meaning that we are done with catching up)
*start = buf + *offset;
*offset = 0;
// this part might lack the handling of len
// but it might be true if the structure is the same between two calls.
}
}
最后,作者使用此函数来使写操作始终始于buf
,但在追上之前的偏移量后才计数len。