UNIX/LINUX,Linux操作系統-文件(2)

 2023-12-09 阅读 49 评论 0

摘要:Linux操作系統—文件(2)(2015-8-17) 分類:Linux操作系統 二:底層文件訪問 文件描述符 ??文件描述符是一個非負整數。對內核而言,所有打開的文件都由文件描述符引用。 ??當打開一個現存文件或者創建一個新的文件時,內核向進程返回一個文件描述符。

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

疑惑:這是什么?函數重載么?可函數重載不是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. mode:存取許可權位,一個32位無符號整數,僅當創建新文件時才使用,由下列一個或多個常數進行或運算構成。應注意,最終文件權限受系統變量umask限制,是所設權限和umask的二進制“非”進行二進制“與”所得的結果。

    • S_IRUSR:文件所有者讀
    • S_IWUSR:文件所有者寫
    • S_IXUSR:文件所有者執行
    • S_IRGRP:用戶組讀
    • S_IWGRP:用戶組寫
    • S_IXGRP:用戶組執行
    • S_IROTH:其它用戶讀
    • S_IWOTH:其它用戶寫
    • S_IXOTH:其它用戶執行
  2. UNIX/LINUX,返回值

    • 成功時返回一個文件描述符
    • 失敗時返回-1,并設置全局變量errno指明失敗原因

文件的關閉

??使用open函數打開的文件在操作結束后,應當使用close函數關閉。close函數的原型如下。

#include <unistd.h>
int close(int filedes);

??調用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);

??各參數和返回值的含義如下:
- 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;
}

linux與windows的區別、解釋:第六行,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$ 

??調用read函數可以從已打開的文件中讀取數據,其原型如下:

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

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

解釋:第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$

文件的定位

??文件的定位。先來說一些干貨,算是普及知識。
??對于可隨機訪問的文件,如磁盤文件,人們往往希望能夠按需定位到文件的某個位置進行讀,寫操作。這可以通過調用lseek函數來完成。
??實際上,每個已打開的文件都有一個與其相關聯的“當前文件位移量”。通常,讀,寫操作都從當前文件位移量處開始,并在讀,寫完成后使位移量增加所讀寫的字節數。
??當打開一個文件時,如果指定O_APPEND選項,該位移量被設置為文件的長度,否則該位移量被設置為0。如果要隨機得訪問文件的內容,可調用lseek函數顯示地定位一個已打開文件的“當前文件位移量”
??lseek函數的原型為:

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

dockerfile詳解、??lseek僅將當前的文件位移量記錄在內核變量內,并不引起任何I/O操作。lseek函數各參數和返回值的含義如下:
- fd:文件描述符
- offset:位移量。off_t類型一般為“long int”的typedef
- whence:指定位移量相對于何處開始,可取下面三個值

- SEEK_SET:文件開始的位置
- SEEK_CUR:文件讀寫指針當前位置
- SEEK_END:文件結束位置
  • 返回值:若成功為當前讀寫位置相對于頭文件的位移量;若出錯為-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;
}

??將該程序編譯并運行結果如下:

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$

說明: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);

??這些函數各參數和返回值的含義如下:
- 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;       /* 最后狀態改變時間 */
};
  • 返回值:成功為0,若出錯為-1,錯誤值記錄在errno中

shell運行、說明:stat和lstat函數的區別:當文件是一個符號鏈接時,lstat返回的是該符號鏈接本身的信息,而stat返回的是該鏈接指向的文件的信息。調用stat系列函數時,文件的屬性信息均保存在struct stat的結構體類型的buf當中。

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

標志位常量值含義
S_IFMT0170000文件類型掩碼
S_IFSOCK0140000套接字
S_IFLNK0120000符號鏈接
S_IFREG0100000普通文件
S_IFBLK0060000塊設備
S_IFDIR0040000目錄
S_IFCHR0020000字符設備
S_IFIFO0010000FIFO(命名管道)
S_ISUID0004000設置了SUID
S_ISGID0002000設置了SGID
S_ISVTX0001000設置了粘滯位
S_IRWXU00700文件所有者權限掩碼
S_IRUSR00400文件所有者可讀
S_IWUSR00200文件所有者可寫
S_IXUSR00100文件所有者可執行
S_IRWXG00070文件所屬組權限掩碼
S_IRGRP00040文件所屬組可讀
S_IWGRP00020文件所屬組可寫
S_IXGRP00010文件所屬組可執行
S_IRWXO00007其他用戶權限掩碼
S_IROTH00004其他用戶可讀
S_IWOTH00002其他用戶可寫
S_IXOTH00001其他用戶可執行

??除了直接使用二進制與的方法進行文件類型測試外,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;
}

??編譯并運行

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:~/桌面$

??怎樣在讀取一個文件屬性之前判斷該文件是否存在呢?答案是使用access函數。
??access函數進行文件的存取許可測試,它的原型如下:

#include <unistd.h>
int access(const char *pathname, int mode);

LINUX是一種,??access函數按實際用戶的ID和實際組ID進行存取測試,其各參數和返回值的含義如下:

1. pathname : 文件名
2. mode : 測試項,其值可以是以下之一個或多個值按位或的結果- R_OK:測試讀許可權
- W_OK:測試寫許可權
- X_OK:測試執行許可權
- F_OK:測試文件是否存在

3. 返回值:成功返回0,失敗返回-1

??下面是一個使用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;
}

??編譯并運行

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:~/桌面$

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/195876.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息