linux基础io(代码片段)

DR5200 DR5200     2022-12-06     666

关键词:

文章目录

一.C文件IO相关操作

只有文件名但不带路径的话,默认在当前路径下打开文件(以写的方式打开文件若文件不存在会自动创建文件),那到底什么是当前路径呢?

// test目录下 myproc.内容

#include<stdio.h>
int main()

        FILE* fp = fopen("test.txt","w");
        if(fp == NULL)
        
                perror("fopen");
                return 1;
        
        fclose(fp);

可以看出,当前路径和可执行程序所处的路径没有关系,在哪个路径下运行起来可执行程序,该路径就被称为当前路径

为了让大家看的更清楚,稍微修改一下代码,关闭文件后不让进程终止来查看进程的相关信息

// test目录下 myproc.内容

#include<stdio.h>
int main()

        FILE* fp = fopen("test.txt","w");
        if(fp == NULL)
        
                perror("fopen");
                return 1;
        
        fclose(fp);
        sleep(10000);

cwd(current work directory) : 在哪个路径下运行起来可执行程序,进程创建的临时文件都在当前路径下创建
exe : 可执行程序路径

打开文件,读写文件,关闭文件都是进程运行的时候完成的

#include<stdio.h>
int main()

        FILE* fp = fopen("test.txt","r");
        if(fp == NULL)
        
                perror("fopen");
                return 1;
        
        // 二.
        int count = 5;
        char buffer[64];
        while(count)
        
        	   // 以\\n为分隔符,一次读取一行内容
                fgets(buffer,sizeof buffer,fp);
                printf("%s",buffer);
                count--;
        
       
//      一. 
//      int ct = 5;
//      while(ct)
//      
//              fputs("hello bit\\n",fp);
//              ct--;
//      
        fclose(fp);

在Linux中,一切皆文件,键盘和显示器也可以将其当作文件,那么为什么在C语言当中我们没有打开显示器文件就可以直接使用printf函数进行写入呢?为什么没有打开键盘文件就可以直接使用scanf函数进行读取呢?,原因如下 :

任何进程在运行的时候,默认打开三个输入输出流
C默认会打开三个输入输出流,分别是stdin, stdout, stderr
仔细观察发现,这三个流的类型都是FILE*,(fopen返回值类型,文件指针),FILE* 是C语言的概念,文件描述符是系统级别的概念

#include<stdio.h>
int main()

        int count = 5;
        char buffer[64];
        while(count)
        
                fgets(buffer,sizeof buffer,stdin);
                printf("%s",buffer);
                count--;
        

        int ct = 5;
        while(ct)
        
                fputs("hello world\\n",stdout); // strerr
                ct--;
        

由"w" 和 “a” 可以联想到之前讲到的重定向(>)和追加重定向(>>)

test.txt已经存在5行hello world,使用"a"方式打开文件,向文件末尾追加5行hello lyp

#include<stdio.h>
int main()

        FILE* fp = fopen("test.txt","a");
        if(fp == NULL)
        
                perror("fopen");
                return -1;
        
        int count = 5;
        while(count)
        
                fputs("hello lyp\\n",fp);
                count--;
        
        fclose(fp);

text.txt中已经存在5行hello world,5行hello lyp,使用"w"方式打开文件,清空原始内容,写入5行hehe

#include<stdio.h>
int main()

        FILE* fp = fopen("test.txt","w");
        if(fp == NULL)
        
                perror("fopen");
                return -1;
        
        int count = 5;
        while(count)
        
                fputs("hehe\\n",fp);
                count--;
        
        fclose(fp);

二.系统文件IO

open系统调用接口介绍

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
 O_RDONLY: 只读打开
 O_WRONLY: 只写打开
 O_RDWR : 读,写打开
 这三个常量,必须指定一个且只能指定一个
 O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
 O_APPEND: 追加写
 mode:
 设置文件权限
 返回值:
 成功:新打开的文件描述符
 失败:-1

flags : 系统函数参数传参标志位

传递给flags的这些常量都是宏,通过如下命令可以查看这些宏的定义,可以发现这些宏的二进制序列只有一个1,这些常量进行或运算传递给flags,open函数内部,再通过与运算判断传递了哪些常量

int open(const char *pathname, int flags, mode_t mode)

	if(flags & X)
	
	if(flags & Y)
	

open(argu1,X | Y,argu3);
grep -E 'O_CREAT|O_RDONLY|O_WRONLY' /usr/include/ -R

下面我们就来使用一下open

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()

	 umask(0);
	 // O_WRONLY|O_CREAT 等价于 fopen 中的"w"
	 int fd = open("test.txt",O_WRONLY|O_CREAT,0666);
     printf("%d\\n",fd);

