unix环境高级编程-3.10-文件共享 跟原子操作

unix环境高级编程-3.10-文件共享 和原子操作

http://blog.csdn.net/wallwind/article/details/6882498

 

  unix系统支持在不同进程间共享打开的文件。

   内核使用三种数据结果表示打开的文件。

(1)每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件的描述符表,可将其视为一个矢量,每个描述符占用一项。

   每个文件描述符相关连的是:

  •        文件描述符标志
  •       指向一个文件表项的指针。

(2)内核为所有打开文件维持一张文件表。每个文件表项包含:

  •         文件状态标志(包含读写,填写,同步,非阻塞等)
  •         当前文件偏移量
  •         指向该文件v节点表项的指针。

(3)每个打开的文件都有一个v节点结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针。对于大多数文件,v节点还包括了该文件的i节点。这些信息是再打开文件时从磁盘上读入内存的。这些文件都是从磁盘读入内存的,所以可以快速使用这些参数。

note: linux没有使用v节点,而是使用了通用的i节点。

unix环境高级编程-3.10-文件共享 跟原子操作

 

此图为打开文件的内核数据结构,该进程有两个不同的打开文件,一个文件打开为标准输入,另一个打开为标准输出。

 

如果两个独立进程各自打开同一个文件,则有如图所示unix环境高级编程-3.10-文件共享 跟原子操作

 

我们假设第一个进程在文件描述符3打开该文件,而另一个进程在文件描述符4上打开该文件。打开该文件的每个进程都得到一个文件表项。但对一个给定的文件只有一个v节点表项。每个进程都有自己的一个文件表项,理由是每个进程都要有对一个文件的偏移量。

下面描述上图的产生事件:

  • 在完成每个write后,在文件表项中的当前文件偏移量增加所写的字节数,如果这是当前文件偏移量超过了当前的文件长度,则在i节点表项忠的当前文件长度被设置为当前文件偏移量。
  • 如果用O_APPEND标志打开一个文件,则相应标志也被设置到文件表现的文件状态标志中。每次对这种具有填写标志的文件执行写操作时候,在文件表项中的当前文件偏移量标志中,首先被设置为i节点表项忠的文件长度,这就似的每次写的数据都添加到文件的当前尾端。
  • 若一个文件用lseek函数丁文到文件的当前的尾端。则文件表项中的当前文件偏移量被设置为i节点表项中的当前文件长度。
  • lseek函数只修改文件表系那个忠的当前文件偏移量。没有进行任何io操作

可能有多个文件描述符指向同一个文件表项

fork 后也会发生同样的情况,此时父子进程对于每个打开文件描述符共享同一个文件表项。

note:文件描述符标志和恩见状态标志在作用域方面的区别。潜质只用与一个进程的一个进程描述符。而后者则使用语指向改给定文件表项的任何进程中的所有描述符。

 

当我们看到多个进程进行对一个文件同时操作的时候,这时候就产生了冲突,如何避免呢,那么就是下一节原子操作~

 

 

 

 

 

===============原子操作

 

1.填写至一个文件

早期的unix系统将数据添加文件尾端如下代码

  1. if (lseek(fd,0l,2)<0  
  2.     err_sys("lseek error" );  
  3. if (write(fd,buf,100)!=0  
  4.     err_sys("write error" );  


对单个进程可正常工作,但是多个进程就不能了。

UNIX提供了一种方法是这种操作成为原子操作,该方法是在打开文件时设置O_APPEND标志。就是每次写文件的时候,都该文件的偏移量移动到尾端。

 

 

2.pread和pwrite函数

  1. ssize_t pread ( int  filedes,  void  *buffer,  size_t  size, off_t offset)  
  2.                                                           ‘unistd.h’ (Unix98): Section 13.2 [Input and Output Primitives], page 299.  
  3. ssize_t pread64 (int  filedes,  void  *buffer,  size_t  size, off64_t offset)  
  4.                                                            ‘unistd.h’ (Unix98): Section 13.2 [Input and Output Primitives], page 299.  


pread返回值:返回字节数,若已经到文件结尾则返回0,出错返回-1;

pwrite返回值:若成功返回字节数,出错返回-1;

 

调用pread,相当于顺序调用lseek 和read,但是pread 又与这种顺序调用下列有重要区别。

  • 调用pread时候,无法终端其定位和读操作。
  • 不更新文件指针。

 

调用pwrite相当于顺序调用lseek 和write,原理同上

 

3.创建一个文件

在对open函数的O_CREAT和O_EXCL选项进行说明时候就是原子操作。

下面是截取gun c手册的。

int open (const char *filename, int flags[, mode t mode]) [Function]
The open function creates and returns a new file descriptor for the file named by
filename. Initially, the file position indicator for the file is at the beginning of the file.
The argument mode is used only when a file is created, but it doesn’t hurt to supply
the argument in any case.
The flags argument controls how the file is to be opened. This is a bit mask; you create
the value by the bitwise OR of the appropriate parameters (using the ‘|’ operator in
C). See Section 13.14 [File Status Flags], page 333, for the parameters available.
The normal return value from open is a non-negative integer file descriptor. In the
case of an error, a value of