1. 程式人生 > >linux常用文件I/O操作之文件共享的實現方式

linux常用文件I/O操作之文件共享的實現方式

文件共享dup和dup2函數fcntl函數




1、文件共享的三種實現方式

1、什麽是文件共享:

(1)文件共享就是同一個文件(同一個文件指的是同一個inode,同一個pathname)被多個獨立的讀寫體(幾乎可以理解為多個文件描述符)去同時(一個打開尚未關閉的同時另一個去操作)操作。

(2)文件共享的意義有很多:譬如我們可以通過文件共享來實現多線程同時操作同一個大文件,以減少文件讀寫時間,提升效率。

2、文件共享的核心就是怎麽弄出來多個文件描述符指向同一個文件。

3、常見的三種文件共享情況:

1、是同一個進程中多次使用open函數打開同一個文件。

2、不同進程中分別使用open函數打開同一個文件(因為此時兩個fd不在同意進程中,所以兩個fd可能相同,也可能不同)。

3、linux提供的dup和dup2兩個API來讓進程賦值文件描述符。

4、我們分析文件共享的核心關註點在於:分別寫、讀還是連續讀寫

補充:

再論文件描述符:

1、文件描述符的本質是一個數字,這個數字本質上是進程表中文件描述符的一個表項,進程通過文件描述符作為index去索引查表得到文件表指針,再間接訪問得到這個文件對應的文件表。

2、文件描述符這個數字是open系統調用內部由操作系統自動分配的,操作系統分配這個fd也不是隨機分配的,也會按照一定的規律。

3、操作系統規定,fd從0開始一次增加,fd也是有最大限制的,在linux的早期版本中fd最大只有20.linux中文件描述符表是一個數組(而不是鏈表),所以這個文件描述符表其實就是一個數組,fd是index,文件表指針是value

4、當我們open時,內核會從文件描述符表中挑選一個最小的未被使用的數字給我們返回。

文件描述符的復制:

5、fd中0、1、2已經默認被系統占用了,當我們運行一個程序得到一個進程時,內部就默認已經打開了3個文件,這三個文件對應的fd就是0、1、2。這三個文件分別叫stdin、stdout、stderr。也就是標準輸入、標準輸出、標準錯誤。因此用戶進程得到的最小的fd就是3了。

6、標準輸入一般對應的是鍵盤(可以理解為:0這個fd對應的是鍵盤的設備文件),標準輸出一般是LCD顯示器(可以理解為:1對應LCD的設備文件)

7、printf函數其實就是默認輸出到標準輸出stdout上了。stdio中還有一個函數叫fpirntf,這個函數就可以指定輸出到哪個文件描述符中。

2、如何實現文件共享

1、同一個進程中多次使用open函數打開同一個文件。同時打開同一個文件不難理解,關鍵在於打開同一個文件後,我們讀取寫入文件時,是分別寫呢還是接續寫,簡單理解就是,覆蓋著寫呢?還是一個寫完了,文件指針自動挪到後邊分開寫?我們不用去猜,記別人的理論,我們直接自己用代碼測試,只要代碼正確的情況測試的結果就是理論。下來我們來看測試代碼:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>


typedef int file_t;  
#define MAXLENG 1024

int main(int argc,char *argv[])
{
   file_t  fd1 = -1, fd2 = -1;
  	
   fd1 = open("./1.txt",O_RDWR | O_TRUNC | O_CREAT,0644);

   if(fd1 < 0)
   {
	   perror("fd1 :open file failed :");
	   _exit(-1);
   }

   fprintf(stdout,"fd1 = %d\n",fd1);
   
   
//這裏為了區別更明顯,	fd2的open以及判斷完全可以放在fd1的判斷中去
   fd2 = open("./1.txt",O_RDWR | O_TRUNC | O_CREAT,0644);

   if(fd2 < 0)
   {
	   perror("fd2:open file fialed :");
	   _exit(-1);
    }

   fprintf(stdout,"fd2 = %d\n",fd2);

   while(1)
   {
	  
	   write(fd1,"aaaa",4);
	   sleep(1);
	   write(fd2,"bbbb",4);
   }
   
   return 0;

}

這段代碼運行完成以後,我們來看結果

[[email protected]_k filetest]# cat 1.txt 
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaa[[email protected]_k filetest]#

這樣我們可以很容易看到,我們雖然是用兩個文件指針分別寫,但是fd1只有在最後一次寫進去,對吧?這時我們看到的;但是這為什麽會這樣呢?這裏我們分析一下:其實fd1進行了寫入操作,只是我們fd1和fd2兩個文件指針是不同的文件指針,當fd1寫入以後,文件指針往後移動,但是fd2並沒有移動,當fd2進行寫入時就會覆蓋掉fd1寫入的內容,並且這裏與你兩次打開文件的屬性有關,這裏使用

O_RDWR | O_TRUNC | O_CREAT三個屬性打開,我們也可以自己測試其他屬性打開的結果。雖然不同屬性打開的結果可能造成的結果不同,但是我們這裏是可以確定的,一個文件指針移動後並不會造成另外一個文件指針移動

