Linux系统应用编程---文件IO

简介: Linux系统应用编程---文件IO

文件描述符

files_struct *file结构体记录在task_struct结构体中,*file指向文件描述符。

一个进程默认打开三个文件描述符  

1.     STDIN_FILENO 0
2. 
3.     STDOUT_FILENO 1
4. 
5.     STDERR_FILENO 2

新打开文件返回文件描述符表中未使用的最小文件描述符

一、文件打开与关闭

open函数打开文件,open原型如下:

1. int open(const char * pathname, int flags);
2. 
3. int open(const char * pathname, int flags, mode_t mode);
  • 只有用到O_CREAT参数的时候,才会使用mode参数
  • 返回值:打开成功返回文件描述符,错误返回-1.
  • flags必选项:以下三个常数中必须指定一个,且仅允许指定一个。

* O_RDONLY 只读打开

* O_WRONLY 只写打开

* O_RDWR 可读可写打开

以下可选项可以同时指定0个或多个,和必选项按位或起来作为flags参数。可选项有很多,这里只介绍一部分,其它选项可参考open(2)的Man Page:

* O_APPEND 表示追加。如果文件已有内容,这次打开文件所写的数据附加到文件的末尾

而不覆盖原来的内容。

* O_CREAT 若此文件不存在则创建它。使用此选项时需要提供第三个参数mode,表示该

文件的访问权限。

* O_EXCL 如果同时指定了O_CREAT,并且文件已存在,则出错返回。

* O_TRUNC 如果文件已存在,并且以只写或可读可写方式打开,则将其长度截断(Truncate)为0字节。

* O_NONBLOCK 对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O(Nonblock I/O)

 

关闭文件用close函数,close函数原型如下:

int close (int fd);

close返回值,关闭成功返回0,错误返回-1.

二、文件读写

read函数从打开的设备或文件中读取数据

ssize_t read(int fd, void *buf, size_t count)
  • 返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0.
  • fd是要读取的文件的文件描述符,参数count是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移。

 

write函数向打开的设备或文件中写数据

ssize_t write(int fd, const void *buf, size_t count);

返回值:成功返回写入的字节数,出错返回-1并设置errno

 

下面使用文件IO来实现一个cp命令类似的功能

1. #include <stdio.h>
2. #include <sys/types.h>
3. #include <sys/stat.h>
4. #include <fcntl.h>
5. #include <stdlib.h>
6. #include <unistd.h>
7. #include <string.h>
8. 
9. #define SIZE 8192
10. 
11. int main(int argc, char * argv[])
12. {
13.        char buf[SIZE];
14.        int fd_src, fd_dest, len;     
15. 
16. if(argc < 3){
17.               printf("./mycp src dest.\n");
18. exit(1);
19.        }
20. 
21. 
22. fd_src = open(argv[1], O_RDONLY);
23. fd_dest = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0777);
24. /*
25. *成功返回读到的字节数
26. *读到文件末尾返回0
27. *读失败返回-1
28. */
29.        while((len = read(fd_src, buf, sizeof(buf))) >0 )
30. write(fd_dest, buf, len);      
31. 
32. close(fd_src);
33. close(fd_dest);
34. 
35. return 0;
36. }

执行结果如下:

可以看到拷贝的2.txt与1..txt文件一样

三、文件定位

lseek可移动当前文件读写位置,函数原型:

off_t lseek (int fd, off_t offset, int whence)
  • lseek()函数的作用是,重置文件描述符fd关联打开的文件偏移量,把这个偏移量重置为参数offset,在重置时要根据指令whence来做,whence可包含的指令有三个

SEEK_SET       表从文件的起始位置偏移offset大小的字节

SEEK_CUR     表从当前位置加上offset大小的字节

SEEK_END     表从未见末尾加上offset大小的字节

  • 返回值:

lseek返回值:当lseek成功完成时,lseek()返回的偏移量是距离文件开头的偏移量,大小是以自己为单位衡量。如果错误返回值为-1,并设置errno的值

 

例1:用lseek拓展一个文件,注意一定要有一次写操作

1. #include <sys/types.h>
2. #include <sys/stat.h>
3. #include <fcntl.h>
4. #include <unistd.h>
5. #include <stdio.h>
6. #include <errno.h>
7. #include <stdlib.h>
8. 
9. int main(void)
10. {
11.        int fd = open("filetest", O_RDWR);
12. if(fd < 0){
13.               perror("open filetest");
14. exit(-1);
15.        }
16. 
17. //拓展一个文件,一定要有一次写操作
18.        lseek(fd, 0x1000, SEEK_SET);
19. //write(fd, "a", 1);
20. close(fd);
21. 
22. return 0;
23. }

注释后://write(fd, "a", 1);

去掉注释

 

例2:用lseek输出一个文件的大小

1. #include <sys/types.h>
2. #include <sys/stat.h>
3. #include <fcntl.h>
4. #include <unistd.h>
5. #include <stdio.h>
6. #include <errno.h>
7. #include <stdlib.h>
8. 
9. int main(void)
10. {
11.        int file_size;
12.        int fd = open("abc", O_RDWR);
13. if(fd < 0){
14.               perror("open abc");
15. exit(-1);
16.        }
17. 
18. fd = open("hello", O_RDWR);
19. if(fd < 0){
20.               perror("open hello");
21. exit(-1);
22.        }
23. 
24. file_size = lseek(fd, 0, SEEK_END);
25.        printf("hello size = %d.\n", file_size);
26. close (fd);
27. 
28. return 0;
29. }

