本節目標:
1,檔案共享
- 開啟檔案核心資料結構
- 一個程式兩次開啟同一個檔案
- 兩個程式開啟同一個檔案
2,複製檔案描述符(dup、dup2、fcntl)
一,檔案共享
1,一個程式開啟兩個檔案核心資料結構
說明:
檔案描述符表:每個程式都有一張,彼此獨立,每個檔案描述符表項都指向一個檔案表,檔案描述符0(STDIN_FILENO)、1(STDOUT_FILENO)、2(STDERR_FILENO),預設已經開啟,分別表示:標準輸入,標準輸出,標準錯誤裝置。
檔案表:每開啟一個檔案就對應一張檔案表,檔案表可以共享,當多個檔案描述符指向同一個檔案表時,檔案表中的
refcnt欄位會相應變化。檔案狀態標識:檔案的開啟模式(R,W,RW,APPEND,NOBLOCK,等),當前檔案偏移量,refcnt:被引用數量,
v節點指標:指向一個v節點表。
v節點表:每個檔案對應一個,無論被被多少個程式開啟都只有一個,它包括v節點資訊(主要是stat結構體中的資訊),i節點資訊。
每個程式預設只能開啟1024個檔案描述符,當一個程式開啟一個檔案時,預設會從0開始查詢未被使用的描述符,由於0,1,2預設被佔用,所有一般從3開始使用。
2、一個程式兩次開啟同一個檔案
當一個程式多次開啟同一個檔案時,首先會在描述符表順序查詢未被使用的描述符,然後每開啟一次建立一張檔案表,但各檔案表中的v節點指標都指向同一個v節點表。
示例程式:
#include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { int fd1; int fd2; char buf1[1024] = {0}; char buf2[1024] = {0}; fd1 = open("test.txt", O_RDONLY); if (fd1 == -1) ERR_EXIT("open error"); read(fd1, buf1, 5); printf("buf1=%s\n", buf1); fd2 = open("test.txt", O_RDWR); if (fd2 == -1) ERR_EXIT("open error"); read(fd2, buf2, 5); printf("buf2=%s\n", buf2); write(fd2, "AAAAA", 5); memset(buf1, 0, sizeof(buf1)); read(fd1, buf1, 5); printf("buf1=%s\n", buf1); close(fd1); close(fd2); return 0; }
執行結果:
說明:先建立test.txt檔案寫入hello,再同一個程式兩次開啟該檔案,可見每開啟一次檔案就引數一張檔案表,不共享偏移量,都開始位置讀取,之後利用第二個檔案描述符寫入AAAAA,在利用第一個描述符可以讀取出,表明都指向同一個v節點表,操作同一個檔案。
3,兩個不同程式開啟同一個檔案
當不同程式開啟同一個檔案時,每個程式首先在它們各自的檔案描述符表中順序查詢未被使用的描述符,最終獲得的檔案描述符可能相同也可能不同,每個fd指向各自的檔案表,但同樣,每個檔案表中的v節點指標都指向同一個v節點表。
二、複製檔案描述符
複製前:
複製後:
複製後,兩個檔案描述符都指向了同一個檔案表,refcnt=2。
複製檔案描述符有三種方法:
1,dup
2,dup2
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
DESCRIPTION
These system calls create a copy of the file descriptor oldfd.
dup() uses the lowest-numbered unused descriptor for the new descriptor.
dup2() makes newfd be the copy of oldfd, closing newfd first if necessary, but note
the following:
* If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
* If oldfd is a valid file descriptor, and newfd has the same value as
oldfd, then dup2() does nothing, and returns newfd.
After a successful return from one of these system calls, the old and new file descriptors may be used interchangeably. They refer to the same open file description (see open(2)) and thus share file offset and file status flags; for example, if the file offset is modified by using lseek(2) on one of the descriptors, the offset is also changed for the other.
RETURN VALUE
On success, these system calls return the new descriptor. On error, -1 is returned, and errno is set appropriately.
示例程式:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fd; fd = open("test.txt",O_WRONLY); if( fd == -1){ perror("open error"); exit(EXIT_FAILURE); } int fd2; fd2 = dup(fd); if(fd2 == -1){ perror("dup error"); exit(EXIT_FAILURE); } printf("oldfd = %d\n",fd); printf("newfd = %d\n",fd2); int fd3; close(0); //或者close(STDIN_FILENO) fd3 = dup(fd); if(fd3 == -1){ perror("dup error"); exit(EXIT_FAILURE); } printf("after close stdin,the newfd = %d\n",fd3); exit(EXIT_SUCCESS); }
執行結果:
用dup進行檔案描述符的複製時,順序查詢即從0開始查詢可以檔案描述符
示例2:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fd; fd = open("test.txt",O_WRONLY); if( fd == -1){ perror("open error"); exit(EXIT_FAILURE); } int fd2; fd2 = dup2(fd,0); if(fd2 == -1){ perror("dup error"); exit(EXIT_FAILURE); } printf("oldfd = %d\n",fd); printf("newfd = %d\n",fd2); exit(EXIT_SUCCESS); }
執行結果:
用dup2進行檔案描述符複製時,指定需要複製的新的描述符,如果該描述符已經被佔用,則先關閉它在重新複製,類似於先呼叫close再dup
3,fcntl
功能:操縱檔案描述符,改變已開啟的檔案的屬性
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
DESCRIPTION
fcntl() performs one of the operations described below on the open file
descriptor fd. The operation is determined by cmd.
fcntl() can take an optional third argument. Whether or not this argu-
ment is required is determined by cmd. The required argument type is
indicated in parentheses after each cmd name (in most cases, the
required type is long, and we identify the argument using the name
arg), or void is specified if the argument is not required.
由第二個引數指定操作型別,後面點的可變引數指定該命令所需的引數
這裡我們進行檔案描述符複製,可將cmd 設為: F_DUPFD (long),該命令表示:
Find the lowest numbered available file descriptor greater than
or equal to arg and make it be a copy of fd. This is different
from dup2(2), which uses exactly the descriptor specified.
On success, the new descriptor is returned.
示例程式:
#include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { int fd; fd = open("test.txt", O_WRONLY); if (fd == -1) ERR_EXIT("open error"); close(1); if (fcntl(fd, F_DUPFD, 0) < 0) ERR_EXIT("dup fd error"); printf("-----------\n");//進行重定向,將不會顯示在標準輸出, return 0; }
執行結果: