Linux系统应用编程 --- 文件I/O

简介: Linux系统应用编程 --- 文件I/O

1. 文件描述符

Linux系统将所有设备都当作文件来处理,而Linux用文件描述符来标识每个文件对象。

文件描述符是一个非负整数,用于唯一标识计算机操作系统中打开的文件。 它描述了数据资源,以及如何访问该资源。

下面这张图, 不同进程拥有自己独立的PCB,PCB是存放进程管理和控制信息数据的一个结构体,其中包含了管理文件的指针,每个进程都会默认打开三个文件,对应文件描述符为0,1,2.

标准输入(standard input)的文件描述符是 0(STDIN_FILENO),

标准输出(standard output)的文件描述符是 1(STDOUT_FILENO),

标准错误(standard error)的文件描述符是 2(STDERR_FILENO)。

POSIX 定义了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 来代替 0、1、2。

可用文件I/O函数很多,包括:打开文件、读写文件等。大多数linux文件IO只需要用到5个函数:open、read、write、lseek、close。

2. 基本API

2.1 open

2.1.1  open函数原型以及简单使用实例

在man手册中可以看到open函数的定义

open函数有重载版本,是否有第三个参数,取决于第二个参数是否有O_CREAT。其他不做介绍,具体看man手册。

1. #include <sys/stat.h>
2. #include <sys/types.h>
3. #include <fcntl.h>
4. #include <stdio.h>
5. #include <stdlib.h>
6. #include <unistd.h>
7. 
8. int main(int argc, char * argv[])
9. {
10.         int fd;
11. if(argc < 2)
12.         {
13.                 printf("./app filename\n");
14. exit(1);
15.         }
16. 
17. 
18. fd = open(argv[1], O_CREAT, 0777);
19.         printf("fd = %d\n", fd);
20. close(fd);
21. 
22. return 0;
23. }

执行结果如下:

针对这段code,需要注意两个问题:

1) exit和return的区别

调用任何函数的return,都是返回这个值;调用main函数的return,程序结束
在任何函数调用exit都是导致程序结束
2) open函数的第三个参数,在code给新创建文件的权限是777,但是实际创建文件的权限是775

这是因为系统默认的umask设置值是2,在open第三个参数指定权限的时候需要用系统的umask值取反一下才可以。

2.1.2 最大打开文件数

1. #include <sys/stat.h>
2. #include <sys/types.h>
3. #include <fcntl.h>
4. #include <stdio.h>
5. #include <stdlib.h>
6. 
7. int main(int argc, char * argv[])
8. {
9.  int fd; 
10.   char name[1024];
11.   int i = 0;
12. 
13.   while(1)
14.   {
15.     sprintf(name, "file%d", ++i);
16.     fd = open(name, O_CREAT, 0777);
17.     if(fd == -1)
18.     {
19.       exit(1);
20.     }
21.     printf("%d\n", i);
22.   }
23. 
24. 
25.   return 0;
26. }

执行后终端会输出fd为1-1021,因为还有3个文件描述符是默认打开的。同时可以看到终端下有file1-file1021个文件生成。

通过ulimit -a指令,可以看到最大打开文件数为1024.

可以通过ulimit -n修改最大打开文件数

 

2.2 read/write

2.2.1 read/write函数原型以及简单使用

1. #include <sys/stat.h>
2. #include <sys/types.h>
3. #include <fcntl.h>
4. #include <stdio.h>
5. #include <stdlib.h>
6. #include <unistd.h>
7. #include <string.h>
8. 
9. int main(int argc, char * argv[])
10. {
11.         int fd;
12.         char buf[1024] = "hello world";
13. if(argc < 2)
14.         {
15.                 printf("./app filename\n");
16. exit(1);
17.         }
18. 
19. 
20. fd = open(argv[1], O_CREAT, 0777);
21.         printf("fd = %d\n", fd);
22. 
23. write(fd, buf, strlen(buf));        
24. 
25. close(fd);
26. 
27. return 0;
28. }

2.2.2 实现文件拷贝code

1. #include <sys/stat.h>
2. #include <sys/types.h>
3. #include <fcntl.h>
4. #include <unistd.h>
5. #include <stdlib.h>
6. #include <string.h>
7. 
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.   {
18.     printf("./mycp src dest\n");
19.     exit(1);
20.   }
21. 
22.   fd_src = open(argv[1], O_RDONLY);
23.   fd_dest = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);
24. 
25.   /*
26.   *成功返回读到字节数
27.   *读到文件末尾返回0
28.   *读失败返回-1
29.   */
30.   while(len == read(fd_src, buf, sizeof(buf)) > 0)
31.   {
32.     write(fd_dest, buf, len);
33.   }
34.   close(fd_src);
35.   close(fd_dest);
36. 
37.   return 0;
38. }

2.3 lseek

每个打开的文件都记录着当前读写位置,打开文件时读写位置是0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。但是有一个例外,如果以O_APPEND方

