【Linux】基础IO——库函数与系统调用的关系

news2024/9/29 13:24:58

目录

  • 引言
  • 1.文件操作语言方案(C的文件操作接口-库函数)
    • 打开文件、关闭文件——fopen、fclose
      • 打开文件的模式
    • 写入——fput、printf
    • 读取——fgets
  • 2.文件操作系统方案(系统的文件操作接口-系统调用)
    • 打开文件、关闭文件——open、close
      • open的参数flags的理解
      • open的参数flag的学习
    • 写入——write
    • 读取——read
    • 几个系统调用的使用
  • 3.库函数与系统调用的关系

引言

打开文件的本质,就是将该文件的属性加载到内存中,OS内部存在大量被打开的文件,所以就需要管理这些文件:
每打开一个文件,都要在OS内创建该文件对象的struct结构体,然后将这些struct file用某种数据结构链接起来,所以在OS内部对文件的管理就转化成了对链表的增删查改。
在这里插入图片描述

文件可以分为两大类:

  1. 磁盘文件:没有被打开,存储在磁盘上
  2. 被打开的文件:被打开了,在内存中创建了对应的数据结构管理文件的属性

文件是被OS打开的,谁让OS打开?
——进程,所以我们所学习的都是 进程 与 被打开文件 的关系(struct task_struct 与 struct file);


1.文件操作语言方案(C的文件操作接口-库函数)

要对文件进行操作,首先要打开文件,打开操作完成后也要关闭文件,下面复习两个接口,fopen与fclose;

打开文件、关闭文件——fopen、fclose

打开文件——fopen
头文件:stdio.h
函数:FILE *fopen(const char *path, const char *mode);
参数
path——打开文件的路径;
mode——打开文件的模式;
返回值:成功返回FILE的指针,失败返回NULL;
若失败会设置错误码errno(表明出错的原因),可以通过perror接收错误码,将错误码转化成错误码描述打印错误原因,也可以通过strerror将错误码转化成错误码字符串;

关闭文件——fclose
头文件:stdio.h
函数:int fclose(FILE *fp);
参数:pf:指向被打开的文件
返回值:成功返回0,失败返回EOF,并设置错误码errno。

打开文件的模式

打开模式含义若指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件创建新文件
“a”(追加)向文本文件尾添加数据创建新文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件创建新文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建立一个新的文件创建新文件
“a+”(读写)打开一个文件,在文件尾进行读写创建新文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件创建新文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写创建新文件

解析:
如果fopen以w模式打开文件,写入的规则是:
1.首先将文件清空
2.从重头重新写入
(若只打开文件,不写入就关闭,文件也会被清空)

如果fopen以a模式打开文件,写入的规则是:
不会清空文件,每次写入都是从文件的结尾写入。


写入——fput、printf

1.fputs
打开文件后还需要进行写入操作,写入操作接口有很多,这里复习fputs。

写入——fputs
头文件:stdio.h
函数:int fputs(const char *s, FILE *stream);
参数:将字符串s写入到文件流stream当中。
返回值:成功返回非负数(写入了多少个字符),错误返回EOF;

以一段最简单的代码为例:在当前目录下创建log.txt文件,并写入5条hello world!。

//fputs文件写入
#include <stdio.h>

#define LOG "log.txt"

int main()
{
    FILE *fp = fopen(LOG, "w");
    if(fp == NULL)
    {
        perror("fopen");
        return 1;
    }

    const char *str = "hello world!\n";
    int cnt = 5;
    while(cnt)
    {
        fputs(str, fp);
        cnt--;
    }

    fclose(fp);
    return 0;
}

2.printf、fprintf、sprintf、snprintf

头文件:stdio.h
函数:

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

解析:
printf——默认向显示器打印,很熟悉,不解析。(显示器也是文件,对应标准输出流stdout)
fprintf——指定文件流stream,向指定文件打印;第二个参数format是格式化控制(与printf一样)
sprintf——向缓冲区打印;可以以格式化流的方式,将格式化信息输出到自定义的缓冲区str里。
snprintf——与sprintf一样,多了一个参数size是缓冲区的大小。

