linux 应用开发笔记---【I/O文件/基础篇 】

news2024/11/26 8:48:54

文章笔记来自于【速学Linux】手把手教你学嵌入式Linux C应用编程_哔哩哔哩_bilibili

一,什么是linux应用程序

1.运行在linux操作系统用户空间的程序

2.内核程序运行在内核空间,应用程序运行在用户空间

在终端执行的命令ls,ps。。。。。。都是运行在用户空间

3.内核空间和用户空间

二,应用编程与裸机编程、驱动编程有什么区别?

三,如何编写linux应用程序

1.系统调用  【linux操作系统向应用层提供的接口,system call ,是linux 应用层进入内核空间的入口】

2.库函数:标准c库函数  【对系统调用的封装】

3.不要局限于编程语言 

四,文件I/O基础

1.什么是文件I/O ?   【对文件的输入和输出操作,也就是对文件的读/写操作】

2.文件描述符

这相当于是每次打开一个文件,每个文件会分配一个文件描述符【分配一个没有被使用的最小非负整数作为文件描述符】,相当于这个文件的标签一下,所有执行 IO 操作的系统调用都是通过文件描述符来索引到对应的文件,一个进程最多可以打开 1024 个文件,当文件被关闭时,对应的文件描述符会被释放,释放之后也就成为了一个没有被使用的文件描述符了

3.打开/创建文件:open()函数

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname 字符串类型,用于标识需要打开或创建的文件,可以包含路径(绝对路径或相对路径)信息,譬如:"./src_file" (当前目录下的 src_file 文件)、 "/home/dengtao/hello.c" 等;如果 pathname 是一个符号链接,会对其进行解引用
flags 调用 open 函数时需要提供的标志,包括文件访问模式标志以及其它文件相关标志,这些标志使
用宏定义进行描述

mode 此参数用于指定新建文件的访问权限
Linux 中 已经定义好的宏,不同的宏定义表示不同的权限

 

4.写文件:write()函数

ssize_t write(int fd, const void *buf, size_t count);
fd 文件描述符。关于文件描述符,前面已经给大家进行了简单地讲解,这里不再重述!我们需要将进
行写操作的文件所对应的文件描述符传递给 write 函数。
buf 指定写入数据对应的缓冲区。
count 指定写入的字节数。
返回值: 如果成功将返回写入的字节数( 0 表示未写入任何字节),如果此数字小于 count 参数,这不
是错误,譬如磁盘空间已满,可能会发生这种情况;如果写入出错,则返回 -1
读文件和写文件存在一个读写位置【读写指针】,假设初始打开的文件才开始是在0的位置,当写入100字节,指针往后移动100字节,且 读写指针是共用指针,然后就会从100字节出开始读取
5.读文件: read()函数
ssize_t read(int fd, void *buf, size_t count);
fd 文件描述符。与 write 函数的 fd 参数意义相同。
buf 指定用于存储读取数据的缓冲区。
count 指定需要读取的字节数。
返回值: 如果读取成功将返回读取到的字节数,实际读取到的字节数可能会小于 count 参数指定的字节
数,也有可能会为 0 ,譬如进行读操作时,当前文件位置偏移量已经到了文件末尾。实际读取到的字节数少
于要求读取的字节数,譬如在到达文件末尾之前有 30 个字节数据,而要求读取 100 个字节,则 read 读取成
功只能返回 30;而下一次再调用 read 读,它将返回 0 (文件末尾)

6. 关闭文件: close()函数

int close(int fd);
fd 文件描述符,需要关闭的文件所对应的文件描述符。
返回值: 如果成功返回 0 ,如果失败则返回 -1

学习示例:

