全部博文(218)
分类: LINUX
2011-09-14 08:46:04
read()和write()系统调用非常相似。它们都需要三个参数:一个文件描述符fd,一个内存区的地址buf(该缓冲区包含接受的数据或者要传送的数据的存放位置),以及一个数count(指定应该传送多少字节)。两个系统调用都返回所成功传送的字节数,或者发送一个错误条件的信号并返回-1。
返回值小于count并不意味着发生了错误。即使请求的字节没有都被传送,也总是允许内核终止系统调用,因此用户应用程序必须检查返回值并重新发出系统调用(如果必要)。
一般会有这几种典型情况下返回小于count的值:当从管道或终端设备读取时,当读到文件的末尾时,或者当系统调用被信号中断时。文件结束条件(EOF)很容易从read()的空返回值中判断出来。这个条件不会与因信号引起的异常终止混淆在一起,因为如果读取数据之前read()被一个信号中断,则发生一个错误。
读或写操作总是发生在由当前文件指针所指定的文件偏移处(文件对象的f_pos字段)。两个系统调用都通过把所传送的字节数加到文件指针上而更新文件指针。
先来看read系统调用的实现,由sys_read()服务例程来实现,其定义为:
---------------------------------------------------------------------
fs/read_write.c
374 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
375 {
376 struct file *file;
377 ssize_t ret = -EBADF;
378 int fput_needed;
379
380 file = fget_light(fd, &fput_needed);
381 if (file) {
382 loff_t pos = file_pos_read(file);
383 ret = vfs_read(file, buf, count, &pos);
384 file_pos_write(file, pos);
385 fput_light(file, fput_needed);
386 }
387
388 return ret;
389 }
---------------------------------------------------------------------
它执行的步骤如下:
1、调用fget_light()从fd获取当前进程相应文件对象的地址file。
---------------------------------------------------------------------
fs/file_table.c
292 /*
293 * Lightweight file lookup - no refcnt increment if fd table isn't shared.
294 * You can use this only if it is guranteed that the current task already
295 * holds a refcnt to that file. That check has to be done at fget() only
296 * and a flag is returned to be passed to the corresponding fput_light().
297 * There must not be a cloning between an fget_light/fput_light pair.
298 */
299 struct file *fget_light(unsigned int fd, int *fput_needed)
300 {
301 struct file *file;
302 struct files_struct *files = current->files;
303
304 *fput_needed = 0;
305 if (likely((atomic_read(&files->count) == 1))) {
306 file = fcheck_files(files, fd);
307 } else {
308 rcu_read_lock();
309 file = fcheck_files(files, fd);
310 if (file) {
311 if (atomic_long_inc_not_zero(&file->f_count))
312 *fput_needed = 1;
313 else
314 /* Didn't get the reference, someone's freed */
315 file = NULL;
316 }
317 rcu_read_unlock();
318 }
319
320 return file;
321 }
---------------------------------------------------------------------
---------------------------------------------------------------------
include/linux/fdtable.h
82 static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)
83 {
84 struct file * file = NULL;
85 struct fdtable *fdt = files_fdtable(files);
86
87 if (fd < fdt->max_fds)
88 file = rcu_dereference_check_fdtable(files, fdt->fd[fd]);
89 return file;
90 }
---------------------------------------------------------------------
2、调用file_pos_read(file)获得当前的文件指针,并保存在局部变量pos中。
3、调用vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)来完成读操作。
---------------------------------------------------------------------
fs/read_write.c
278 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
279 {
280 ssize_t ret;
281
282 if (!(file->f_mode & FMODE_READ))
283 return -EBADF;
284 if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
285 return -EINVAL;
286 if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
287 return -EFAULT;
288
289 ret = rw_verify_area(READ, file, pos, count);
290 if (ret >= 0) {
291 count = ret;
292 if (file->f_op->read)
293 ret = file->f_op->read(file, buf, count, pos);
294 else
295 ret = do_sync_read(file, buf, count, pos);
296 if (ret > 0) {
297 fsnotify_access(file->f_path.dentry);
298 add_rchar(current, ret);
299 }
300 inc_syscr(current);
301 }
302
303 return ret;
304 }
---------------------------------------------------------------------
这个函数完成如下操作:
a.如果file->f_mode中的标志不允许所请求的读操作访问(file->f_mode没有设置FMODE_READ位),则返回一个错误码-EBADF。
b.如果文件对象没有read()及aio_read()文件操作,则返回一个错误码-EINVAL。
c.调用access_ok()粗略地检查buf和count参数。
d.调用rw_verify_area()对要访问的文件部分检查是否有冲突的强制锁。如果有,则返回一个错误码,如果该锁已经被F_SETLKW命令请求,那么就挂起当前进程。
e.调用file->f_op->read(如果已定义)来传送数据;否则,调用 do_sync_read(file, buf, count, pos),这个函数本质上调用file->f_op->aio_read来完成读操作。所有这些方法都返回实际传送的字节数。另一方面的作用是,文件指针被适当地更新。
4. 调用file_pos_write(file, pos)将保存在局部变量pos中的当前的文件指针写回。
5、调用fput_light()释放文件对象。
6、返回实际传送的字节数。
write系统调用大致也是同样的流程。