Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2057117
  • 博文数量: 218
  • 博客积分: 5767
  • 博客等级: 大校
  • 技术积分: 5883
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-01 14:44
文章存档

2012年(53)

2011年(131)

2009年(1)

2008年(33)

分类: LINUX

2011-09-14 08:46:04

http://blog.chinaunix.net/space.php?uid=12567959&do=blog&id=161002


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()粗略地检查bufcount参数。

 

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系统调用大致也是同样的流程。
阅读(7113) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

anwengel2012-01-11 09:52:35

文中“由sys_read()服务例程来实现,其定义为:”
374 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
这个地方为什么这样定义呢?
这个服务例程是什么时候执行的?