注:
Linux下一切皆文件,显示器也是一个文件,所以:若fprintf(stdout, str);则与printf(str);一样向显示器打印。


读取——fgets

fgets

头文件:stdio.h
函数:char *fgets(char *s, int size, FILE *stream);
参数:从特定的文件流stream中,按行读取对应的内容,将读到的内容放入缓冲区s中,size是缓冲区的大小
返回值:成功返回字符串的起始地址,失败返回NULL。

2.文件操作系统方案(系统的文件操作接口-系统调用)

打开文件、关闭文件——open、close

open:

头文件
sys/types.h
sys/stat.h
fcntl.h
函数
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数
pathname——要打开的文件路径+文件名
flags——打开文件对应的选项(比如读、写)
mode——打开文件的权限(文件的权限,rwx,比如0666)
返回值
成功返回文件描述符(file descriptor),失败返回-1;

close:

头文件
unistd.h
函数
int close(int fd);


open的参数flags的理解

1.系统是如何给一个函数传递多个标志位的?
——比如想给一个函数传递多个标志位,肯定不能设置多个形参比如int func(int flag1, int flag2, int flag3)。又因为传入的标志位flag是int型,有32个比特位,我们可以用1个比特位表示1个标志位,一个整数就可以同时传递32个标志位了——位图
一般就是使用下面的这种方式传递标志位:

#define ONE 0x1
#define TWO 0x2
#define THREE 0x4
#define FOUR 0x8
#define FIVE 0x10

void Print(int flag)
{
    if(flag & ONE) printf("ONE\n");
    if(flag & TWO) printf("TWO\n");
    if(flag & THREE) printf("THREE\n");
    if(flag & FOUR) printf("FOUR\n");
    if(flag & FIVE) printf("FIVE\n");
}

int main()
{
	//测试
    Print(ONE);
    Print(TWO);
    Print(THREE);
    Print(FOUR);
    Print(FIVE);
    printf("----------\n");
    Print(ONE|TWO);
    printf("----------\n");
    Print(ONE|TWO|THREE);
    printf("----------\n");
    return 0;
}

结果:
在这里插入图片描述

而我们在查看open的参数flag的时候,发现有很多参数(如下图所示,没有截全),这些其实就是宏值,每个宏对应的数字比特位是不重叠的,每个对应一个选项,所以学习时学习每个的含义就可以了。
在这里插入图片描述

open的参数flag的学习

1.O_CREAT——文件存在就打开,不存在就创建
(一般创建新文件不使用两个参数的open,因为权限会乱码,要使用三个参数的open函数,指定文件权限)
2.O_WRONLY——只写
(O_WRONLY | O_CREAT默认不会对原始文件内容做清空!重复写入的时候会很乱,可以自己尝试)
3.O_TRUNC——对文件内容做清空
4.O_APPEND——追加(没有写入,只是追加)
(追加一般不和清空一起使用,因为逻辑上就有矛盾)
5.O_RDONLY——只读
(读取是对一次已经存在的文件读取,所以使用两个参数的open,不用指定文件权限)

使用例:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LOG "log.txt"

int main()
{
    int fd = open(LOG, O_CREAT | O_WRONLY, 0666);
    if(fd == -1)
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    else
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    close(fd);
}

在这里插入图片描述
此时发现生成的文件权限不是指定的666,而是664,这是为什么?
——因为存在权限掩码umask,umask的计算:最终权限 = 起始权限 & (~umask),普通用户的umask为0002,所以生成的权限是664。

那么我们要如何不让权限掩码影响我们,直接生成指定权限的文件呢?
——调用umask函数,可以设定当前进程启动时,属于自己的umask。
当该进程生成文件的时候,umask用系统的还是该进程的?
——就近原则,离谁更近就用谁的,所以肯定是用当前进程的umask。

umake:设定进程的权限掩码
头文件
sys/types.h
sys/stat.h
函数
mode_t umask(mode_t mask);
参数:想要设定的权限掩码

//头文件略
int main()
{
    umask(0);
    int fd = open(LOG, O_CREAT | O_WRONLY, 0666);
    if(fd == -1)
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    else
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    close(fd);
    return 0;
}