2、第二種方法用不同進程中分別使用open函數打開同一個文件(因為此時兩個fd不在同意進程中,所以兩個fd可能相同,也可能不同)。這裏還沒有總結fork函數產生新的進程。所以先不總結,博主後邊補充上去。


3、使用linux提供的dup和dup2兩個API來讓進程賦值文件描述符。

1、使用dup進行文件描述符復制

(1)dup系統調用對fd進行復制,會返回一個新的文件描述符(譬如原來的fd是3,返回的就是4)

(2)dup系統調用有一個特點,就是自己不能指定復制後得到的fd的數字是多少,而是由操作系統內部自動分配的,分配的原則遵守fd分配的原則。

(3)dup返回的fd和原來的oldfd都指向oldfd打開的那個動態文件,操作這兩個fd實際操作的都是oldfd打開的那個文件。實際上構成了文件共享。

(4)dup返回的fd和原來的oldfd同時向一個文件寫入時,結果是分別寫還是接續寫?

使用dup的缺陷分析

(1)dup並不能指定分配的新的文件描述符的數字,dup2系統調用修復了這個缺陷,所以平時項目中實際使用時根據具體情況來決定用dup還是dup2.

2、使用dup2進行文件描述符復制

(1)dup2和dup的作用是一樣的,都是復制一個新的文件描述符。但是dup2允許用戶指定新的文件描述符的數字。

(2)使用方法看man手冊函數原型即可。

測試使用dup和dup2共享文件操作:

1、dup共享文件的實現

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>

typedef int file_t;
#define MAXSIZE 1024

int main(int argc,char *argv[])
{
	file_t fd1 = -1, fd2 = -1;
	fd1 = open("./3.txt",O_RDWR | O_CREAT | O_TRUNC,0644);

	if(fd1 < 0)
	 {
		perror("open fiel error :");
		_exit(-1);
	}
	
	fprintf(stdout,"fd1 = %d\n",fd1);

	fd2 = dup(fd1);

	if(-1 == fd2)
	{
		perror("dup error:");
		_exit(-1);
 	}

	fprintf(stdout,"fd2 = %d\n",fd2);

	while(1)
	{
		write(fd1,"aaaa",4);
		sleep(1);
		write(fd2,"bbbb",4);
	
	}



	return 0;
}

我們繼續來看結果:

[[email protected]_k filetest]# cat 3.txt 
aaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaa
aabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaa
[[email protected]_k filetest]#

從結果我們這次可以發現a和b是交替出現的,這也符合預期,dup雖然是產生了兩個文件描述符,但是共用同一個文件指針,當一個寫入以後,文件指針已經移動到後邊去,自然不會覆蓋掉了。


2、dup2共享文件交叉寫入測試

1、對於dup2和dup寫入文件一樣,區別在於dup2可以自己指定文件描述符,所以這裏就不給出dup2寫入文件的實例。

2、交叉寫入的時候,結果是接續寫

3、fcntl函數

1、fcntl函數是一個多功能文件管理的工具箱,可以接受2個參數+1個變參,第一個參數是fd表示喲啊操作那個文件,第二個參數是cmd表示要進行那個命令操作。變參是用來傳遞參數的,要配合cmd命令使用

2、cmd的樣子類似於F_XXX,不同的cmd具有不同的功能。學習時,沒有必要把所有的cmd含義弄清楚,只需要把一個弄明白就行,其他的查man手冊即可。

3、常用fcntl的cmd

1、F_DUPFD這個cmd的作用是復制文件描述符(作用類似於dup和dup2),這個命令的功能是從可用的fd數字列表中找一個比arg大或者和arg一樣大的數字作為oldfd的一個復制的fd,和dup2有點像但是不同。dup2返回的就是我們指定的那個newfd否則就會出錯,但是F_DUPFD命令返回的是>=arg的最小的那一個數字。

4、標準I/O庫

1、標準IO和文件IO有什麽區別

1、最大的區別:標準I/O庫是C庫函數,而文件I/O是linux系統調用的API

2、C語言庫函數是由API封裝起來的,庫函數內部也是通過調用API來完成操作的,但是庫函數多了一層封裝,所以比API更加好用。

3、庫函數比API還有一個優勢,API在不同的操作系統的接口是不一樣的是不能通用的,但是C庫函數在不同操作系統上幾乎是一樣的,所以C庫函數具有可移植性而API不具有可移植性。

2、常用標準IO函數介紹:

常見的標準IO庫函數:fopen fclose fwrite fread ffulsh、fseek


以上文檔,本人學習參考了不同老師的文檔自己總結而來,代碼都是本人親自測試。關於文檔那塊有問題,請各位指正。

本文出自 “12162969” 博客,請務必保留此出處http://12172969.blog.51cto.com/12162969/1959060

linux常用文件I/O操作之文件共享的實現方式