Linux中的基础IO(二)

文章目录

  • Linux中的基础IO(二)
    • 一、基本接口
    • 二、文件描述符
    • 三、文件描述符的分配规则
    • 四、重定向
    • 五、dup2系统调用
    • 六、minishell

一、基本接口

int open(const char *pathname, int flags, mode_t mode)
const char *pathname:代表要打开或要创建的目标文件。int flags:可传入多个参数选项,进行或运算组成flags
选项 解释
O_RDONLY 以只读的方式打开文件
O_WRONLY 以只写的方式打开文件
O_RDWR 读,写打开,(打开方式必须三选一)
O_CREAT 若文件不存在,则创建文件,需要使用mode选项指明新创建文件的权限(mode用函数umask()进行设置当前进程创建的文件的权限掩码,如umask(0)等)
O_APPEND 追加写
O_TRUNE 打开文件时,清空原有文件中的内容

如:写文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{umask(0);int fd = open("myfile", O_WRONLY|O_CREAT, 0644);if(fd < 0){perror("open");return 1;}int count = 5;const char *msg = "hello bit!\n";int len = strlen(msg);while(count--){write(fd, msg, len);//fd: 文件描述符, msg:缓冲区首地址, //len: 本次读取,期望  写入多少个字节的数据。 返回值:实际写了多少字节数据}close(fd);return 0;
}

  • 读文件:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}const char *msg = "hello bit!\n";char buf[1024];while(1){ssize_t s = read(fd, buf, strlen(msg));//类比writeif(s > 0){printf("%s", buf);}else{break;}}close(fd);return 0;
}

二、文件描述符

  • 1.文件描述符和文件流指针的区别:

文件流指针:文件流指针是标准库函数操作的句柄,类型为FILE*结构体类型
文件描述符:文件描述符是系统调用的句柄,类型是int型,注意⚠️,文件流指针中包含文件描述符

  • 2.Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0,标准输出1,标准错误2,所以输入输出可以采用下面 的方式
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{char buf[1024];ssize_t s = read(0, buf, sizeof(buf));if(s > 0){buf[s] = 0;write(1, buf, strlen(buf));write(2, buf, strlen(buf));}return 0;
}

三、文件描述符的分配规则

  • 1.文件描述符的管理

  • 结合上图:我们知道文件描述符都是从0开始的整数,当我们打开一个文件时,操作系统要在内存中创建相应的数据结构来管理描述目标文件
  • 于是就有了file结构体,表示已经打开的文件对象。
  • 而进程执行open系统调用,所以必须让进程和文件关联起来。
  • 描述进程的是进程控制块PCB,Linux中是task_struct,内部除了描述进程相关信息外,还有一个指针*file,指向一张表file_struct,这张表最重要的部分是包含一个指针数组,数组中的每个元素都指向一个打开文件的指针,所以,本质上文件描述符就是该数组的下标,即只要拿到数组下标,取数组中的file指针就可以找到对应的文件,上面就是操作系统管理文件描述符的方式
  • 2.文件描述符的分配规则:

看下面的代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;
}


发现文件描述符fd为3。那么关闭0或者2再试试:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{close(0);//close(2);int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;
}


发现结果是0或者2,由此得出文件描述符的分配规则是:在file_struct结构体中,找当前没有被使用的最小的下标,作为新的文件描述

四、重定向

  • 1.重定向:

先看代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{close(1);int fd = open("myfile", O_WRONLY|O_CREAT, 00644);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);fflush(stdout);close(fd);exit(0);
}

  • 此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中
  • 因为一开始我们关闭了标准输出文件描述符1,导致按照文件描述符的分配规则,fd就得到最小未使用的文件描述符,其代表数组的元素内容是指向myfile文件的,所以就输出到myfile中,fd=1。
  • 这种现象叫做输出重定向。常见的重定向有:>(清空重定向), >>(追加重定向), <(标准输入重定向)。
  • 2.重定向的原理:

  • 结合上图,重定向的本质是重新定向文件描述符所在的file指针的指向,即改变以个文件描述符所对应的文件信息,进而改变操作的文件
  • 注意⚠️文件描述符重定向的过程中文件描述符的数量没有发生改变,只是改变其数组内部file指针的指向而已