此时发现生成的文件权限掩码与自己设定的一致。
在这里插入图片描述

写入——write

头文件:unistd.h
函数:ssize_t write(int fd, const void *buf, size_t count);
参数
fd——文件描述符(open的返回值)
buf——缓冲区
count——写入的字符数
将缓冲区中count大小的数据写入到fd中。
返回值:实际写了多少字节,失败返回-1;

读取——read

头文件:unistd.h
函数:ssize_t read(int fd, void *buf, size_t count);
参数
fd——文件描述符(open的返回值)
buf——缓冲区
count——空间的大小(字节数)
从文件描述符fd中,将数据按数据块的方式读取出来,读取到buf中,读取conut个字节
返回值:读取了多少字节,失败返回-1;

几个系统调用的使用

使用1:
O_WRONLY只写入

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LOG "log.txt"

int main()
{
    umask(0);
    int fd = open(LOG, O_CREAT | O_WRONLY | O_TRUNC, 0666);//打开/创建文件+只写+清空
    if(fd == -1)
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    else
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }

    const char *str = "hello world!";
    int cnt = 5;
    while(cnt)
    {
        char line[128];
        snprintf(line, sizeof(line), "%s, %d\n", str, cnt);
        write(fd, line, strlen(line));//strlen不计算\0,但是这里不+1,因为\0是C语言的规定,不是文件的规定!!
        cnt--;
    }
    close(fd);
    return 0;
}

结果1:
在这里插入图片描述


使用2:
O_WRONLY | O_APPEND追加写入(不带清空)

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LOG "log.txt"

int main()
{
    umask(0);
    int fd = open(LOG, O_CREAT | O_WRONLY | O_APPEND, 0666);//追加写入
    //int fd = open(LOG, O_CREAT | O_WRONLY | O_TRUNC, 0666);//打开/创建文件+只写+清空
    if(fd == -1)
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    else
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }

    const char *str = "hello world!";
    int cnt = 5;
    while(cnt)
    {
        char line[128];
        snprintf(line, sizeof(line), "%s, %d\n", str, cnt);
        write(fd, line, strlen(line));//strlen不计算\0,但是这里不+1,因为\0是C语言的规定,不是文件的规定!!
        cnt--;
    }
    close(fd);
    return 0;
}

结果2:
运行多次,可以发现是追加写入。
在这里插入图片描述


使用3:O_RDONLY读取
(这里读取的就是使用2中生成的文件)

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LOG "log.txt"

int main()
{
    umask(0);
    int fd = open(LOG, O_RDONLY);//只读
    if(fd == -1)
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }
    else
    {
        printf("fd: %d, errno: %d, errstring: %s\n", fd, errno, strerror(errno));
    }

    char buffer[1024];
	//sizeof会计算\0,IO操作一定要注意\0的问题!记得-1
    ssize_t n = read(fd, buffer, sizeof(buffer)-1);
    if(n > 0)
    {
        buffer[n] = '\0';
        printf("%s", buffer);
    }
    close(fd);
    return 0;
}

结果3:
读取成功(我们这里是一个一个读取的,不是和C接口一样按行读取,要实现需要精细化处理)
在这里插入图片描述

3.库函数与系统调用的关系

C语言的接口(库函数)一定调用了系统调用接口,是上下层关系。

在这里插入图片描述
程序员与系统调用接口的关系:
我们平时使用的C、C++库都是对系统调用的封装!

而函数库也是人写的,他们与我们同样不可能绕过OS来对硬件做操作,同时也无法直接操作操作系统,所以只能通过系统调用的方式来操作硬件设备。
在这里插入图片描述
同时其他所有语言都是一样的,都要调用这些系统调用接口,只是封装的形式不同!
在这里插入图片描述

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

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

相关文章

【游戏逆向】Lua游戏逆向及破解方法介绍

