本文共 10959 字,大约阅读时间需要 36 分钟。
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 版权声明:本文为博主原创文章,转载请附上博文链接!