1.打开文件,写入内容

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
    int fd;
    int ret;
    fd = open("./test.txt",O_WRONLY | O_CREAT | O_EXCL,0644);
    if(-1 == fd){
        printf("open error");
        return 1;
    }
    printf("open ok\r\n");
    ret = write(fd, "Hello world",11);
    if(-1 == ret){
        printf("write error\r\n");
        close(fd);
        return 1;
    }
    printf("write %d Bytes OK",ret);

    close(fd);
    return 0;
}

 运行结果:

 2.读取文件内容

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


int main()
{
    int fd;
    char buf[125] = {0};
    int ret;
    fd = open("./test.txt",O_RDONLY);   
    if(-1 == fd){
        printf("open error");
    }
    printf("open ok");
    ret = read(fd, buf, 11);
    if(-1 == ret){
        printf("read error\r\n");
        close(fd);
        return 1;
    }
    printf("read %d bytes: %s",ret,buf);
    close(fd);
    return 0;
}

运行结果:

7.调整读写位置偏移:lseek()函数

off_t lseek(int fd, off_t offset, int whence);
fd 文件描述符。
offset 偏移量,以字节为单位。
whence 用于定义参数 offset 偏移量对应的参考值,该参数为下列其中一种(宏定义):
SEEK_SET :读写偏移量将指向 offset 字节位置处(从文件头部开始算
SEEK_CUR :读写偏移量将指向当前位置偏移量 + offset 字节位置处, offset 可以为正、也可以为
负,如果是正数表示往后偏移,如果是负数则表示往前偏移;
SEEK_END :读写偏移量将指向文件末尾 + offset 字节位置处,同样 offset 可以为正、也可以为负,
如果是正数表示往后偏移、如果是负数则表示往前偏移。

 五,深入探究文件I/O

1.linux系统如何进行文件管理?

文件储存在硬盘上,硬盘的最小存储单位叫做“扇区”(Sector),每个扇区储存 512 字节(相当于 0.5KB),操作系统读取硬盘的时候,以“块”为单位进行文件读取。【“块”的大小,最常
见的是 4KB,即连续八个 sector 组成一个 block

静态文件:文件存放在磁盘内叫做静态文件

inode: 指向文件存放在磁盘的位置

pcb: 进程控制块,管理该进程,譬如用于记录进程的状态信息、运行特征,

 2.错误处理

1.errno

2.strerror函数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void)
{
    int fd1;
    fd1 = open("./test111.c",O_RDONLY);
    if(-1 == fd1)
    {
        printf("%s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

 运行结果:

3.perror:可以加入自己的打印信息

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void)
{
    int fd1;
    fd1 = open("./test111.c",O_RDONLY);
    if(-1 == fd1)
    {
        perror("open error");
        return -1;
    }
    
    return 0;
}

运行结果

 3.空洞文件

概念:当一个文件本身只有4k的大小,但是当前的通过lseek从文件头部偏移6000个字节,开始写入内容,导致4096-6000字节间没有内容,出现了“空洞”,也就是空洞文件

使用场景:一个文件可以从不同的地址开始写入,不再是单线程从头开始写入,多个线程同时写入,利用了多线程的优势

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
static unsigned char buf[4096];
int main(void)
{
    int fd;
    int ret;
    fd = open("./test.txt",O_WRONLY | O_CREAT | O_EXCL,0644);
    if(-1 == fd)
    {
        perror("open error");
        return 1;        
    }
    ret = lseek(fd, 4096, SEEK_SET);
    if(-1 == ret)
    {
        perror("lseek error");
        close(fd);
        return 1;
    }

    ret = write(fd, buf, 4096);
    if(-1 == ret)
    {
        printf("write error");
        close(fd);
        return 1;
    }
    close(fd);
    return 0;


}

运行结果:

ls 命令:查看到的大小是文件的逻辑大小   
du 命令:查看到的大小是文件实际占用存储块的大小

4.O_TRUINC和O_APPEND

O_TRUINC将文件原本的内容全部丢弃,文件大小变为 0
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void)
{
    int fd;
    fd = open("./test.txt",O_WRONLY | O_TRUNC);
    if(-1 == fd)
    {
        perror("open error");
        close(fd);
        return 1;        
    }    
    close(fd);
    return 0;
}

运行结果:

O_APPEND当每次使用 write()函数对文件进行写操作时,都会自动把文件当前位置偏移量移动到文件末尾
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void)
{
    int fd;
    int ret;
    fd = open("./test.txt",O_WRONLY | O_APPEND);
    if(-1 == fd)
    {
        perror("open error");
        close(fd);
        return 1;        
    }    
    ret = write(fd,"hello world",11);
    if(-1 == ret)
    {
        perror("write error");
        close(fd);
        return 1;
    }
    
    close(fd);
    return 0;
}