我们想以只写的方式打开test.txt文件,若test.txt文件不存在,会创建一个test.txt文件出来,权限为666
但结果权限为664(默认umask为2),所以我们可以把umask设置为0

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()

        umask(0);

        int fd1 = open("test.txt",O_WRONLY|O_CREAT,0666);
        printf("%d\\n",fd1);

        int fd2 = open("test.txt",O_WRONLY|O_CREAT,0666);
        printf("%d\\n",fd2);

        int fd3 = open("test.txt",O_WRONLY|O_CREAT,0666);
        printf("%d\\n",fd3);

        int fd4 = open("test.txt",O_WRONLY|O_CREAT,0666);
        printf("%d\\n",fd4);

        int fd5 = open("test.txt",O_WRONLY|O_CREAT,0666);
        printf("%d\\n",fd5);

通过运行结果发现,文件描述符从3开始,这是因为默认打开了标准输入(0),标准输出(1),标准错误(2),所谓的文件描述符是数组下标

write

 ssize_t write(int fd, const void *buf, size_t count);

fd : 文件描述符,buf : 写的内容 ,count : 期望写入的字节数
ssize_t : 实际写入的字节数
实际写入的字节数 <= 期望写入的字节数(ssize_t <= count)

// test.txt为空,向test.txt写入5行hello world

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()

        umask(0);

        int fd1 = open("test.txt",O_WRONLY|O_CREAT,0666);
        printf("%d\\n",fd1);

        int count = 5;
        const char* buf = "hello world\\n";
        while(count)
        
                write(fd1,buf,strlen(buf));
                count--;
        

read

ssize_t read(int fd, void *buf, size_t count);

fd : 文件描述符,buf : 读取的数据存放的地址 ,count : 期望所读的字节数
ssize_t : 实际所读的字节数
实际所读字节数 <= 期望所读字节数(ssize_t <= count)

// test.txt中有5行hello world,从test.txt中读取字符写入到标准输出中

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()

        umask(0);

        int fd1 = open("test.txt",O_RDONLY);
        printf("%d\\n",fd1);

        char c;
        while(1)
        
                ssize_t s = read(fd1,&c,1);
                if(s <= 0)
                
                        break;
                
                write(1,&c,1);
        

C语言提供的IO接口底层都封装了系统调用接口

为什么要进行封装 ?

(1).保证可读性以及调用简单
(2).保证跨平台性 : 我们可以发现C语言可以在任一平台上跑,原因在于C语言在设计时,与系统强相关的不可跨平台的接口都被C语言封装了一遍(我们上面用C语言所写的fopen(),fclose()等函数底层都封装了open,close等系统调用)

open函数返回值

在认识返回值之前,先来认识一下两个概念: 系统调用 和 库函数

上面的 fopen fclose fread fwrite fgets fputs都是C标准库当中的函数,我们称之为库函数(libc)。
而 open close read write lseek 都属于系统提供的接口,称之为系统调用接口
回忆一下我们讲操作系统概念时,画的一张图

文件描述符 fd

磁盘文件 vs 内存文件

内存文件(磁盘文件加载而来) : 一个进程可以打开多个文件,在系统中,任何时刻,都可能存在大量的已经打开的文件,对这些文件进行管理依然遵循先描述再组织,struct_file就是描述文件的结构体,以双向链表的方式将其组织起来,对文件的管理就变成了对双链表的增删查改,在内存中有task_struct,struct_file两条双链表,我们想要知道这些被打开的文件哪些属于某一个进程,我们就需要建立进程和文件的对应关系

磁盘文件 : 存在磁盘上的文件并不是只保存了内容,还保存了文件的属性/元信息(文件的名字,创建日期,大小)

文件 = 内容 + 属性

我们将文件打开时,是有两份的,一份在硬盘上,一份在内存里(内存文件更多的是文件的属性信息),当我们想要读写数据时,再延后式的慢慢加载数据到内存(这里会有缓冲区的概念)

// 人和操作系统唯一的交互方式就是进程,而进程和操作系统的交互方式是系统调用接口

open系统调用打开文件的过程

(1). 进程打开一个文件,内存中创建struct_file结构体,存储打开文件的属性信息
(2). 操作系统为该文件分配文件描述符(fd_array数组中未被分配的且最小的下标)
(3). 将struct_file结构体的地址填到fd_array数组对应的位置,返回该文件的文件描述符

由此可知read,write等系统调用为什么要传入fd ?,因为拿到fd可以找到文件对应的struct_file结构体,得到文件的相关信息

