Linux内核中的FILE文件数据结构和dup,dup2函数

时间:2022-07-27 23:37:41

内核中的数据结构和dup,dup2函数

首先介绍一下内核数据结构:

一个打开的文件在内核中使用三种数据结构表示:
文件描述符表
文件描述符标识
文件表项指针
文件表项
文件状态标志
读写追加同步和非阻塞
当前文件偏移量
i节点表项指针
引用计数器
i节点
文件类型和对该文件的操作
当前文件长度
文件所有者
文件所在的设备,文件访问权限
指向文件数据在磁盘块上所在位置的指针等

下面用一张图来阐述这三种数据结构的关系

Linux内核中的FILE文件数据结构和dup,dup2函数

在上面我们简单的介绍了一下Linux下关于内核文件读写的相关的数据结构,所以对内核文件读写相关原理有了一个了解,那么下面主要介绍的就是dup和dup2这两个函数,类似于重定向的意思;

dup和dup2函数

在讲dup和dup2之前可能需要对重定向的意思大概有个了解,重定向就是将你需要的输入或者输出替换成其他的输入或者输出

 如:cat < password; //输入重定向
cat > a.txt //输出重定向
cat >> a.txt //追加输出重定向

下面就介绍一下dup和dup2函数的含义

#include<unistd.h>
int dup(int oldfd);
int dup2(int oldfd,int newfd);
返回:成功返回新文件描述符,出错返回-1
功能:文件描述符的复制
参数:
oldfd原来的文件描述符
newfd新的文件描述符
由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值
用dup2则可以用newfd参数制定新描述符的数值,如果newfd已经打开,则先将其关闭,如oldfd等于newfd,则dup2返回newfd,而不关闭它
在进程间通信时可采用来改变进程的标准输入和标准输出设备

if(dup2(oldfdin,newfdin)!=newfdin){

}

dup2是将oldfd复制给newfd中去,复制的最根本的东西是文件描述符指针;
如:
dup2(fd_in,STDIN_FILENO);
dup2(fd_out,STDOUT_FILENO);
其是就将fd_in复制给STDIN_FILENO;

使用dup2函数打造我们自己的重定向的mycat

在上一篇博客中的copy.c在这里进行了引用,所以在这篇中就不去赘述了。

#include<stdio.h>
#include<stdlib.h>
#include"io.h"
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
/**
*使用dup或者dup2打造属于我们自己的myCat,使用+来标识输入重定向,使用-来标识我们的输出重定向
*
*/


int main(int argc,char * argv[]){
int fdin = STDIN_FILENO;
int fdout = STDOUT_FILENO;
int flag = 0;
int i = 0;
for(i = 1; i < argc;i++){
//如果其中有符号跟+相同
if(!strcmp("+",argv[i])){
fdin = open(argv[++i],O_RDONLY);
if(fdin < 0){
perror("open file error\n");
exit(EXIT_FAILURE);
}
//进行输入重定向,将输入的参数文件的文件描述符中的文件数据项的指针赋值给标准输出,所以此时标准输出的文件数据项指针指向的就是
//fdin中指针指向的位置。。所以被输入的重新定向为文件了
if(dup2(fdin,STDIN_FILENO)!=STDIN_FILENO){
perror("dup2 error\n");
exit(EXIT_FAILURE);
}
close(fdin);
//如果有符号跟—相同的话,进行输出重定向,将输出的参数的文件文件描述符中的文件数据项的指针赋值给标准输出,此时标准输出的指向不再是标准 //输出,而是更换成文件了。所以此时也就重新定向给了输出文件
}else if(!strcmp("-",argv[i])){
fdout = open(argv[++i],O_WRONLY | O_CREAT | O_TRUNC,0777);
if(fdout < 0){
perror("open write file error\n");
exit(EXIT_FAILURE);
}
if(dup2(fdout,STDOUT_FILENO) != STDOUT_FILENO){
perror("dup2 write file error\n");
exit(EXIT_FAILURE);
}
close(fdout);
}else{
// 如mycat a.txt,也就是将文件的内容输出到界面上
flag = 1;
fdin = open(argv[i],O_RDONLY);
if(fdin < 0 ){
perror("open read file2 error\n");
exit(EXIT_FAILURE);
}
if(dup2(fdin,STDIN_FILENO)){
perror("dup2 error2\n");
exit(EXIT_FAILURE);
}
copy(STDIN_FILENO,STDOUT_FILENO);
close(fdin);
}
}

if(!flag){
copy(STDIN_FILENO,STDOUT_FILENO);
}
return 0;
}

fcntl函数:

#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd,int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd ,struct floct *lock);

返回:若成功则以来于cmd,若出错为-1
功能:可以改变已经打开文件的性质

常见的功能:
复制一个现存的描述符号,新文件描述符号作为函数值返回(cmd = F_DUPFD)
获得/设置文件描述符标志(cmd = F_GETFD或F_SETFD)
获得/设置文件状态标志(cmd=F_GETFL或F_SETFL)
获得/设置文件锁(cmd=F_SETLK.cmd=F_GETLK.F_SETLKW)
第三个参数为struct flock结构

使用fcntl动态的修改file的属性

/**
*使用fcntl函数来动态的追加或者取出掉相关属性
*/


void setFcntl(int fd,int flag){
int val = fcntl(fd,F_GETFL);
if(val != -1){
val |= flag;
}
if(fcntl(fd,F_SETFL,val) != -1){
fprintf(stdin,"set file flag success\n");
}else{
fprintf(stderr,"set file flag failure\n");
}
}

使用fcntl动态的清除掉file的某些读写属性

/**
*动态的去除掉文件中的相关属性
*/

void clearFcntl(int fd,int flag){
int val = fcntl(fd,F_GETFL);
if(val != -1){
val &=~flag;
}
if(fcntl(fd,F_SETFL,val)!=-1){
printf("clear file flag success\n");
}else{
printf("clear file flag failure\n");
}
}

关于I/O处理的五种模型;

**阻塞I/O模型:**
若所调用的I/O函数没有完成相关的功能就会使进程挂起,直到相关数据到达才会返回,如终端,网络设备的访问

非阻塞模型

    当请求的I/O操作不能完成是,则不让进程休眠,而且返回一个错误
如:open read write访问
低速系统调用时,进程可能会阻塞
非阻塞I/O确定操作,(read,open,write)不阻塞,如果操作不能完成,则直接返回出错
设定非阻塞方式
使用open打开文件,设置O_NONBLOCK标志
如果一个文件已经打开,则使用fcntl修改文件状态标志
或者在打开的同时直接进行设置

I/O多路转接模型

    如果请求的I/O操作阻塞,且他不是真正阻塞I/O而且让其中一个函数等待,在这期间,IO还能进行其他操作,如select函数

信号驱动模型

    在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动I/0

异步I/O模型:

    在这种模型下,当一个描述符已准备好,可以启动I/O时,进程会通知内核,由内核进行后续处理,现在用的比较少

在最后谢谢 大家的观看,写的不好的,或者错误的,请各位看官不腻赐教,不懂的地方,也可以相互交流,这也是在记录自己的成长过程的一种方式,谢谢,欢迎持续访问博客:会有更多精彩的

欢迎继续访问,我的博客