前言 随着手游的发展&#xff0c;越来越多的Cocos-lua端游开发者转移到手游平台。Lua脚本编写逻辑的手游也是越来越多&#xff0c;如梦幻西游、刀塔传奇、开心消消乐、游龙英雄、奇迹暖暖、疾风猎人、万万没想到等手游。随着Lua手游的增加&#xff0c;其安全性更值得关注&…

python基本数据类型---数字字符串

引入 在内存中存储的数据可以是不同的数据类型。比如名字可以使用字符串存储&#xff0c;年龄可以使用数字存储&#xff0c;python有6种基本数据类型&#xff0c;用于各种数据的存储&#xff0c;分别是&#xff1a;numbers(数字类型)、string(字符串)、List(列表)、Tuple(元组…

哈希表企业应用-DNA的字符串检测

DNA的字符串检测-引言 若干年后, ikun DNA 检测部成立,专门对 这些ikun的解析检测 突然发现已经完全控制不了 因为学生已经会了 而且是太会了 所以DNA采用 以下视频测试: ikun必进曲 ikun必经曲 ikun必阶曲 如何感受到了吧!,如果你现在唱跳并且还Rap 还有打篮球 还有铁山靠 那…

自动化运维工具Ansible之playbooks剧本

目录 一、playbooks 1、playbooks简述 2、playbooks剧本格式 3、playbooks组成部分 4、playbooks启动及检测 5、playbooks模块实战实例1 6、vars模块实战实例2 7、when模块实战实例3 8、with_items循环模块实战实例4 9、template模块实战实例5 10、tags模块实战实例…

有研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具(上)

导语&#xff1a;研究员公开了一个解析并提取 Dell PFS BIOS 固件的工具。 Dell PFS BIOS提取器 介绍 解析 Dell PFS BIOS 映像并提取其 SPI/BIOS/UEFI 固件组件。它支持所有Dell PFS 修订版和格式&#xff0c;包括最初在 ThinOS 包中LZMA压缩、ZLIB压缩或拆分成块的格式。输出…

Vue 注册组件介绍

Vue组件的基本概念 Vue组件是一种可复用的Vue实例&#xff0c;用于封装可重用的HTML元素、JavaScript代码和CSS样式。它可以让开发者更好地组织和复用代码&#xff0c;使Web应用程序更加可维护和可扩展 Vue组件通常由三部分组成&#xff1a;模板&#xff08;template&#xf…

NLP实战:快递单信息抽取-基于ERNIE1.0 预训练模型

目录 一、介绍 二、代码 2.1前期准备 2.2加载自定义数据集 2.3数据处理 2.4数据读入 2.5PaddleNLP一键加载预训练模型 2.6设置Fine-Tune优化策略&#xff0c;模型配置 2.7模型训练与评估 ​编辑 2.8模型预测 三、总结 原文&#xff1a; 一、介绍 命名实体识别&…

网易云音乐开发--主页静态页面搭建

如何用VScode来开发小程序 wxml和wxss来高亮小程序 窗口设置 轮播图制作 就是通过swiper来设置轮播图 iconfont字体图标使用 这里要借助阿里的iconfonticonfont-阿里巴巴矢量图标库 找到自己喜欢的图标&#xff0c;添加到购物车 添加到项目 这样就可以统一的管理图标的库 …

Windows环境安装Elasticsearch和Kibana

文章目录 1 Elasticsearch1.1 下载1.2 解压并添加环境变量1.3 访问1.4 cmd命令1.5 中文分词器1.5.1 下载1.5.2 安装1.5.2.1 命令安装1.5.2.2 手动安装1.5.2.3 验证分词 1.6 使用curl批量导入 2 安装 kibana2.1 下载kibana2.2 中文界面2.3 操作索引2.3.1 增加索引2.3.1.1 单条新…

电商--订单支付中存在的问题以及思考

文章目录 前言背景订单支付大致流程订单支付流程中的注意细节防止订单重复创建为何会出现重复创建订单处理措施 掉单导致的重复支付为何会出现这种场景处理措施 已支付流水退款为何会出现这种场景 前言 最近感觉应该把自己在工作中遇到的一些比较有意思的核心流程进行总结以此…

win10远程桌面控制Ubuntu服务器 - 内网穿透实现公网远程