重定向

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()

        umask(0);
        int fd = open("test.txt",O_WRONLY|O_CREAT,0666);
        if(fd < 0)
        
                return 1;
        

        write(1,"hehe\\n",5);
        write(1,"hehe\\n",5);
        write(1,"hehe\\n",5);
        write(1,"hehe\\n",5);
        write(1,"hehe\\n",5);

        close(fd);

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()

        umask(0);
        close(1);
        int fd = open("test.txt",O_WRONLY|O_CREAT,0666);
        if(fd < 0)
        
                return 1;
        

        write(fd,"hehe\\n",5);
        write(fd,"hehe\\n",5);
        write(fd,"hehe\\n",5);
        write(fd,"hehe\\n",5);
        write(fd,"hehe\\n",5);

        close(fd);

对比这两段代码,我们会发现关闭显示器文件后,fd的值为1,即test.txt文件的文件描述符为1,达到的效果为原本要写入到显示器当中的内容写入到了test.txt中,这种现象叫做输出重定向(>)

重定向的本质 : 修改文件描述符对应的struct_file*的指向

// 输出重定向
cat > test.txt

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()

        umask(0);
        close(1);
        int fd = open("test.txt",O_WRONLY|O_CREAT,0666);
        if(fd < 0)
        
                return 1;
        
	
        printf("hello world\\n");
        fprintf(stdout,"hello fprintf\\n");
        fputs("hello fputs %d %d %f %c\\n",stdout);
		// 需要刷新缓冲区
        fflush(stdout);

        close(fd);

解释这段代码前先进行一下知识的铺垫

实际上,C语言的 fopen 函数打开文件主要做了以下三件事情
(1). 创建FILE结构体
(2). 底层调用open函数打开文件,返回fd,将fd填充到FILE结构体当中的 _fileno(封装的文件描述符)
(3). 返回FILE结构体的地址(FILE*)

所以我们C语言中平常所使用的fread,fwrite,fgets,fputs中拿到我们传递的参数stream(FILE* stream),根据stream指针找到struct FILE结构体,struct FILE结构体中封装了文件的fd,拿到fd后,去task_struct->files_struct->fd_array数组中得到文件对应的struct_file结构体,由此得到文件信息进行读取或写入

之前也提到过,任何一个进程,默认会打开3个文件 : 标准输入(键盘文件),标准输出(显示器文件),标准错误(显示器文件),在C语言中创建对应的FILE结构体,调用open函数打开文件,用返回值fd填充 _fileno,返回FILE结构体的地址给stdin,stdout,stderr(FILE*指针)

再来看这段代码,我们首先关闭了1号文件描述符,open打开文件后,1号文件描述符被分配给了test.txt文件,代码中 printf/fprintf/fputs 参数stream都为stdout,stdout->struct FILE结构体->fd->struct_file结构体,此时struct_file结构体对应test.txt文件,因此内容写入到了test.txt文件当中

// 输入重定向
cat < test.txt

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()

        umask(0);
        close(0);
        int fd = open("test.txt",O_RDONLY);
        if(fd < 0)
        
                perror("open");
                return 1;
        
        char buffer[50];
        fgets(buffer,50,stdin);
        printf("%s\\n",buffer);
		
		close(fd);

// 追加重定向

