Linux操作系統-文件(2)

Linux操作系統—文件(2)(2015-8-17)
分類:Linux操作系統
二:底層文件訪問
文件描述符
  文件描述符是一個非負整數。對內核而言,所有打開的文件都由文件描述符引用。 
  當打開一個現存文件或者創建一個新的文件時,內核向進程返回一個文件描述符。當讀,寫一個文件時,用open或者create返回的文件描述符標識該文件,將其作爲參數傳遞給read或者write。從內核源碼的角度來看,文件描述符其實是當前進程所打開的文件結構數組的下標。 
  按照慣例,UNIX/Linux Shell使文件描述符0與進程的標準輸入相結合,文件描述符1與標準輸出相結合,文件描述符2與標準出錯相結合。在頭文件< unistd.h> 中定義了常量STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO,其值分別爲0,1,2。 
  一個進程能打開的文件數由< limits.h >文件中的OPEN_MAX限定。

文件的創建,打開
  調用open函數可以打開或創建一個文件,其原型如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
1
2
3
4
5
疑惑:這是什麼?函數重載麼?可函數重載不是C++裏面的東西嗎?

  打開一個文件後,即在文件描述符和文件之間建立了一個關聯。open函數既可以打開一個已近存在的文件,也可以創建一個新的文件。具體執行打開操作還是創建操作由flag參數指定。open函數各參數和返回值的含義如下: 
1. pathname:要打開或創建的文件的名字 
2. flags:由下列一個或多個常數進行或運算構成

- O_RDONLY:只讀打開
- O_WRONLY:只寫打開
- O_RDWR:讀,寫打開
- APPEND:每次寫時追加到文件的尾端
- O_CREAT:若此文件不存在則創建它,應結合第三個參數mode使用
- O_EXCL:結合O_CREATE,當文件不存在時,才創建文件
- O_TRUNC:如果此文件存在,而且爲只讀或只寫則將其長度截斷爲0
1
2
3
4
5
6
7
mode:存取許可權位,一個32位無符號整數,僅當創建新文件時才使用,由下列一個或多個常數進行或運算構成。應注意,最終文件權限受系統變量umask限制,是所設權限和umask的二進制“非”進行二進制“與”所得的結果。

S_IRUSR:文件所有者讀
S_IWUSR:文件所有者寫
S_IXUSR:文件所有者執行
S_IRGRP:用戶組讀
S_IWGRP:用戶組寫
S_IXGRP:用戶組執行
S_IROTH:其它用戶讀
S_IWOTH:其它用戶寫
S_IXOTH:其它用戶執行
返回值

成功時返回一個文件描述符
失敗時返回-1,並設置全局變量errno指明失敗原因
文件的關閉
  使用open函數打開的文件在操作結束後,應當使用close函數關閉。close函數的原型如下。

#include <unistd.h>
int close(int filedes);
1
2
  調用close函數後,終止了文件描述符與文件之間的關聯,被關閉的文件描述符重新變爲可用。關閉一個文件的同時也釋放了該進程加在該文件上的所有記錄鎖。當一個進程終止時,它所打開的所有文件都由內核自動關閉。 
  close函數的參數和返回值的含義如下: 
1. filedes爲待關閉的文件描述符 
2. 返回值:成功時返回0,失敗時返回-1

文件的讀,寫
  在Linux底層,可使用read函數讀取已打開文件中的數據,用write函數寫新的數據到已打開的文件中。 
  write函數的原型:

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
1
2
  各參數和返回值的含義如下: 
- fd:文件描述符 
- buf:待寫入文件的數據的緩衝區, 是一個常量指針 
- count:待寫的字節數,該字節數應當小於或等於緩衝區大小 
- 返回值:若成功,則返回已寫的字節數,若出錯,則返回-1,錯誤值記錄在errno中

比如:

#include <unistd.h>
#include <stdlib.h>

int main()
{
    if ((write(1, "Here is some data\n", 18)) != 18)
        write(2, "A write error has occurred on file descriptor 1\n", 46);

    return 0;
}
1
2
3
4
5
6
7
8
9
10
解釋:第六行,write函數向文件描述符1(也就是標準輸出——屏幕)寫入18字節的數據,如果出錯了,也即是返回值不爲18,則向文件描述符2(也就是標準錯誤輸出)寫入46字節的數據。程序的運行結果如下:

biantiao@lazybone1994-ThinkPad-E430:~/sh$ gcc -o ex_write ex_write.c
biantiao@lazybone1994-ThinkPad-E430:~/sh$ ./ex_write
Here is some data
biantiao@lazybone1994-ThinkPad-E430:~/sh$ 
1
2
3
4
5
  調用read函數可以從已打開的文件中讀取數據,其原型如下:

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
1
2
  read函數各參數和返回值含義如下: 
- buf:用於放置讀取到的數據的緩衝區 
- 返回值:若成功,返回已讀取的字節數(0表示已到達文件尾),若出錯,返回-1,錯誤記錄在errno中。

比如:

#include <unistd.h>
#include <stdlib.h>

int main()
{
    char buffer[128];
    int nread;

    nread = read(0, buffer, 128);
    if (nread == -1){
        write(2, "A read error has occurred.\n", 26);
    }
    else if ((write(1, buffer, nread)) != nread){
        write(2, "A write error has occurred.\n", 27);
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
解釋:第9行調用read函數,從鍵盤讀取最多128個字節(文件描述符爲1),read返回實際讀取到的字節數記錄在nread中。第10行爲判斷是否出錯。第12行調用write函數,將讀取到的buffer中的數據重新輸出到標準輸出設備。如果輸出的字節數與讀取到的字節數不一致,則在第13行向標準錯誤輸出設備輸出錯誤提示。

biantiao@lazybone1994-ThinkPad-E430:~/sh$ gcc -o ex_read ex_read.c
biantiao@lazybone1994-ThinkPad-E430:~/sh$ ./ex_read
This is a sample example for read function
This is a sample example for read function
biantiao@lazybone1994-ThinkPad-E430:~/sh$
1
2
3
4
5
文件的定位
  文件的定位。先來說一些乾貨,算是普及知識。 
  對於可隨機訪問的文件,如磁盤文件,人們往往希望能夠按需定位到文件的某個位置進行讀,寫操作。這可以通過調用lseek函數來完成。 
  實際上,每個已打開的文件都有一個與其相關聯的“當前文件位移量”。通常,讀,寫操作都從當前文件位移量處開始,並在讀,寫完成後使位移量增加所讀寫的字節數。 
  當打開一個文件時,如果指定O_APPEND選項,該位移量被設置爲文件的長度,否則該位移量被設置爲0。如果要隨機得訪問文件的內容,可調用lseek函數顯示地定位一個已打開文件的“當前文件位移量” 
  lseek函數的原型爲:

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
1
2
3
  lseek僅將當前的文件位移量記錄在內核變量內,並不引起任何I/O操作。lseek函數各參數和返回值的含義如下: 
- fd:文件描述符 
- offset:位移量。off_t類型一般爲“long int”的typedef 
- whence:指定位移量相對於何處開始,可取下面三個值

- SEEK_SET:文件開始的位置
- SEEK_CUR:文件讀寫指針當前位置
- SEEK_END:文件結束位置
1
2
3
返回值:若成功爲當前讀寫位置相對於頭文件的位移量;若出錯爲-1,錯誤值記錄在errno中
  下面是一個綜合實例:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";

int main()
{
    int fd;

    /* 調用open函數以只寫(O_WRONLY)和創建(O_CREAT)方式在當前目錄創建一個所有者具有
       讀(S_IRUSR)和寫(S_IWUSR)權限的普通文件file.hole,打開的文件描述符記錄於fd變量
       如果open的返回值小於0,說明打開文件失敗,輸出錯誤提示並退出程序
    */
    if ( (fd = open("file.hole", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) < 0 ){
        write(2, "create error.\n", 13);
        return -1;
    }

    /* 向文件寫入“abcdefghij”,如果寫入成功,則當前位移量爲10 */
    if (write(fd, buf1, 10) != 10){
        write(2, "buf1 write error.\n", 17);
        return -1;
    }

    /* 當前的位移量爲10,調用lseek函數,將當前位移量設置爲40 */
    if (lseek(fd, 40, SEEK_SET) == -1){
        write(2, "lseek error.\n", 12);
        return -1;
    }

    /* 從當前位移量爲40處開始寫入“ABCDEFGHIJ” */
    if (write(fd, buf2, 10) != 10){
        write(2, "buf2 write error.\n", 17);
        return -1;
    }

    /* 寫入成功後,當前位移量爲50 */

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  將該程序編譯並運行結果如下:

biantiao@lazybone1994-ThinkPad-E430:~/sh/file$ gcc -o ex_lseek ex_lseek.c
biantiao@lazybone1994-ThinkPad-E430:~/sh/file$ ./ex_lseek
biantiao@lazybone1994-ThinkPad-E430:~/sh/file$ od -c file.hole
0000000   a   b   c   d   e   f   g   h   i   j  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040  \0  \0  \0  \0  \0  \0  \0  \0   A   B   C   D   E   F   G   H
0000060   I   J
0000062
biantiao@lazybone1994-ThinkPad-E430:~/sh/file$
1
2
3
4
5
6
7
8
9
說明:od -c命令表示以字符形式顯示二進制文件的內容。

讀取文件的屬性
  Linux提供了stat系列函數用來讀取文件的屬性信息。這些函數的原型如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *file_name, struct stat *buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *file_name, struct stat *buf);
1
2
3
4
5
6
  這些函數各參數和返回值的含義如下: 
- file_name:文件名 
- filedes:文件描述符 
- buf:文件信息結構緩衝區,該緩衝區爲一個結構體,定義如下:

struct stat{
    dev_t       st_dev;         /* 保存本文件的設備的ID */
    ino_t       st_ino;         /* 與文件關聯的索引節點號 */
    mode_t      st_mode;        /* 文件權限和文件類型信息 */
    nlink_t     st_nlink;       /* 該文件上硬鏈接的個數 */
    uid_t       st_uid;         /* 文件所有者的UID號 */
    gid_t       st_gid;         /* 文件所有者的GID號 */
    dev_t       st_rdev;        /* 特殊文件的設備ID */
    off_t       st_size;        /* 文件大小 */
    blksize_t   st_blksize;     /* 文件系統I/O的塊大小 */
    blkcnt_t    st_blocks;      /* 塊數 */
    time_t      st_atime;       /* 最後訪問時間 */
    time_t      st_mtime;       /* 最後修改時間 */
    time_t      st_ctime;       /* 最後狀態改變時間 */
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
返回值:成功爲0,若出錯爲-1,錯誤值記錄在errno中
說明:stat和lstat函數的區別:當文件是一個符號鏈接時,lstat返回的是該符號鏈接本身的信息,而stat返回的是該鏈接指向的文件的信息。調用stat系列函數時,文件的屬性信息均保存在struct stat的結構體類型的buf當中。

  看看一看st_mode成員。st_mode成員的每一個位代表了一種權限或文件類型,可以將該成員與下表中所示的標誌位進行二進制“與”運算以測試文件權限或類型。

標誌位    常量值    含義
S_IFMT    0170000    文件類型掩碼
S_IFSOCK    0140000    套接字
S_IFLNK    0120000    符號鏈接
S_IFREG    0100000    普通文件
S_IFBLK    0060000    塊設備
S_IFDIR    0040000    目錄
S_IFCHR    0020000    字符設備
S_IFIFO    0010000    FIFO(命名管道)
S_ISUID    0004000    設置了SUID
S_ISGID    0002000    設置了SGID
S_ISVTX    0001000    設置了粘滯位
S_IRWXU    00700    文件所有者權限掩碼
S_IRUSR    00400    文件所有者可讀
S_IWUSR    00200    文件所有者可寫
S_IXUSR    00100    文件所有者可執行
S_IRWXG    00070    文件所屬組權限掩碼
S_IRGRP    00040    文件所屬組可讀
S_IWGRP    00020    文件所屬組可寫
S_IXGRP    00010    文件所屬組可執行
S_IRWXO    00007    其他用戶權限掩碼
S_IROTH    00004    其他用戶可讀
S_IWOTH    00002    其他用戶可寫
S_IXOTH    00001    其他用戶可執行
  除了直接使用二進制與的方法進行文件類型測試外,Linux還提供了以下宏用於文件類型的判定(其中m參數即爲st_mode成員) 
- S_ISREG(m):是否爲普通文件 
- S_ISDIR(m):是否爲目錄 
- S_ISCHR(m):是否爲字符設備 
- S_ISBLK(m):是否爲塊設備 
- S_ISFIFO(m):是否爲管道設備 
- S_ISLNK(m):是否爲符號連接 
- S_ISSOCK(m):是否爲套接字

實例:下面這個實例演示瞭如何使用stat函數

#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    struct stat sb;

    if (argc != 2){
        printf("Usage : %s <pathname>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (stat(argv[1], &sb) == -1){

        perror("stat");
        exit(EXIT_FAILURE);
    }

    printf("File type: ");
    switch (sb.st_mode & S_IFMT){
        case S_IFBLK : printf(" Block device.\n");          break;
        case S_IFCHR : printf(" Character device\n");       break;
        case S_IFDIR : printf(" Directory\n");              break;
        case S_IFIFO : printf(" FIFO/Pipe\n");              break;
        case S_IFLNK : printf(" Symlink\n");                    break;
        case S_IFREG : printf(" Regular file\n");           break;
        case S_IFSOCK : printf("Socket\n");                 break;
        default :      printf("Unknown file\n");            break;      
    }
    printf("I-node number : %ld\n", (long)sb.st_ino);
    printf("Mode : %lo (octal)\n", (unsigned long)sb.st_mode);
    printf("Link count : %ld\n", (long)sb.st_nlink);
    printf("Ownership : UID=%ld GID=%ld\n", (long)sb.st_uid, (long)sb.st_gid);
    printf("Preferred I/O block size : %ld bytes\n", (long)sb.st_blksize);
    printf("Blocks allocated : %lld\n",(long long)sb.st_blocks);

    printf("Last status change : %s", ctime(&sb.st_ctime));
    printf("Last file access : %s", ctime(&sb.st_atime));
    printf("Last file modification : %s", ctime(&sb.st_mtime));

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  編譯並運行

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o stat stat.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./stat stat.c
File type:  Regular file
I-node number : 4476379
Mode : 100664 (octal)
Link count : 1
Ownership : UID=1001 GID=1001
Preferred I/O block size : 4096 bytes
Blocks allocated : 8
Last status change : Thu Aug 20 09:09:17 2015
Last file access : Thu Aug 20 09:09:18 2015
Last file modification : Thu Aug 20 09:09:17 2015
biantiao@lazybone1994-ThinkPad-E430:~/桌面$
1
2
3
4
5
6
7
8
9
10
11
12
13
  怎樣在讀取一個文件屬性之前判斷該文件是否存在呢?答案是使用access函數。 
  access函數進行文件的存取許可測試,它的原型如下:

#include <unistd.h>
int access(const char *pathname, int mode);
1
2
  access函數按實際用戶的ID和實際組ID進行存取測試,其各參數和返回值的含義如下:

1. pathname : 文件名
2. mode : 測試項,其值可以是以下之一個或多個值按位或的結果

- R_OK:測試讀許可權
- W_OK:測試寫許可權
- X_OK:測試執行許可權
- F_OK:測試文件是否存在

3. 返回值:成功返回0,失敗返回-1
1
2
3
4
5
6
7
8
9
  下面是一個使用access函數的例子,功能是測試提供的文件是否存在並可讀,如果不存在或不可讀則輸出相應的提示信息,如果可讀則讀取前20個字節的數據並輸出:

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

int main(int argc, char *argv[])
{
    int i, num;
    int fd;
    char buf[20];

    if (argc != 2){
        printf("Usage : %s <pathname>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (access(argv[1], F_OK) != 0){
        printf("The file '%s' doesn't existed!\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    if (access(argv[1], R_OK) != 0){
        printf("The file '%s' can not be read!\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    if ((fd = open(argv[1], O_RDONLY)) < 0){
        printf("Failed to open file '%s' for read!\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    if ((num = read(fd, buf, 20)) < 0){
        close(fd);
        printf("Failed to read file '%s'!\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    printf("The starting %d bytes of '%s' is :\n", num, argv[1]);
    for (i = 0; i < num; i++){
        printf("%c", buf[i]);
    }
    printf("\n");
    close(fd);

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  編譯並運行

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o access access.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./access
Usage : ./access <pathname>
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./access access.c
The starting 20 bytes of 'access.c' is :
#include <sys/types.
biantiao@lazybone1994-ThinkPad-E430:~/桌面$
--------------------- 
作者:LazyBone1994 
來源:CSDN 
原文:https://blog.csdn.net/lazybone1994/article/details/48091907 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章