??文件描述符是一個非負整數。對內核而言,所有打開的文件都由文件描述符引用。
??當打開一個現存文件或者創建一個新的文件時,內核向進程返回一個文件描述符。當讀,寫一個文件時,用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
mode:存取許可權位,一個32位無符號整數,僅當創建新文件時才使用,由下列一個或多個常數進行或運算構成。應注意,最終文件權限受系統變量umask限制,是所設權限和umask的二進制“非”進行二進制“與”所得的結果。
UNIX/LINUX,返回值
??使用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:文件結束位置
??下面是一個綜合實例:
#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; /* 最后狀態改變時間 */
};
shell運行、說明: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;
}
??編譯并運行
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:~/桌面$
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态