cat >> test.txt

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()

        umask(0);
        close(1);
        int fd = open("test.txt",O_WRONLY|O_APPEND); // 等价于C语言 fopen 中的"a"选项
        if(fd < 0)
        
                perror("open");
                return 1;
        

        printf("hello world\\n");
        fprintf(stdout,"hello fprintf\\n");
        fputs("hello fputs %d %d %f %c\\n",linux入门基础io(代码片段)

基础IO✔回顾C文件的接口✔系统文件I/O✔文件描述符文件描述符的分配规则重定向✔FILE缓冲区fclose和close✔dup2系统调用✔理解文件系统inode硬链接软链接文件的三个时间✔回顾C文件的接口在学习C语言时我们了解了一些C语言的对... 查看详情

linux入门基础io(代码片段)

基础IO✔回顾C文件的接口✔系统文件I/O✔文件描述符文件描述符的分配规则重定向✔FILE缓冲区fclose和close✔dup2系统调用✔理解文件系统inode硬链接软链接文件的三个时间✔回顾C文件的接口在学习C语言时我们了解了一些C语言的对... 查看详情

linux下的基础io(代码片段)

目录一.C语言文件IO操作(库函数)1.打开文件fopen 2.写入二进制文件fwrite和读取二进制文件fread3.关闭文件fclose 二.系统文件IO(系统调用接口)1.打开文件open系统调用2.读文件read和写文件write3.关闭文件close  三.文件描述符1.系统管理... 查看详情

linux:基础io(代码片段)

...述符三,静态库和动态库总结前言本文将为大家介绍基础IO的相关知识,让我一起来学习吧。一,复习C语言当中学习的文件接口操作FILE*fopen(constchar*path,constchar*mode); path:待要打开的文件 mode:以何种方式打开 r:可读方... 查看详情

linux:基础io(代码片段)

...述符三,静态库和动态库总结前言本文将为大家介绍基础IO的相关知识,让我一起来学习吧。一,复习C语言当中学习的文件接口操作FILE*fopen(constchar*path,constchar*mode); path:待要打开的文件 mode:以何种方式打开 r:可读方... 查看详情

linux基础io(代码片段)

文章目录一.C文件IO相关操作二.系统文件IOopen函数返回值重定向使用dup2系统调用给简易shell中增加重定向功能理解文件系统理解软硬链接三.动态库和静态库制作动静态库一.C文件IO相关操作只有文件名但不带路径的话,默认在... 查看详情

linux基础io(代码片段)

文章目录一.C文件IO相关操作二.系统文件IOopen函数返回值重定向使用dup2系统调用给简易shell中增加重定向功能理解文件系统理解软硬链接三.动态库和静态库制作动静态库一.C文件IO相关操作只有文件名但不带路径的话,默认在... 查看详情

linux基础io(代码片段)

目录系统文件IOopenwritereadclose文件描述符fdFILE重定向使用dup2系统调用文件系统磁盘磁盘和内存交互磁盘的分区与格式化Ext2文件系统的存储方案inode软硬链接软连接硬链接文件的三个时间系统文件IOopenopen接口的作用是打开文件。... 查看详情

linux基础io(代码片段)

目录系统文件IOopenwritereadclose文件描述符fdFILE重定向使用dup2系统调用文件系统磁盘磁盘和内存交互磁盘的分区与格式化Ext2文件系统的存储方案inode软硬链接软连接硬链接文件的三个时间系统文件IOopenopen接口的作用是打开文件。... 查看详情

linux基础io(代码片段)

文章目录C语言文件IOC语言文件接口汇总什么是当前路径?默认打开的三个流系统文件I/Oopenopen的第一个参数open的第二个参数open的第三个参数open的返回值closewriteread文件描述符fd文件描述符的分配规则重定向重定向的原理dup2... 查看详情

13linux下的基础io(代码片段)

不同操作系统所暴露出的接口是不同的,因此Linux下的一些系统调用接口是无法移植到Windows下的。文章目录一、C语言中的文件接口二、系统文件I/O2.1.系统调用接口open2.2.文件描述符fd(filedescriptor)2.3.补充内容--函数... 查看详情

5.linux基础io(代码片段)

目录open系统调用和库函数重定向文件管理、理解文件系统静态库动态库怎么打包文件 =内容+属性FILE*,c语言,软件如何理解硬件和软件的关系stdin,标准输入,键盘stdout,标准输出,显示器stderr,标准错误... 查看详情

linux练习生基础io(详细)(代码片段)

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

《linux从0到99》九基础io(代码片段)

基础IO1.回顾c语言文件操作接口2.系统调用文件操作系统01open函数02read函数03write函数04lseek函数05close函数3.文件描述符0&1&2文件描述符的分配规则(最小未分配原则)4.文件描述符与文件流指针的区别01文件流指针的本... 查看详情

万字详解linux系列基础io(代码片段)

文章目录前言(1)当前目录(2)stdin、stdout、stderr一、open(1)标志位(2)O_WRONLY(3)O_CREAT二、close,read,write三、文件描述符1.概念2.原理3.分配规则四、重定向1.输出 查看详情

万字详解linux系列基础io(代码片段)

文章目录前言(1)当前目录(2)stdin、stdout、stderr一、open(1)标志位(2)O_WRONLY(3)O_CREAT二、close,read,write三、文件描述符1.概念2.原理3.分配规则四、重定向1.输出 查看详情

linux文件系统与基础io(代码片段)

文件的宏观理解1⃣️文件在哪里呢?“狭义”上在磁盘,“广义”上一切皆文件。主要研究“狭义”,磁盘为外设,那么对文件的所有操作,本质都是对外设的输入输出,简称IO2⃣️文件是什么?空文... 查看详情

linux篇第九篇——基础io(系统文件io+文件描述符+重定向+文件系统+软硬链接)(代码片段)

⭐️这篇博客就要开始聊一聊Linux中基础IO相关知识,IO相信大家都不陌生,我们在C/C++中对文件进行读写的操作,也就是文件IO,这篇博客我也会带大家回顾一下。这篇博客还会介绍系统中的文件IO调用的接... 查看详情