执行结果

 

四、设置/获取文件控制属性

fcntl函数原型如下:

1. #include <unistd.h>
2. 
3. #include <fcntl.h>
4. 
5. int fcntl(int fd, int cmd);
6. 
7. int fcntl(int fd, int cmd, long arg);
8. 
9. int fcntl(int fd, int cmd, struct flock *lock);

先看一个例程,这里重新打开STDIN_FILENO,并添加非阻塞的属性,当用户有输入时,直接在标准输出上显示用户的输入,实现类似cat的功能(只不过这里是非阻塞)

1. #include <unistd.h>
2. #include <sys/types.h>
3. #include <sys/stat.h>
4. #include <string.h>
5. #include <stdlib.h>
6. #include <errno.h>
7. #include <string.h>
8. #include <fcntl.h>
9. #include <error.h>
10. #include <stdio.h>
11. 
12. #define MSG_TRY "try again\n"
13. 
14. int main(void)
15. {
16.        char buf[10];
17.        int fd, n;
18. fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);
19. if(fd < 0){
20.               perror("open /dev/tty");
21. exit(1);
22.        }    
23. 
24. tryagain:
25.        n = read(fd, buf, 10);
26. if(n < 0){
27. if(errno == EAGAIN){
28.                      sleep(1);
29. write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
30.                      goto tryagain;
31.               }
32.               perror("read /dev/tty");
33. exit(1);
34.        }
35. write(STDOUT_FILENO, buf, n);
36. close(fd);
37. 
38. return 0;
39. }

 

为什么我们不直接对STDIN_FILENO做非阻塞read,而要重新open一遍/dev/tty呢?因为STDIN_FILENO在程序启动时已经被自动打开了,而我们需要在调用open时指定O_NONBLOCK标志。

这里用fcntl函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File Status Flag),而不必重新open文件。

所以上面的代码可修改如下,用fcntl函数实现

 

1. #include <unistd.h>
2. #include <fcntl.h>
3. #include <errno.h>
4. #include <string.h>
5. #include <stdlib.h>
6. 
7. #define MSG_TRY "try again\n"
8. 
9. int main(void)
10. {
11.     char buf[10];
12.     int n;
13.     int flags;
14. 
15.     flags = fcntl(STDIN_FILENO, F_GETFL);
16.     flags |= O_NONBLOCK;
17. 
18. if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) {
19.         perror("fcntl");
20. exit(1);
21.     }
22. 
23. tryagain:
24.     n = read(STDIN_FILENO, buf, 10);
25. if (n < 0) {    
26. if (errno == EAGAIN) {
27.         sleep(1);
28. write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
29.         goto tryagain;
30.     }
31. 
32.     perror("read stdin");
33. exit(1);
34. }
35. 
36. write(STDOUT_FILENO, buf, n);
37. 
38. return 0;
39. }
目录
相关文章
|
1天前
|
Linux Go 数据安全/隐私保护
Linux 中的文件属性解析
在 Linux 系统中,每个文件和目录有一组属性控制其操作和访问权限。了解这些属性对有效管理文件至关重要。文件属性包括:文件类型(如 `-` 表示普通文件,`d` 表示目录),权限(如 `rwx` 表示所有者权限,`r-x` 表示组和其他用户权限),所有者,组,硬链接数,文件大小和最后修改时间。通过 `chown` 和 `chmod` 命令可更改文件所有者、所属组及权限。此外,还有特殊权限(如 SUID、SGID)和 ACL(访问控制列表)提供更精细的访问控制。
|
2天前
|
人工智能 Linux
Linux查找大文件的方法
Linux查找大文件的方法
|
2天前
|
Ubuntu Linux
Linux(Ubuntu)系统临时IP以及静态IP配置(关闭、启动网卡等操作)
请注意,以上步骤是在临时基础上进行配置的。如果要永久保存静态IP地址,通常还需要修改 `/etc/network/interfaces`文件,以便在系统重启后保持配置。同时,确保备份相关配置文件以防止出现问题。
14 1
|
3天前
|
Linux 数据安全/隐私保护
Linux系统忘记密码的三种解决办法
这篇博客介绍了三种在Linux忘记密码时重置登录密码的方法:1) 使用恢复模式,通过控制台界面以管理员权限更改密码;2) 利用Linux Live CD/USB启动,挂载硬盘分区并使用终端更改密码;3) 进入单用户模式,自动以管理员身份登录后重置密码。每个方法都提供了详细步骤,提醒用户在操作前备份重要数据。
|
3天前
|
JSON Unix Linux
Linux系统之jq工具的基本使用
Linux系统之jq工具的基本使用
32 2
|
3天前
|
数据采集 监控 安全
linux系统被×××后处理经历
linux系统被×××后处理经历
|
1月前
|
存储 Java 数据处理
|
1月前
|
Java API
java中IO与NIO有什么不同
java中IO与NIO有什么不同
|
3月前
|
存储 Java 数据安全/隐私保护
从零开始学习 Java:简单易懂的入门指南之IO字符流(三十一)
从零开始学习 Java:简单易懂的入门指南之IO字符流(三十一)
|
3月前
|
存储 移动开发 Java
从零开始学习 Java:简单易懂的入门指南之IO字节流(三十)
从零开始学习 Java:简单易懂的入门指南之IO字节流(三十)