文章目录 前言视频教程1. ubuntu安装XRDP2.局域网测试连接3. Ubuntu安装cpolar内网穿透4.cpolar公网地址测试访问5.固定域名公网地址 转载自远程穿透文章&#xff1a;Windows通过RDP异地远程桌面Ubuntu【内网穿透】 前言 XRDP是一种开源工具&#xff0c;它允许用户通过Windows…

软件测试之测试的分类(重点:黑盒测试、白盒测试、单元测试、集成测试、系统测试)

文章目录 1. 按照测试对象进行划分1&#xff09;界面测试2&#xff09;可靠性测试3&#xff09;容错性测试4&#xff09;文档测试5&#xff09;兼容性测试6&#xff09;易用性测试7&#xff09;软件安装卸载的测试8&#xff09;安全测试9&#xff09;性能测试10&#xff09;内存…

代码随想录算法训练营第五十天| 123.买卖股票的最佳时机III、188.买卖股票的最佳时机IV

文章目录 123.买卖股票的最佳时机III188.买卖股票的最佳时机IV:star: 123.买卖股票的最佳时机III 至多买卖两次 分清楚动态规划所有状态至关重要&#xff0c;这是求dp数组的前提 和之前买卖股票问题解题思路相似&#xff0c;只是多增加了第二天的状态 总结&#xff1a;买卖股票…

Docker代码环境打包

1. 介绍 Docker是一种开源的容器化平台&#xff0c;它可以在操作系统级别运行应用程序。通过将应用程序及其依赖项封装成一个可移植的容器&#xff0c;Docker使得应用程序可以在任何环境中轻松部署、运行和管理。使用Docker&#xff0c;开发人员可以避免在不同环境中出现的配置…

测试从业第 3 年,我看到了终点......

先说明&#xff0c;今天的内容&#xff0c;是写给想成为高级测试开发、自动化测试专家的人看的&#xff0c;因为&#xff0c;它可能颠覆你的认知。 众所周知&#xff0c;如今无论是大厂还是中小厂&#xff0c;自动化测试基本是标配了&#xff0c;毕竟像双11、618 这种活动中庞…

vue-element-admin入门

vue-element-ui的基本使用 vue-element-admin下载vue-element-admin对接后端接口mock接口信息编写后端接口对接测试移除mock替换接口 vue-element-admin下载 这里下载的是基础模板&#xff0c;要下载完整版的可以去官网下载 # clone the project git clone https://github.co…

leetcode刷题日志4.0

目录 前言&#xff1a; 1.三个数的最大乘积 2.错误的集合 3.机器人能否返回原点 4.最长连续递增序列 5.验证回文串 II 6.交替位二进制数 前言&#xff1a; 五一假期结束了&#xff0c;大家玩的开心吗&#xff1f;不过我们还得回到我们的日常生活学习工作当中&#xff0c;…

X86架构与Arm架构的主要区别分析

​ X86架构和ARM架构是主流的两种CPU架构&#xff0c;X86架构的CPU是PC服务器行业的老大&#xff0c;ARM架构的CPU则是移动端的老大。 X86架构和arm架构实际上就是CISC与RISC之间的区别&#xff0c;很多用户不理解它们两个之间到底有哪些区别&#xff0c;实际就是它们的领域不…

《算法训练营》语言基础(゚Д゚

&#x1f442; 无论你多怪异我还是会喜欢你&#xff08;《刺客伍六七》动画推广版片尾曲&#xff09; - 周子琰 - 单曲 - 网易云音乐 一起补基础&#xff01; φ(゜▽゜*)♪ &#x1f442; My Nams Suzie - Susie/Farfashah …

20230503-win10-U盘安装WIN10-22h2

20230503-win10-U盘安装WIN10-22h2 一、软件环境 zh-cn_windows_10_consumer_editions_version_22h2_updated_april_2023_x64_dvd_80cec13e.isofirpe 1.8.2标签&#xff1a;firpe win10 22h2分栏&#xff1a;WINDOWS 二、硬件环境 8G或以上的有PE功能的启动U盘一个台式机需…