式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。lseek和标准I/O库的fseek函数类似,可以移动当前读写位置(或者叫偏移量)。

lseek函数原型:

off_t lseek(int fd, off_t offset, int whence);

下面的代码展示lseek的两个作用(只是lseek附带的功能)

//用lseek拓展一个文件

//用lseek获取文件大小

1. #include <sys/stat.h>
2. #include <sys/types.h>
3. #include <fcntl.h>
4. #include <stdio.h>
5. #include <stdlib.h>
6. #include <errno.h>
7. #include <unistd.h>
8. #include <stdio.h>
9. 
10. int main(void)
11. {
12.   int fd = open("abc", O_RDWR);
13.   if(fd < 0)
14.   {
15.     perror("open abc");
16.     exit(-1);
17.   }
18. 
19.   lseek(fd, 0x1000, SEEK_SET);
20. 
21.   //拓展一个文件,一定要有一次写操作
22.   write(fd, "a",  1);
23.   close(fd);
24. 
25. //用lseek获取文件大小
26.   fd = open("hello", O_RDWR);
27.   if(fd < 0)
28.   {
29.     perror("open hello");
30.     exit(-1);
31.   }
32.   printf("hello size = %d\n", lseek(fd, 0, SEEK_END));
33.   close(fd);
34.   return 0;
35. }

2.4 fcntl

fcntl的作用:

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

 

fcntl函数原型

1. int fcntl(int fd, int cmd);
2. int fcntl(int fd, int cmd, long arg);
3. int fcntl(int fd, int cmd, struct flock *lock);

这个函数和open一样,也是用可变参数实现的,可变参数的类型和个数取决于前面的cmd参数。下面的例子使用F_GETFL和F_SETFL这两种fcntl命令改变STDIN_FILENO的属性,加上O_NONBLOCK选项。

1. #include <unistd.h>
2. #include <fcntl.h>
3. #include <errno.h>1.8节 ioctl 11
4. #include <string.h>
5. #include <stdlib.h>
6. #define MSG_TRY "try again\n"
7. int main(void)
8. {
9.  char buf[10];
10.   int n;
11.   int flags;
12.   flags = fcntl(STDIN_FILENO, F_GETFL);
13.   flags |= O_NONBLOCK;
14.   if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) {
15.     perror("fcntl");
16.     exit(1);
17.   }
18. 
19.   tryagain:
20.   n = read(STDIN_FILENO, buf, 10);
21.   if (n < 0) 
22.   {
23.     if (errno == EAGAIN) 
24.     {
25.       sleep(1);
26.       write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
27.       goto tryagain;
28.     }
29.     perror("read stdin");
30.     exit(1);
31.   }
32.   write(STDOUT_FILENO, buf, n);
33.   return 0;
34. }

2.5 ioctl

设置和获取设备文件的物理特性时,用ioctl

这个API主要是在设备驱动的时候用,用的时候在具体看吧。。

相关文章
|
18天前
|
存储 缓存 监控
Linux缓存管理:如何安全地清理系统缓存
在Linux系统中,内存管理至关重要。本文详细介绍了如何安全地清理系统缓存,特别是通过使用`/proc/sys/vm/drop_caches`接口。内容包括清理缓存的原因、步骤、注意事项和最佳实践,帮助你在必要时优化系统性能。
151 78
|
8天前
|
Ubuntu Linux Go
golang编译成Linux可运行文件
本文介绍了如何在 Linux 上编译和运行 Golang 程序,涵盖了本地编译和交叉编译的步骤。通过这些步骤,您可以轻松地将 Golang 程序编译成适合 Linux 平台的可执行文件,并在目标服务器上运行。掌握这些技巧,可以提高开发和部署 Golang 应用的效率。
66 14
|
7天前
|
存储 NoSQL Linux
linux积累-core文件是干啥的
核心文件是Linux系统在程序崩溃时生成的重要调试文件,通过分析核心文件,开发者可以找到程序崩溃的原因并进行调试和修复。本文详细介绍了核心文件的生成、配置、查看和分析方法
34 6
|
9天前
|
存储 NoSQL Linux
linux之core文件如何查看和调试
通过设置和生成 core 文件,可以在程序崩溃时获取详细的调试信息。结合 GDB 等调试工具,可以深入分析 core 文件,找到程序崩溃的具体原因,并进行相应的修复。掌握这些调试技巧,对于提高程序的稳定性和可靠性具有重要意义。
48 6
|
18天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
83 13
|
19天前
|
Ubuntu Linux C++
Win10系统上直接使用linux子系统教程(仅需五步!超简单,快速上手)
本文介绍了如何在Windows 10上安装并使用Linux子系统。首先,通过应用商店安装Windows Terminal和Linux系统(如Ubuntu)。接着,在控制面板中启用“适用于Linux的Windows子系统”并重启电脑。最后,在Windows Terminal中选择安装的Linux系统即可开始使用。文中还提供了注意事项和进一步配置的链接。
40 0
|
2月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
138 8
|
2月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
555 6
|
2月前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
104 3