linux系統程式設計之檔案與IO(八):檔案描述符相關操作-dup,dup2,fcntl

mickole發表於2013-07-11

本節目標:

1,檔案共享

  • 開啟檔案核心資料結構
  • 一個程式兩次開啟同一個檔案
  • 兩個程式開啟同一個檔案

2,複製檔案描述符(dup、dup2、fcntl)

 

一,檔案共享

1,一個程式開啟兩個檔案核心資料結構

QQ截圖20130711103354

說明:

檔案描述符表:每個程式都有一張,彼此獨立,每個檔案描述符表項都指向一個檔案表,檔案描述符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、一個程式兩次開啟同一個檔案

QQ截圖20130711103437

當一個程式多次開啟同一個檔案時,首先會在描述符表順序查詢未被使用的描述符,然後每開啟一次建立一張檔案表,但各檔案表中的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;
}

執行結果:

QQ截圖20130711155856

說明:先建立test.txt檔案寫入hello,再同一個程式兩次開啟該檔案,可見每開啟一次檔案就引數一張檔案表,不共享偏移量,都開始位置讀取,之後利用第二個檔案描述符寫入AAAAA,在利用第一個描述符可以讀取出,表明都指向同一個v節點表,操作同一個檔案。

3,兩個不同程式開啟同一個檔案

QQ截圖20130711103452

當不同程式開啟同一個檔案時,每個程式首先在它們各自的檔案描述符表中順序查詢未被使用的描述符,最終獲得的檔案描述符可能相同也可能不同,每個fd指向各自的檔案表,但同樣,每個檔案表中的v節點指標都指向同一個v節點表。

 

二、複製檔案描述符

複製前:

QQ截圖20130711112146

 

複製後:

QQ截圖20130711112201

複製後,兩個檔案描述符都指向了同一個檔案表,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);
}

執行結果:

QQ截圖20130711161508

用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);
}

執行結果:

QQ截圖20130711162102

用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;
}

執行結果:

QQ截圖20130711163716

相關文章