linux学习:文件类型、文件操作、系统IO、内存映射

目录

文件类别

文件操作

系统 IO

头文件

打开文件

关闭文件

文件描述符

读写

例子  拷贝文件

偏移量

其他接口

mmap()映射


文件类别

  • 普通文件(regular):存在于外部存储器中,用于存储普通数据。
  • 目录文件(directory):用于存放目录项,是文件系统管理的重要文件类型。
  • 管道文件(pipe):一种用于进程间通信的特殊文件,也称为命名管道 FIFO。
  • 套接字文件(socket):一种用于网络间通信的特殊文件。
  • 链接文件(link):用于间接访问另外一个目标文件,相当于 Windows 快捷方式。
  • 字符设备文件(character):字符设备在应用层的访问接口。
  • 块设备文件(block):块设备在应用层的访问接口。

文件信息的最左边一栏是各种文件类型的缩写

  • b(block)块设备文件
  • c(character)字符设备文件
  • d(directory)目录文件
  • l(link)链接文件(软链接)
  • p(pipe)管道文件(命名管道)
  • -(regular)普通文件
  • s(socket)套接字文件
  1. 块设备文件和字符设备文件,是 Linux 系统中块设备和字符设备的访问节点, 在内核中注册了某一个设备文件之后,还必须在/dev/下为这个设备创建一个对应的节点文 件(网络接口设备除外),作为访问这个设备的入口
  2. 目录文件用来存放目录项,是实现文 件系统管理的最重要的手段。
  3. 链接文件指的是软链接,是一种用来指向别的文件的特殊文件, 其作用类似于 Windows 中的快捷方式,但他有更加有用的功能,比如库文件的版本管理。
  4. 普通文件指的是外部存储器中的文件,比如二进制文件和文本文件。
  5. 套接字文件指的是本机内进程间通信用的 Unix 域套接字,或称本地域套接字。

文件操作

对一个文件的操作有两种不同的方式,既可以使用由操作系统直接提供的编程接口 (API),即系统调用,也可以使用由标准 C 库提供的标准 IO 函数

在几百个 Linux 系统调用中,有一组函数是专门针对文件操作的,比如打开文件、关 闭文件、读写文件等,这些系统调用接口就被称为“系统 IO”,相应地,在几千个标准 C 库函数中,有一组函数也是专门针对文件操作的,被称为“标准 IO”,他们是工作在不同 层次,但都是为应用程序服务的函数接口

系统 IO

头文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

打开文件

注意

  • flags 的各种取值可以用位或的方式叠加起来,比如创建文件的时候需要满足这样 的选项:读写方式打开,不存在要新建,如果存在了则清空他。那么此时指定的 flags 的取 值应该是:O_RDWR | O_CREAT | O_TRUNC。
  • mode 是八进制权限,比如 0644,或者 0755 等。
  • 它可以用来打开普通文件、块设备文件、字符设备文件、链接文件和管道文件,但 只能用来创建普通文件,每一种特殊文件的创建都有其特定的其他函数。
  • 其返回值就是一个代表这个文件的描述符,是一个非负整数。这个整数将作为以后任何系统 IO 函数对其操作的句柄,或称入口

关闭文件

文件描述符

是一个数组的下标值,在内核中打开的文件是用 file 结构体来表示的,每一个结构体都会有一个 指针来指向他们,这些指针被统一存放在一个叫做 fd_array 的数组当中,而这个数组被存 放在一个叫做 files_struct 的结构体中,该结构体是进程控制块 task_struct 的重要组成 部分

 

  • task_struct 被称为进程控制块,是程序运行时在 内核中的实现形式
  • 结构体 file{}是内核管理文件操作的最重要的数据之一,里面存放了对该文件的访问模 式、文件位置偏移量等重要信息。在操作文件之前,open( )参数中指定的模式将被记录在 该结构体中,在操作文件之时,文件相关的控制数据也一并在此统一管理

读写

  • 实际的读写字节数要通过返回值来判断
  • 当实际的读写字节数小于 count 时,有以下几种情形
    • 读操作时,文件剩余可读字节数不足 count
    • 读写操作期间,进程收到异步信号
  • 读写操作同时对 f_pos 起作用。也就是说,不管是读还是写,文件的位置偏移量(即 内核中的 f_pos)都会加上实际读写的字节数,不断地往后偏移

例子  拷贝文件

