Linux系统编程---文件IO

一、系统调用

由操作系统实现并提供给外部应用程序的编程接口(Application Programming Interface,API),用户程序可以通过这个特殊接口来获得操作系统内核提供的服务

系统调用和库函数的区别:

系统调用(系统函数)     内核提供的函数

库调用                         程序库中的函数

错误处理函数

errno用于记录系统的最后一次错误代码,返回一个int值(错误码),在errno.h中定义,不同的错误码表示不同的含义,新建errno.c如下:

#include<stdio.h>
#include<errno.h>
#include<string.h>int main(void)
{FILE *fp = fopen("txt","r");//打开一个不存在的文件if (NULL == fp){printf("fopen failed\n");printf("errno:%d\n",errno);//打印errno返回的错误码printf("fopen:%s\n",strerror(errno));//使用strerror函数来解释错误码return 1;}return 0;
}

编译再执行可得如下结果:

fopen failed
errno:2
fopen:No such file or directory

虚拟地址空间:

文件描述符

  • 当我们打开文件或者新建文件时,系统会返回一个文件描述符用来指定已打开的文件,这个文件描述符相当于这个已打开文件的标号,操作这个文件描述符就相当于操作这个描述符所指定的文件;
  • 程序运行起来后每个进程都有一张文件描述符的表,标准输入、输出,标准错误输出,对应的文件描述符0、1、2就记录在表中,程序运行起来后这三个文件描述符是默认打开的;

文件描述符是指向一个文件结构体的指针

进程控制块(PCB):本质---结构体

FILE结构体:主要包含文件描述符、文件读写位置、IO缓冲区三部分内容

最大打开文件数:一个进程默认打开文件的个数1024

命令查看:ulimit -a 查看open files 对应值。默认为1024   

可以使用ulimit -n 4096 修改

cat /proc/sys/fs/file-max可以查看该电脑最大可以打开的文件个数。受内存大小影响。

二、常用文件IO函数

1.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:文件打开方式: #include <fcntl.h>

                O_RDONLY|O_WRONLY|O_RDWR

                O_CREAT|O_APPEND|O_TRUNC|O_EXCL|O_NONBLOCK ....

        mode:这个参数只有在文件不存在时有效,指新建文件时指定文件的权限

                取值8进制数,用来描述文件的访问权限。 rwx 0664

                创建文件最终权限 = mode & ~umask

返回值:

        成功: 打开文件所得到对应的 文件描述符(整数)

        失败: -1, 设置errno

flags必选项:

O_RDONLY        以只读的方式打开    

O_WRONLY       以只写的方式的打开

O_RDRW           以可读、可写的方式打开

 可选项,和必选项进行位或(|)

O_CREAT                文件不存在则创建文件,使用此选项时需使用mode说明文件的权限
O_EXCL                   如果同时指定了O_CREAT,且文件已经存在,则出错
O_TRUNC                如果文件存在,则清空文件内容
O_APPEND              写文件时,数据添加到文件末尾
O_NONBLOCK         对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O

打开dict.cp,如果不存在则创建,并添加权限,如果存在则清空内容:

#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>int main(int argc,char *argv[])
{int fd;open("./dict.cp",O_RDONLY | O_CREAT | O_TRUNC,0644);//rw-r--r--printf("fd = %d\n",fd);close(fd);return 0;
}

 对于存在mydir目录以及不存在mydir目录执行以下程序:

#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>int main(int argc,char *argv[])
{int fd;open("mydir",O_WRONLY);printf("fd = %d,errno = %d:%s\n",fd,errno,strerror(errno));close(fd);return 0;
}

mydir目录存在输出为:

fd = 21991,errno = 21:Is a directory

mydir目录不存在输出为:

fd = 22002,errno = 2:No such file or directory

2.close函数

#include <unistd.h>

int close(int fd);
功能:
        关闭已打开的文件
参数:
        fd:文件描述符,open()的返回值
返回值:
        成功:0
        失败:-1,并设置errno

3.write函数

#include <unistd.h>


ssize_t write(int fd, const void *buf, size_t count);
功能:
        把指定数目的数据写到文件(fd)
参数:
        fd:文件描述符
        buf:数据首地址
        count:写入数据的长度(字节)
返回值:

        成功:实际写入数据的字节个数
        失败:-1

4.read函数

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
功能:
        把指定数目的数据读到内存(缓冲区)
参数:
        fd:文件描述符
        buf:内存首地址
        count:读取的字节个数
