文件IO系统调用内部机制

一、文件IO系统调用

(1)一般的,进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。CPU硬件决定了这些(这就是为什么它被称作“保护模式”)。

(2)为了和用户空间上运行的进程进行交互,内核提供了一组接口。透过该接口,应用程序可以访问硬件设备和其他操作系统资源。这组接口在应用程序和内核之间扮演了使者的角色,应用程序发送各种请求,而内核负责满足这些请求(或者让应用程序暂时搁置)。实际上提供这组接口主要是为了保证系统稳定可靠,避免应用程序肆意妄行,惹出大麻烦。

(3)应用程序与Linux内核之间存在着系统调用接口,由glibc实现open、read和write函数。

(4)具体流程

根据__NR_open变量判断open等函数

①用户层调用open等函数;

②glibc内的open等函数会设置触发异常的原因,并调用汇编指令swi或svc来触发异常;

③cpu发现该异常,就会调用Linux内核中的异常处理函数,分辨异常原因并处理异常(根据不同原因调用不同的函数)。

(4)应用层将触发异常的原因传递给内核

①old ABI:将open等函数导致的原因信息嵌入swi指令内,内核解析该指令。(ABI指应用程序二进制接口)

②EABI:将触达原因先存放于R7或R8寄存器中,内核从R7或R8中读取信息从而解析。

最后调用sys_call_table数组对应的函数

注:配置内核和应用程序要使用相同的ABI接口。

(5)每个进程有自己的文件句柄空间,进程之间互不影响。

(6)文件句柄与具体文件挂钩

先获得未使用的文件句柄,然后打开文件得到一个file结构体,最后在当前进程记录下这个文件:记录“文件句柄、file结构体”的对应关系。后面APP使用文件句柄操作文件时,在内核里面就是根据对应的file结构体去操作文件

对于每一个进程,都有一个task_struct结构体,fd数组的每一项都指向一个file结构体,每个file结构体对应一个文件,如下图

二、每个进程有自己的文件句柄空间,进程之间互不影响。

每次调用open函数来打开同一个文件时,总会获得一个新的文件句柄fd。

每一套fd之间是互不影响的,内部的f_pos是相互独立的(f_pos就是操作的文件位置,每次操作过后f_pos就会自动+1)。因此,通过read函数对不同fd进行读取一个字节数据,最终获得的数据是相同的。

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

int main(int argc, char **argv)
{
    char buf[10];
    char buf2[10];
    
    if (argc != 2)
        return -1;

    int fd = open(argv[1], O_RDONLY);
    int fd2 = open(argv[1], O_RDONLY);

    printf("fd = %d\n", fd);
    printf("fd2 = %d\n", fd2);

    read(fd, buf, 1);
    read(fd2, buf2, 1);

    printf("data get from fd : %c\n", buf[0]);
    printf("data get from fd2: %c\n", buf2[0]);
    
    return 0;
}

如上,fd和fd2打开相同的文件, 使用read函数读取一个字符,读取法人结果是一致的,运行结果如下:

$ cat 1.txt
adf
$ ./dup 1.txt
fd = 3
fd2 = 4
data get from fd : a
data get from fd2: a

三、常见文件句柄

  • 句柄0(标准输入,stdin)

    • 这个句柄用于接收输入数据。默认情况下,它从键盘读取输入数据,但可以被重定向到从文件或其他输入流中读取数据。
    • 在C语言中,它通常与stdin关联,在Python中与sys.stdin关联。
  • 句柄1(标准输出,stdout)

    • 这个句柄用于输出数据。默认情况下,它将数据输出到屏幕上,但可以被重定向到文件或其他输出流。
    • 在C语言中,它通常与stdout关联,在Python中与sys.stdout关联。
  • 句柄2(标准错误,stderr)

    • 这个句柄用于输出错误信息。默认情况下,它也将数据输出到屏幕上,通常用于显示错误消息。它也可以被重定向到文件或其他流,独立于标准输出。
    • 在C语言中,它通常与stderr关联,在Python中与sys.stderr关联。

 三、dup函数

3.1 函数原型

#include <unistd.h>

int dup(int oldfd);

int dup2(int oldfd, int newfd);

3.2 作用

复制文件描述符,就是两个文件句柄指向同一个文件描述符,这两个文件句柄共享文件偏移地址、状态

lodfd:被复制的文件句柄

 3.3 使用dup函数复制文件句柄

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
 
int main(int argc, char* argv[])
{
	if(argc != 2)
		return -1;

	int fd = open(argv[1], O_RDONLY);
	int fd2 = open(argv[1], O_RDONLY);
	int fd3 = dup(fd);
	if(fd < 0 | fd2 < 0)
        return -1;
	
	char c1, c2, c3;
	read(fd, &c1, 1);
	read(fd2, &c2, 1);
	read(fd, &c3, 1);

	printf("fd: %c\n", c1);
	printf("fd2: %c\n", c2);
	printf("fd3: %c\n", c3);
    
	close(fd);
	close(fd2);
	return 0;
}

dup函数不会新建一个file结构体,而是指向传入的fd对应的结构体,fd和fd3指向同一个文件,因此fd用read函数读取一个字符后,pos+1,fd3会读取下一个字符,dup函数返回的文件句柄是“未使用的最小文件句柄”

 运行结果如下:

$ cat 1.txt 
abc
$ gcc -o dup dup.c 
$ ./dup 1.txt
fd: a
fd2: a
fd3: b

四、dup2函数

4.1 函数原型

#include <unistd.h>

int dup(int oldfd);

int dup2(int oldfd, int newfd);

lodfd:被复制的文件句柄

newfd:复制得到的文件句柄

可以指定复制得到的文件句柄为newfd

4.2 作用

以dup2(fd,1)为例

①关闭文件句柄1的文件;

②文件句柄1指向句柄为fd的file结构体

由于句柄1是标准输出,后续输出都会定向到句柄为fd的file结构体,如下图所示

4.3 代码实现

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


int main(int argc, char **argv)
{
    int fd;
    int c = 2333;

    fd = open(argv[1], O_RDWR);
    printf("%d\n", fd);

    dup2(fd,1);

    printf("%d\n", c);
    return 0;
}

运行结果

$ gcc -o dup2 dup2.c 
$ cat 2.txt  //2.txt为空
$ ./dup2 2.txt
3
$ cat 2.txt 
2333         //输出重定向

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值