运行结果:

open可以多次打开一个文件,产生多个文件描述符,也就是存在多个文件表,,但是都指向同一个文件,但是只能产生一个动态文件,以及同一个inode。对于打开的每一个fd.都要依次关闭。

5.文件描述符的复制

int dup(int oldfd);                           复制文件描述符

int dup2(int oldfd, int newfd);        文件描述符的数可以自行决定

新的文件描述符和旧的指向同一个文件表,去完成任何操作

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int fd1, fd2;
    int ret;
    fd1 = open("./test_file", O_RDWR | O_CREAT | O_EXCL,
    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (-1 == fd1) {
        perror("open error");
        exit(-1);
    }
    /* 复制文件描述符 */
    fd2 = dup2(fd1, 100);
    if (-1 == fd2) {
        perror("dup error");
        ret = -1;
        goto err1;
    }
    printf("fd1: %d\nfd2: %d\n", fd1, fd2);
    ret = 0;
    close(fd2);
err1:
    /* 关闭文件 */
    close(fd1);
    exit(ret);
}

运行结果

6.文件共享

概念:同一个文件(譬如磁盘上的同一个文件,对应同一个 inode)被多个独立的读写体同时进行 IO 操作

常见的三种文件共享的实现方式

(1)同一个进程中多次调用open函数打开同一个文件,多个文件描述符指向不同的文件表,但是多个文件表中的inode指针指向inode节点

(2)不同的进程分别打开同一个文件

(3)同一个进程中通过dup(dup2)函数对文件描述符进行复制 

 7.原子操作与竞争冒险

竞争冒险:进程获得cpu的使用权完全是由操作系统决定的,先后顺序是不可预期的

原子操作:多个步骤组成的一个操作,要么不执行,要么必须执行完所有操作,不可以只执行所有步骤的一个子集

(1)O_APPEND 实现原子操作
移动到文件末尾,然后读写文件
(2)pread() 和 pwrite()
移动到偏移位置,然后读写文件

(3) 创建一个文件O_EXCL
先判断是否有文件 然后创建文件

 8.截断文件

ftruncate():使用文件描述符 fd 来指定目标文件大小,目标文件必须具有可写权限

truncate(): 使用 文件路径path 来指定目标文件大小
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{  
    int fd;
    int ret;
    fd = open("./test.txt", O_RDONLY | O_WRONLY);
    if(-1 == fd)
    {
        perror("open error");
        return 1;        
    }
    ret = ftruncate(fd, 4096);
    if(ret == -1)
    {
        perror("sss");
        return 1;
    }
    close(fd);
    return 0;
}

运行结果:

9.fcntl和ioctl函数

fcntl()函数():对文件描述符做一系列的控制操作