返回值:
        成功:实际读取到的字节个数
        失败:-1

 用read和write实现一个copy函数:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>int main(int argc,char *argv[])
{char buf[1024];int n = 0;int fd1 = open(argv[1],O_RDONLY);//readif (fd1 == -1){perror("open argv1 error");exit(1);}int fd2 =open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0644);if (fd2 == -1){perror("open argv2 error");exit(1);}while((n = read(fd1,buf,1024)) != 0){if (n < 0){perror("read error");break;  }write(fd2,buf,n);}close(fd1);close(fd2);return 0;
}

5.lseek函数

#include <sys/types.h>#include cunistd.h>


off_t lseek(int fd,off_t offset,int whence);

功能:
       改变文件的偏移量(读写位置)

参数:
        fd:文件描述符

        offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。


        whence:其取值如下:
                SEEK_SET:从文件开头移动offset个字节

                SEEK_CUR:从当前位置移动offset个字节            

                SEEK__END:从文件未尾移动offset个字节

返回值:
        若lseek成功执行,则返回新的偏移量

        如果失败,返回-1

lseek允许超过文件结尾设置偏移量,文件会因此被拓展。

使用lseek获取文件大小:

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>int main(int argc,char *argv[])
{int fd = open(argv[1],O_RDWR);if (fd == -1){perror("read error");exit(1);}int lenth = lseek(fd,0,SEEK_END);//获取文件大小printf("file size:%d\n",lenth);close(fd);return 0;
}

make之后执行 ./lseek_test dict.c 可得到dict.c的文件大小

out:

file size:48

使用lseek扩展文件大小:

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>int main(int argc,char *argv[])
{int fd = open(argv[1],O_RDWR);if (fd == -1){perror("read error");exit(1);}int lenth = lseek(fd,52,SEEK_END);//扩展文件大小printf("file size:%d\n",lenth);write(fd,"a",1);close(fd);return 0;
}

make之后执行 ./lseek_test dict.c 可得到dict.c的文件大小,再执行ls -l dict.c得到扩展之后的文件大小,输出分别为:

file size:100

-rw-rw-r-- 1 *** *** 101 *** ** *** dict.c

应用场景:

1. 文件的“读”、“写”使用同一偏移位置。

2. 使用lseek获取文件大小(返回值接收)

3. 使用lseek拓展文件大小:要想使文件大小真正拓展,必须【引起IO操作】(即write)

使用 truncate 函数,直接拓展文件。

