Linux系统编程---文件IO

news2024/11/25 20:18:22

一、系统调用

由操作系统实现并提供给外部应用程序的编程接口(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

2.close函数

#include <unistd.h>

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

代码示例:

#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;
}

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);//read
        if (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 1seek(int fd,off_t offset,int whence);

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

参数:
        fd:文件描述符

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


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

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

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

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

        如果失败,返回-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);//获取文件大小
        //int lenth = lseek(fd,107,SEEK_END);//扩展文件大小
        printf("file size:%d\n",lenth);

        //write(fd,"a",1);

        close(fd);

        return 0;
}

应用场景:

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

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

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

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

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",250);
        printf("ret = %d\n",ret);

        return 0;
}

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

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.coloradmin.cn/o/1591220.html

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

相关文章

《零基础入行IT:步步为营的转型攻略与实践策略》

在信息化社会&#xff0c;IT行业以其强劲的发展势头、广阔的就业前景和丰厚的薪酬待遇&#xff0c;吸引了无数希望转行或初入职场人士的目光。然而&#xff0c;对于毫无相关背景知识的人来说&#xff0c;如何成功叩开IT行业的大门&#xff0c;似乎是一项颇具挑战性的任务。本文…

RabbitMQ的介绍

为什么使用 MQ&#xff1f; 流量削峰和缓冲 如果订单系统最多能处理一万次订单&#xff0c;这个处理能力在足够应付正常时段的下单&#xff0c;但是在高峰期&#xff0c;可能会有两万次下单操作&#xff0c;订单系统只能处理一万次下单操作&#xff0c;剩下的一万次被阻塞。我们…

HCIP-Datacom(H12-821)题库补充(4月12日)

最新 HCIP-Datacom&#xff08;H12-821&#xff09;完整题库请扫描上方二维码访问&#xff0c;持续更新中。 在BGP进程下&#xff0c;Aggregate命令中的detail&#xff3f;suppressed关键字的作用是以下哪一项&#xff1f; A&#xff1a;抑制生成的聚合路由下发IP路由表 B&…

【前端】解决前端图表大数据配色难题:利用HSL动态生成颜色方案

解决前端图表大数据配色难题&#xff1a;利用HSL动态生成颜色方案 在数据可视化项目中&#xff0c;尤其是当需要绘制包含大量数据点的图表时&#xff0c;一个常见的挑战是如何为每个数据点分配一个独特而又视觉上容易区分的颜色。使用固定的颜色列表可能在数据点数量超过列表限…

人脸表情数据集

https://download.csdn.net/download/mqdlff_python/89128573

【最全四种方案对比】Redis 与 MySQL 数据一致性问题探讨

前言&#xff1a; 缓存必须要有过期时间&#xff1b;保证数据库跟缓存的最终一致性即可&#xff0c;不必追求强一致性。 目录如下&#xff1a; 1. 什么是数据库与缓存一致性2. 缓存的使用策略 2.1 Cache-Aside (旁路缓存)2.2 Read-Through&#xff08;直读&#xff09;2.3 W…

秒杀优化-异步秒杀思路

6、秒杀优化 6.1 秒杀优化-异步秒杀思路 我们来回顾一下下单流程 当用户发起请求&#xff0c;此时会请求nginx&#xff0c;nginx会访问到tomcat&#xff0c;而tomcat中的程序&#xff0c;会进行串行操作&#xff0c;分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足…

vue 原理【详解】MVVM、响应式、模板编译、虚拟节点 vDom、diff 算法

vue 的设计模式 —— MVVM M —— Model 模型&#xff0c;即数据V —— View 视图&#xff0c;即DOM渲染VM —— ViewModel 视图模型&#xff0c;用于实现Model和View的通信&#xff0c;即数据改变驱动视图渲染&#xff0c;监听视图事件修改数据 初次渲染 将模板编译为 render …

面试-数据库基础以及MySql、ClickHost、Redis简介