int fcntl(int fd, int cmd, ... /* arg */ )
cmd 命令集:
复制文件描述符( cmd=F_DUPFD cmd=F_DUPFD_CLOEXEC)
获取 / 设置文件描述符标志( cmd=F_GETFD cmd=F_SETFD
获取 / 设置文件状态标志( cmd=F_GETFL cmd=F_SETFL
获取 / 设置异步 IO 所有权( cmd=F_GETOWN cmd=F_SETOWN
获取 / 设置记录锁( cmd=F_GETLK cmd=F_SETLK

ioctl()函数:是一个文件 IO 操作的杂物箱,可以处理的事情非常杂、不统一

int ioctl(int fd, unsigned long request, ...);

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

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

相关文章

计算机视觉的应用20-图像生成模型(Stable Diffusion)的原理详解与相关项目介绍

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用20-图像生成模型&#xff1a;Stable Diffusion模型的原理详解与相关项目介绍。大家知道现在各个平台发的各种漂亮的女生&#xff0c;这些漂亮的图片是怎么生成的吗&#xff0c;其实它们底层原理就是…

Windows关闭端口服务命令

winR 打开命令运行 cmd 命令netstat -o -n -a | findstr :9993 显示所有的端口占用情况 -a 显示所有连接和监听端口 -n 以数字形式显示地址和端口号。 此选项一般与 -a选项组合使用 -o 显示与每个连接相关的所属进程 ID 终止 PID taskkill /F /PID 3652

C51--4G模块

EC03-DNC&#xff1a;4G通信模块 EC03-DNC 功能特点&#xff1a; 采用最新4G CAT1方案&#xff1b; 支持数据透明传输; 支持TCP、UDP 网络协议; 支持心跳包、注册包功能最大支持64个字节数&#xff1b; 支持MQTT协议&#xff0c;支持接入OneNet平台、百度云平台、阿里云平台的…

freeRTOS异常处理函数分析(以RISC-V架构进行分析)

1、异常处理函数的注册 对RISC-V架构中断不熟悉&#xff0c;可参考博客&#xff1a;《RISC-V架构——中断处理和中断控制器介绍》&#xff1b; 2、异常处理函数分析 2.1、数调用关系 freertos_risc_v_trap_handler //异常处理函数入口portcontextSAVE_CONTEXT_INTERNAL //保存…

字符串函数精讲1

又是好几天没有更新了&#xff0c;最近有些忙&#xff0c;但这并不是理由&#xff0c;还是怪我自己玩的时间多了&#xff01;但还是有在每天敲代码的&#xff01;话不多说&#xff0c;开始这一期的学习&#xff1a; strlen的使用和模拟实现 • 字符串以 \0 作为结束标志&#…

Python实现学生信息管理系统(详解版)

Python实现学生信息管理系统-详解版 个人简介实验名称&#xff1a;学生信息管理系统系统功能实验步骤详讲添加入住学生信息删除学生的住宿信息修改学生的住宿信息查询学生的住宿信息显示所有学生住宿信息显示所有请假学生的信息 运行截图展示1.主界面2.添加新的入住学生信息3.显…

Java LeetCode篇-深入了解关于单链表的经典解法

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 移除链表元素 1.1 使用双指针方法 2.0 反转链表 2.1 递归法 2.2 头插法 3.0 链表中倒数第 k 个节点 3.1 递归法 3.2 快慢指针 4.0 合并两个有序链表 4.1 递归法 …

java科学计数法表示数值

Background 大多数计算器及计算机程序用科学记数法显示非常大和非常小的结果&#xff1b;但很多时候&#xff0c;我们需要做一个统一&#xff0c;要么全部以科学计数法输出&#xff0c;要么就全部显示为普通计数。注意&#xff1a;这里对大于等于1的数据做了特殊处理&#xff0…

RocketMQ源码剖析之createUniqID方法

目录 版本信息&#xff1a; 写在前面&#xff1a; 源码剖析&#xff1a; 总计&#xff1a; 版本信息&#xff1a; RocketMQ-5.1.3 源码地址&#xff1a;https://github.com/apache/rocketmq 写在前面&#xff1a; 首先&#xff0c;笔者先吐槽一下RocketMQ的官方&#xff0…

用代码评论代替代码注释

在一个软件项目中&#xff0c;某些逻辑部分可能非常复杂&#xff0c;容易让人困惑。为了确保其他开发人员能够理解这些代码&#xff0c;同时也为了自己回顾时能够快速上手&#xff0c;我们通常会编写相关文档或添加大量注释来对这些复杂的逻辑进行解释。这样做的好处是能够提高…

1.自动化运维工具Ansible的安装

1.物料准备 四台服务器&#xff0c;其中一个是主控机&#xff0c;三个为host 2.安装 在主控机上安装ansible 2.1 设置EPEL仓库 Ansible仓库默认不在yum仓库中&#xff0c;因此我们需要使用下面的命令启用epel仓库。 yum install epel-release -y2.2 执行安装命令 yum i…

香港人均gdp世界排名,和内地相比怎么样?

香港人均gdp世界排名&#xff0c;和内地相比怎么样&#xff1f; 香港作为世界贸易港口&#xff0c;也是中国最发达的城市之一。其经济相比于北上广深而言&#xff0c;都要发达。香港人均收入世界排名第18&#xff0c;人均收入为4.2万美元&#xff0c;在世界各国人均收入排名中处…

八个优秀开源内网穿透工具

内网穿透&#xff08;NAT穿透&#xff09;是一种将本地网络服务暴露给互联网的一种技术。这种技术可以很好地解决许多局域网内的资源共享。采用路由的方式将一台计算机变成一个“路由器”&#xff0c;将公共的网络地址转为内部网络地址&#xff0c;从而实现通过英特网可以访问局…

dbeaver连接amabri-hbase

目录 尝试过程 解决之道 总结 尝试过程 注意此章节为记录试错过程&#xff0c;无需跟随操作&#xff0c;仅作试错记录。真正操作方法请看“解决之道”章节 环境ambari安装的hbase2.1.6 使用apche phoenix默认驱动配置 备注&#xff1a;Apache Phoenix 是一个开源的、基于…

软件设计师——法律法规(一)

&#x1f4d1;前言 本文主要是【法律法规】——软件设计师法律法规的题目&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日…

7 种 JVM 垃圾收集器详解

一、概述 如果说收集算法是内存回收的方法论&#xff0c;那么垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定&#xff0c;因此不同的厂商、版本的虚拟机所提供的垃圾收集器都可能会有很大差别&#xff0c;并且一般都会提供参数供用…

国内外四款SAST工具约登指数

开放Web应用程序安全项目&#xff08;OWASP&#xff09;&#xff0c;是一个非营利性基金会&#xff0c;致力于提高软件的安全性。OWASP Benchmark是一个免费和开放的测试套件。主要是通过Java语言基准测试案例对SAST工具进行评价。通过Yonden Index(约登指数)进行计算。约登指数…

量子力学应用:探索科技前沿的奇幻之旅

量子力学应用:探索科技前沿的奇幻之旅 引言 量子力学,这门探讨微观世界规律的学科,自其诞生以来就充满了神秘与奇幻。随着科学技术的不断进步,量子力学已经从纯理论研究走向了实际应用领域,为我们打开了一个全新的科技世界。在本文中,我们将深入探讨量子力学的应用方面,…

从 Elasticsearch 到 SelectDB,观测云实现日志存储与分析的 10 倍性价比提升

作者&#xff1a;观测云 CEO 蒋烁淼 & 飞轮科技技术团队 在云计算逐渐成熟的当下&#xff0c;越来越多的企业开始将业务迁移到云端&#xff0c;传统的监控和故障排查方法已经无法满足企业的需求。在可观测理念逐渐深入人心的当下&#xff0c;人们越来越意识到通过多层次、…

深入了解HMAC加密技术:原理、应用与实践

一、引言 在网络安全领域&#xff0c;消息认证码&#xff08;MAC&#xff09;是一种重要的技术手段。Hash-based Message Authentication Code&#xff08;HMAC&#xff09;作为其中的一种&#xff0c;凭借其简单、高效、安全的特性&#xff0c;广泛应用于各种网络通信场景。本…