五、dup2系统调用

int dup2(int oldfd, int newfd);

作用是将newfd文件描述符的file指针指向oldfile文件描述符的file指针指向的文件。

看代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {int fd = open("myfile", O_CREAT | O_RDWR);if (fd < 0) {perror("open");return 1;}close(1);dup2(fd, 1);for (;;) {char buf[1024] = {0};ssize_t read_size = read(0, buf, sizeof(buf) - 1);if (read_size < 0) {perror("read");break;}printf("%s", buf);fflush(stdout);}return 0;
}


说明使用dup2也可以改变文件描述符的指向(重定向)

六、minishell

#include <stdio.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>char g_command[1024];//该函数的功能是获取命令行输入的数据
int GetCommand()
{//因为开始获得的内存中的内容是不定的,所以使用前要先初始化memset(g_command,'\0',sizeof(g_command));//这里需要为'\0'留一个位置,不能把g_command全部用来读取内容,否者就没有结束标志,容易发生内存访问越界if(fgets(g_command,sizeof(g_command)-1,stdin)==NULL){printf("fgets is error!\n");return -1;}//printf("g_command:%s\n",g_command);return 0;
}//解析字符串
char** DealCommand(char* command)
{if(command==NULL&&*command=='\0'){printf("dealcommand is error\n");return NULL;}//用来保存命令static char* argv[1024]={0};int argc=0;while(*command){//isspace函数用来去掉多余的空格//isspace的返回值:如果当前字符是空格返回1,否则返回0//注意'\0'在issapce函数中不算空格的,所以要进行判断while(!isspace(*command)&&*command!='\0'){argv[argc]=command;argc++;//去找下一个空格while(!isspace(*command)&&*command!='\0'){command++;}*command='\0';}command++;}argv[argc]=NULL;//for(int i=0;i<argc;i++)//{//  printf("%d:%s   ",i,argv[i]);//}//printf("\n");return argv;
}//进行重定向
int redirect(char * g_command)
{char* ptr = g_command;char* file = NULL;int fd ;//用来标记是清空重定向还是追加重定向int redirect_type = -1;while(*ptr !='\0'){//如果当前字符是 > ,把他置为'\0',并判断下一个位置是否为'\0'if(*ptr == '>'){*ptr++ = '\0';redirect_type++;if(*ptr == '>'){*ptr++ = '\0';redirect_type++;}//去掉多余的空格while(isspace(*ptr)){ptr++;}//file就是空格后面的第一个字符串file = ptr;//继续找空格,在这两个空格之间就是文件的名称while(!isspace(*ptr)&&*ptr != '\0'){ptr++;}*ptr='\0';//如果redirect_type==0说明是清空重定向,如果==1说明是追加重新定向if(redirect_type == 0){fd = open(file,O_CREAT|O_TRUNC|O_WRONLY,0664);}else{fd = open(file,O_CREAT|O_APPEND|O_WRONLY,0664);}dup2(fd,1);}ptr++;}return 0;
}//进行程序替换
int exec()
{redirect(g_command);char** argv=DealCommand(g_command);pid_t pid =fork();if(pid<0){printf("foek is error!\n");return -1;}else if(pid==0){//child//如果argc中为NULL,就直接返回if(argv[0]==NULL){exit(-1);}//进行替换,execvp第一个参数是可执行程序名,第二个参数是该可执行程序的参数组成的数组execvp(argv[0],argv);//execl("/usr/bin/ls","ls","-a",NULL);}else{//fatherwaitpid(pid,NULL,0);}return 0;
}int main()
{//循环读数据while(1){printf("[dev@localhost dev]$ ");int ret = GetCommand();if(ret == -1){//如果读取失败,继续循环读取,不能直接break调continue;}//处理解析数据//char** argv = DealCommand(g_command);//进行替换//exec(argv);exec();}return 0;
}

Linux中的基础IO(二)相关推荐

  1. Linux中的基础IO(一)

    Linux中的基础IO 文章目录 Linux中的基础IO 一.C语言中的文件接口 二.随机读写数据文件 三.文件读写的出错检测 一.C语言中的文件接口 写在前面 计算机文件是以计算机硬盘为载体存储在计 ...

  2. Linux系统进阶-基础IO

    Linux系统进阶-基础IO 文章目录 Linux系统进阶-基础IO C语言中的文件接口 对文件进行写入 对文件进行读取 什么是当前路径 默认打开的三个流 stdout & stderr 系统 ...

  3. linux+管道+分段,Linux中shell基础、重定向、管道符、环境变量

    原标题:Linux中shell基础.重定向.管道符.环境变量 1.什么是shell Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口(命令解释器).它接收用户输入的命令并把它送入内核 ...

  4. linux基础配置脚本,Linux中selinux基础配置教程详解

    selinux(Security-Enhanced Linux)安全增强型linux,是一个Linux内核模块,也是Linux的一个安全子系统. 三种模式: Enforcing:强制模式,在selin ...

  5. Linux中5种IO模型

    在了解IO模型时需要清楚什么是同步和异步,什么是阻塞和非阻塞 同步/异步 阻塞/非阻塞 当IO操作发生时,一定是两方参与的,分别是调用方和被调用方.阻塞和非阻塞相对于的事调用方,同步和异步相对于的好似 ...

  6. 【Linux学习】基础IO

    目录 前言 一.C语言文件IO 1. C语言文件接口以及打开方式 2. 对当前路径的理解 3. 默认打开的三个流 二. 系统文件IO 1. 系统接口 open write read close 系统接 ...

  7. 【Linux练习生】基础IO(详细)

    本节我们讲解基础IO的部分,将围绕以下内容进行梳理讲解: 复习C文件IO相关操作 认识文件相关系统调用接口 认识文件描述符,理解重定向 对比fd和FILE,理解系统调用和库函数的关系 理解文件系统中i ...

  8. linux中_Linux基础知识(Linux系统、Linux中的链表)

    Linux系统简介 Linux系统的结构及特点 Linux系统的结构图如下图所示: 从上图可以看出,Linux是一个典型的宏内核(一体化内核)结构.硬件系统上面时硬件抽象层,在硬件抽象层上面时内核服务 ...

  9. Linux中的文件IO

    1.什么是文件IO (1)IO就是input/output,输入/输出.文件IO的意思就是读写文件. 2.linux常用文件IO接口 (1)open.close.write.read.lseek 3. ...

最新文章

  1. c语言编程能控制热风炉,利用C语言设计热风炉悬链线拱顶研究.pdf
  2. decimal.Round 的区别
  3. (十四)Java springcloud B2B2C o2o多用户商城 springcloud架构- Spring Cloud构建分布式电子商务平台...
  4. 为什么选择ASP.NET Core
  5. 交换机的基本原理配置(一)
  6. 在 Ubuntu 14.04 中配置 PXE 服务器
  7. 【Pyhton爬虫】中国大学排名爬虫
  8. 【面试经验】关于BERT,面试官们都怎么问
  9. Git:git如何拉取指定分支到本地
  10. Linux下防止rm -frv 删除错误的解决办法
  11. SSE图像算法优化系列十五:YUV/XYZ和RGB空间相互转化的极速实现(此后老板不用再担心算法转到其他空间通道的耗时了)。...
  12. IDEA 插件开发 中文乱码
  13. 读取ClientKey的另一种思路,无需注入DLL
  14. linux内核零拷贝技术
  15. mysql5.1免安装版_mysql5.1免安装版配置
  16. 学校人脸识别门禁功能介绍
  17. helm charts 使用
  18. python怎么加图片_python如何增加背景图片
  19. 理解“卷积” Understanding Convolutions
  20. iPhone数据线连接电脑成为电脑USB摄像头

热门文章

  1. 初一七年级计算机信息全册教案,初一信息技术教案全一册
  2. Linux 下 Redis 安装教程
  3. Cortex-M3-栏目-文章来源
  4. Cortex‐M3-总线接口
  5. bash环境(变量与bash配置文件)
  6. hibernate的映射文件字段长度和数据库里面的字段长度
  7. 网络服务器预防dos***的层次
  8. VBS的疑惑,它们不考虑效率吗?
  9. POJ - 2942 Knights of the Round Table(点双缩点+二分图判定)
  10. 洛谷 - P4011 孤岛营救问题(bfs+状态压缩)