int ret =truncate("dict.cp”,250):

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>int main(int argc,char *argv[])
{//open/lseek(fd,249.SEED_END)/write(fd,"\0",1);int ret = truncate("dict.cp",200);printf("ret = %d\n",ret);return 0;
}

起始cidt.cp文件大小为0,经过以上代码扩展之后,cidt.cp文件大小为200

三、系统调用和库函数比较---预读入缓输出

fputc/fgetc实现:

int main(void){FILE *fp,*fp_out;int n = 0;fp = fopen( " hello.c" , "r");if(fp == NULL){perror( " fopen error" );exit( 1);}fp_out = fopen ( "hello.cp" ,"w" );if(fp_out =NULL){perror( "fopen error" );exit(1);}while((n = fgetc(fp))!= EOF){fputc(n, fp_out) ;}fclose(fp);fclose(fp_out);return 0;
}

read/write实现:

int main( int argc, char *argv[])
{char buf[ 1];int n = 0;int fd1 = open(argv[1],0_RDONLY);int fd2 = open(argv[2],O_RDWR|0_CREAT|0_TRUNC,0664);while((n = read (fd1,buf,1)) != 0){write(fd2, buf, n);}close(fd1);close(fd2);return 0;
}

结果表明:read/write速度慢

原因分析:

  • read/write这块,每次写一个字节,会疯狂进行内核态和用户态的切换,所以非常耗时。
  • fgetc/fputc,有个缓冲区,4096,所以它并不是一个字节一个字节地写,内核和用户切换就比较少

预读入,缓输出机制。所以系统函数并不是一定比库函数牛逼,能使用库函数的地方就使用库函数。

  • 标准IO函数自带用户缓冲区,系统调用无用户级缓冲。系统缓冲区是都有的。

四、阻塞和非阻塞

产生阻塞的场景:读设备文件。读网络文件的属性。(读常规文件无阻塞概念)

/dev/tty -- 终端文件。

open("/dev/tty", O_RDWR | O_NONBLOCK) --- 设置 /dev/tty 非阻塞状态。(默认为阻塞状态)

更改非阻塞读取终端——超时设置

#include <unistd.h>  
#include <fcntl.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <errno.h>  
#include <string.h>  #define MSG_TRY "try again\n"  
#define MSG_TIMEOUT "time out\n"  int main(void)  
{  //打开文件int fd, n, i;  fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);  if(fd < 0){  perror("open /dev/tty");  exit(1);  }  printf("open /dev/tty ok... %d\n", fd);  //轮询读取char buf[10];  for (i = 0; i < 5; i++){  n = read(fd, buf, 10);  if (n > 0) {                    //说明读到了东西  break;  }  if (errno != EAGAIN) {          //EWOULDBLOCK    perror("read /dev/tty");  exit(1);  } else {  write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));  sleep(2);  }  }  //超时判断if (i == 5) {  write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT));  } else {  write(STDOUT_FILENO, buf, n);  }  //关闭文件close(fd);  return 0;  
}  

五、传入传出参数

传入参数:

        1. 指针作为函数参数。

        2. 同常有const关键字修饰。

        3. 指针指向有效区域, 在函数内部做读操作。

传出参数:

        1. 指针作为函数参数。

        2. 在函数调用之前,指针指向的空间可以无意义,但必须有效。

        3. 在函数内部,做写操作。

        4。函数调用结束后,充当函数返回值。

传入传出参数:

        1. 指针作为函数参数。

        2. 在函数调用之前,指针指向的空间有实际意义。

        3. 在函数内部,先做读操作,后做写操作。

        4. 函数调用结束后,充当函数返回值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/814508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一起学习python——基础篇(19)

今天来说一下python的如何修改文件名称、获取文件大小、读取文中指定的某一行内容。 1、修改文件名称&#xff1a; import os testPath"D:/pythonFile/test.txt" testPath2"D:/pythonFile/test2.txt" #修改文件名称使用rename方法&#xff0c; #第一个参…

TQ15EG开发板教程:在MPSOC上运行ADRV9009(vivado2018.3)

首先需要在github上下载两个文件&#xff0c;本例程用到的文件以及最终文件我都会放在网盘里面&#xff0c; 地址放在最后面。在github搜索hdl选择第一个&#xff0c;如下图所示 GitHub网址&#xff1a;https://github.com/analogdevicesinc/hdl/releases 点击releases选择版…

31省结婚、离婚、再婚等面板数据(1990-2022年)

01、数据介绍 一般来说&#xff0c;经济发达地区的结婚和离婚率相对较高&#xff0c;而经济欠发达地区的结婚和离婚率相对较低。此外&#xff0c;不同省份的文化、习俗、社会观念等因素也会对结婚和离婚情况产生影响。 本数据从1990年至2022年&#xff0c;对各地区的结婚、离…

Github 2024-04-14 php开源项目日报Top9

根据Github Trendings的统计,今日(2024-04-14统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目9TypeScript项目1Laravel: 以优雅语法简化Web开发 创建周期:4028 天开发语言:PHP协议类型:MIT LicenseStar数量:30824 个Fork数量:1…

在Linux驱动中,如何确保中断上下文的正确保存和恢复?

大家好&#xff0c;今天给大家介绍在Linux驱动中&#xff0c;如何确保中断上下文的正确保存和恢复&#xff1f;&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 在Linux驱动中&am…

中国省级人口结构数据集(2002-2022年)

01、数据简介 人口结构数据不仅反映了地域特色&#xff0c;更是预测地区未来发展趋势的重要工具。在这些数据中&#xff0c;总抚养比、少年儿童抚养比和老年人口抚养比是三大核心指标。 少儿抚养比0-14周岁人口数/15-64周岁人口数 老年抚养比65周岁及以上人口数/15-64周岁人…

leetcode每日一题(1702. 修改后的最大二进制字符串)

题目描述 题解 这道题贪心的思想&#xff0c;我们只需要尽可能多的把0变成1&#xff0c;而且进行操作1才能使其变大。观察发现以下几点&#xff1a; 不论原字符串有多少个0&#xff0c;最后都会剩余1个0。 假设原字符串只有一个0&#xff0c;不能进行任何操作&#xff0c;显然…

一招将vscode自动补全的双引号改为单引号

打开设置&#xff0c;搜索quote&#xff0c;在结果的HTML选项下找到自动完成&#xff0c;设置默认引号类型即可。 vscode版本&#xff1a;1.88.1&#xff0c; vscode更新日期&#xff1a;2024-4-10

利用Java代码调用Lua脚本改造分布式锁

4.8 利用Java代码调用Lua脚本改造分布式锁 lua脚本本身并不需要大家花费太多时间去研究&#xff0c;只需要知道如何调用&#xff0c;大致是什么意思即可&#xff0c;所以在笔记中并不会详细的去解释这些lua表达式的含义。 我们的RedisTemplate中&#xff0c;可以利用execute方…

共轭梯度法 Conjugate Gradient Method (线性及非线性)

1. 线性共轭梯度法 共轭梯度法&#xff08;英语&#xff1a;Conjugate gradient method&#xff09;&#xff0c;是求解系数矩阵为对称正定矩阵的线性方程组的数值解的方法。 共轭梯度法是一个迭代方法&#xff0c;它适用于 1. 求解线性方程组&#xff0c; 2. 共轭梯度法也可…

学习基于pytorch的VGG图像分类 day5

注&#xff1a;本系列博客在于汇总CSDN的精华帖&#xff0c;类似自用笔记&#xff0c;不做学习交流&#xff0c;方便以后的复习回顾&#xff0c;博文中的引用都注明出处&#xff0c;并点赞收藏原博主. 目录 VGG的数据集处理 1.数据的分类 2.对数据集的处理 VGG的分类标签设置 …

2款Notepad++平替工具(实用、跨平台的文本编辑器)

前言 今天大姚给大家分享2款Notepad平替工具&#xff0c;实用、跨平台&#xff08;支持Window/MacOS/Linux操作系统平台&#xff09;的文本编辑器。 NotepadNext NotepadNext是一个跨平台的 Notepad 的重新实现。开发是使用 QtCreator 和 Microsft Visual C (msvc) 编译器完…

python输入某年某月某日判断这一天是这一年的第几天

如何使用python实现输入某年某月某日判断这一天是这一年的第几天 from datetime import datetime #引入日期类 def is_leap_year(year):"""判断是否为闰年"""return (year % 4 0 and year % 100 ! 0) or (year % 400 0)# 根据年份和月份返回当…

深度解析 Spark(进阶):架构、集群运行机理与核心组件详解

关联阅读博客文章&#xff1a;深度解析SPARK的基本概念 引言&#xff1a; Apache Spark作为一种快速、通用、可扩展的大数据处理引擎&#xff0c;在大数据领域中备受关注和应用。本文将深入探讨Spark的集群运行原理、核心组件、工作原理以及分布式计算模型&#xff0c;带领读者…

test4141

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

ES6: set和map数据结构以及使用场景

ES6:set和map数据结构 一、Set 数据结构&#xff1a;二、使用场景&#xff1a;使用Set 进行去重三、Map 数据结构四、使用场景&#xff1a;使用Map进行树型数据懒加载刷新五、Set和Map的区别六、Map、Set的实际使用场景 Set 和 Map 是 ES6 中引入的两种新的数据结构&#xff0c…

JavaScript中的Blob、Buffer、ArrayBuffer和TypedArray详解

文章的更新路线&#xff1a;JavaScript基础知识-Vue2基础知识-Vue3基础知识-TypeScript基础知识-网络基础知识-浏览器基础知识-项目优化知识-项目实战经验-前端温习题&#xff08;HTML基础知识和CSS基础知识已经更新完毕&#xff09; 正文 摘要&#xff1a;本文详细介绍了JavaS…

(UDP)其他信息: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

“System.Net.Sockets.SocketException”类型的异常在 mscorlib.dll 中发生&#xff0c;但未在用户代码中进行处理其他信息: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。这个异常表示端口已经被占用了&#xff0c;需要释放端口或者使用其他端口来建立连接。您可以…

vite+react+ts+scss 创建项目

npm create vitelatest输入项目名称选择react选择typescript swc WC 通过利用 Rust 编写的编译器&#xff0c;使用了更先进的优化技术&#xff0c;使得它在处理 TypeScript 代码时能够更快地进行转换和编译。特别是在大型项目中&#xff0c;SWC 相对于传统的 TypeScript 编译器…

springboot火车运输物资定额领用管理系统java

本系统设计的现状和趋势&#xff0c;从需求、结构、数据库等方面的设计到系统的实现&#xff0c;分别为管理员和库房部、财务部、用户、生产部的实现。论文的内容从系统的设计、描述、实现、分析、测试方面来表明开发的过程。本系统根据现实情况来选择一种可行的开发方案&#…