面试-数据库基础以及MySql、ClickHost、Redis简介 0.数据完整性1.数据库并发控制1.1事物1.2 并发读写错误1.3 锁1.3.1 乐观锁与悲观锁1.3.2 共享锁和排他锁1.3.3 行锁与表锁1.3.4 意向锁 1.4 封锁协议与隔离级别1.5 MVCC1.5.1 概念1.5.2 当前读与快照读1.5.3 MVCC in InnoDB 2.…

linux启动流程(s3c2400)

概述 大致流程&#xff1a;内核&#xff08;kernel&#xff09;都是由bootloader程序引导启动的&#xff0c;所以我们应该先烧进去bootloader程序。然后可以通过保存的内核代码或者通过远程连接&#xff08;nfs/tftp&#xff09;的主机下载再运行&#xff0c;再挂载根文件系统。…

海外媒体宣发:旅游业媒体5个提高转化率绝佳实践方法-华媒舍

随着旅游业的迅速发展和数字化进程的加速&#xff0c;旅游业媒体成为了推动旅游业发展的重要力量。仅仅依靠大量流量和曝光并不足以实现收益最大化&#xff0c;更为关键的是如何提高旅游业媒体的转化率&#xff0c;将流量转化为实际销售和盈利。本篇文章将介绍并讨论5个提高旅游…

『VUE』16. v-model表单输入和绑定(详细图文注释)

目录 绑定机制v-model修饰符简易绑定使用修饰符lazy实现勾选框效果总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 绑定机制 在 Vue.js 中&#xff0c;使用 v-model 指令可以实现表单输入元素与 Vue 实例中的数据双向绑定。这…

Redis--16--Spring Data Redis

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Spring Data Redishttps://spring.io/projects/spring-data-redis 1.依赖2.RedisTemplate3.案例 序列化1.默认是 JdkSerializationRedisSerializer2.添加Redis配置文…

CSS导读 (复合选择器 上)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 二、CSS的复合选择器 2.1 什么是复合选择器 2.2 后代选择器(重要) 2.3 子选择器(重要) Questions 小提…

GRE/MGRE详解

GRE GRE&#xff1a;通用路由封装&#xff0c;是标准的三层隧道技术&#xff0c;是一种点对点的隧道技术&#xff1b; 该技术可以实现不同的网络之间安全的访问&#xff1b; 如上&#xff1a;可以使用该技术搭建一条专线&#xff0c;实现公司A与分公司A1之间相互通信&#xf…

蓝桥杯 前一晚总结 模板 新手版

《准备实足&#xff0c;冲冲冲 省一》https://www.yuque.com/lenyan-svokd/hi7hp2/hfka297matrtsxy2?singleDoc# 《准备实足&#xff0c;冲冲冲 省一》 #include<bits/stdc.h> // 包含标准库头文件using namespace std; using ll long long; // 定义 long long 数据类…

00_如何使用国内镜像源下载QT

如何使用国内镜像源下载QT 如何使用国内镜像源下载QT 如何使用国内镜像源下载QT 第一步&#xff1a;下载下载qt online installer 网站&#xff1a;https://download.qt.io/official_releases/online_installers/ 添加链接描述 下载windows版本 第二步&#xff1a; 剪切放…

synchronized的优化策略

synchronized的优化策略 一:synchronized 的"自适应"1.1:偏向锁 二:锁消除三:锁粗化 一:synchronized 的"自适应" 锁升级的过程: (1)未加锁的状态(无锁) 当代码中开始调用执行synchronized (2)偏向锁 遇到锁冲突 (3)轻量级锁 冲突进一步提升 (4)重量级锁 …

streamlit 大模型前段界面

结合 langchain 一起使用的工具&#xff0c;可以显示 web 界面 pip install streamlit duckduckgo-search 运行命令 streamlit run D:\Python_project\NLP\大模型学习\test.py import os from dotenv import load_dotenv from langchain_community.llms import Tongyi load…

主从同步优化

2.3.主从同步优化 主从同步可以保证主从数据的一致性&#xff0c;非常重要。 可以从以下几个方面来优化Redis主从就集群&#xff1a; 在master中配置repl-diskless-sync yes启用无磁盘复制&#xff0c;避免全量同步时的磁盘IO。Redis单节点上的内存占用不要太大&#xff0c;…