1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7
8 #define SIZE 1024
9
10 int main(int argc, char **argv)
11 {
12     int fd_from, fd_to;
13
14     if(argc != 3)
15     {
16         printf("Uage: %s <src> <dst>", argv[0]);
17         exit(1);
18     }
19     // 以只读方式打开源文件,以只写方式打开目标文件
20     fd_from = open(argv[1], O_RDONLY);
21     fd_to = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC, 0644);
22
23     char buf[SIZE];
24     char *p;
25     int nread, nwrite;
26
27     while(1)
28     {
29         nread = read(fd_from, buf, SIZE);
30
31         if(nread == 0)
32             break;
33
34         write(fd_to, buf, nread);
35     }
36
37     close(fd_from);
38     close(fd_to);
39
40     return 0;
41 }

偏移量

例子  可以实现一个进程拷贝前半段,另一个进程拷贝后半段

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdbool.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <strings.h>
7 #include <errno.h>
8
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <fcntl.h>
12
13 int main(int argc, char **argv)
14 {
15     if(argc != 3) // 输入两个参数,用法类似于 Shell 命令:cp file1 file2
16     {
17         printf("Usage: %s <src> <dst>\n", argv[0]);
18         exit(1);
19     }
20
21     // 创建一个子进程(详细讲解参见第五章)
22     pid_t a = fork();
23
24     // 父子进程都打开源文件和目标文件
25     int fd1 = open(argv[1], O_RDONLY);
26     int fd2 = open(argv[2], O_CREAT|O_RDWR|O_TRUNC, 0644);
27     if(fd1 == -1 || fd2 == -1)
28     {
29         perror("open()");
30         exit(1);
31     }
32
33     int size = lseek(fd1, 0, SEEK_END); // 获得文件大小
34     if(a == 0) // 在子进程中,将位置偏移量调整到中间位置(形成空洞)
35     {
36         lseek(fd1, size/2, SEEK_SET);
37         lseek(fd2, size/2, SEEK_SET);
38     }
39     else if(a > 0) // 在父进程中,将文件位置偏移量调整到文件开头处
40     {
41         lseek(fd1, 0, SEEK_SET);
42     }
43
44     char buf[100];
45     int nread;
46
47     while(1)
48     {
49         bzero(buf, 100);
50         nread = read(fd1,bug,100);
51         if(nread==0)
52             break;
53
54         if(a > 0)
55         {
56             // 在父进程中,查看当前偏移量是否已经到达中间位置
57             int n;
58             n = lseek(fd1, 0, SEEK_CUR) - size/2;
59             if(n >= 0) // 到达甚至已经超过中间位置
60             {
61                 write(fd2, buf, n); // 写入未超过中间位置的字节
62                 exit(0); // 然后退出
63             }
64         }
65
66         write(fd2, buf, nread);
67     }
68
69     close(fd1);
70     close(fd2);
71
72     return 0;
73 }

其他接口

调整文件为异步工 作模式

int on = 1;
ioctl(fd, FIOASYNC, &on);//后面的变参也由前面的 request 命令字决定
等同于
fcntl(fd, F_SETFL, O_ASYNC);

fcntl( )而言,其第二个参数命令字 cmd 有很多

mmap()映射

该函数在进程的虚拟内存空间 中映射出一块内存区域,用以对应指定的一个文件,该内存区域上的数据跟对应的文件的数 据是一一对应的,并在一开始的时候用文件的内容来初始化这片内存

例子  使用 mmap( )实现类似 Shell 命令 cat 的功能:将一个普通文件的内容显示到屏幕上

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <errno.h>
5
6 #include <sys/stat.h>
7 #include <sys/mman.h>
8 #include <sys/types.h>
9 #include <fcntl.h>
10
11 int main(int argc, char **argv)
12 {
13     if(argc != 2)
14     {
15         printf("Usage: %s <filename>\n", argv[0]);
16         exit(1);
17     }
18
19     // 以只读方式打开一个普通文件
20     int fd = open(argv[1], O_RDONLY);
21
22     // 申请一块大小为 1024 字节的映射内存,并将之与文件 fd 相关联
23     char *p = mmap(NULL, 1024, PROT_READ, 24 MAP_PRIVATE, fd, 0);
25
26     // 将该映射内存的内容打印出来(即其相关联文件 fd 的内容)
27     printf("%s\n", p);
28
29     return 0;
30 }
  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值