首页 > 其他分享 >树莓派5B -零基础应用开发系列(第二期)

树莓派5B -零基础应用开发系列(第二期)

时间:2024-09-25 19:49:50浏览次数:12  
标签:文件 树莓 第二期 buffer ret fd include open 5B

树莓派5B - 零基础应用开发(第二期)

基础知识篇(适用于零基础想学习Linux操作系统的的小白新手)

核心思想

Linux 系统下,一切皆文件,也包括各种硬件设备(一定要有这个概念)。

系统调用

系统调用(system call)其实是 Linux 内核提供给应用层的应用编程接口(API),是 Linux 应用层进入内核的入口。

字符设备驱动

字符设备驱动是 Linux 内核中一种常见的设备驱动类型,主要用于与字符设备交互,如串口、键盘等。这类驱动程序通过实现特定的文件操作(如读取、写入、打开、关闭等)与用户空间程序交互。
字符设备按字节流方式进行数据传输。
字节流(Byte Stream)是一种连续的数据传输方式,数据以字节为单位顺序传输。字节流的本质是按字节传递数据,而不是按块 或其他更大单位, 这意味着接收方按顺序获取每个字节,并且可 以立即处理,不需要等待全 部数据传输完毕。

库函数

库函数也就是 C 语言库函数,C 语言库是应用层使用的一套函数库,在 Linux 下,通常以动态(.so) 库文件的形式提供,存放在根文件系统/lib 目录下,C 语言库函数构建于系统调用之上,也就是说库函数其大部分都是由系统调用封装而来的,但是也有一些库函数不调用任何的系统调用,例如:字符串处理函数 strlen()、strcat()、memcpy()、memset()、strchr()。

.so 文件代表共享库(shared object),即动态链接库。.so 文件可以包含多 种后缀,常见的是 .so 和 .so.数字。这两种后缀有不同的含义,通常涉及库 的版本控制和符号链接

.so 文件:.so 文件是共享库文件的基础形式,代表 “shared object”。它通常 是开发过程中和动态链接时使用的版本,不带具体的版本号。

.so.数字 文件:带有版本号的 .so.数字 文件表示具体版本的共享库。数字后 缀通常用于指代共享库的 API 或 ABI 版本。应用程序在运行时会链接到特 定版本的库,以确保库接口的兼容性。

符号链接与版本控制:在大多数情况下,系统会创建符号链接(symlink), 使没有版本号的 .so 文件指向某个特定版本的库文件。
libc.so -> libc.so.6
libpthread.so -> libpthread.so.0
① 库函数是属于应用层,而系统调用是内核提供给应用层的编程接口,属 于系统内核的一部分
② 库函数运行在用户空间,调用系统调用会由用户空间(用户态)陷入到 内核空间(内核态)
③ 库函数通常是有缓存的,而系统调用是无缓存的,所以在性能、效率上, 库函数通常要优于系统调 用;
④ 可移植性:库函数相比于系统调用具有更好的可移植性

文件描述符

例如调用 open 函数会有一个返回值,在 open 函数执行成功的情况下,会返回一个非负整数,该返回值就是一个文件描述符(file descriptor)文件描述符是一个非负整数;对于 Linux 内核而言,所有打开的文件都会通过文件描述符进行索引。
当调用 open 函数打开一个现有文件或创建一个新文件时,内核会向进程返 回一个文件描述符,用于指代被打开的文件,所有执行 IO 操作的系统调用 都是通过文件描述符来索引到对应的文件。

当调用 read/write 函数进行文件读写时,会将文件描述符传送给 read/write 函数。

在 Linux 系统下,我们可以通过 ulimit 命令来查看进程可打开的最大文件 数 ulimit -n 。该最大值默认情况下是 1024。每一个被 打开的文件在同一 个进程中都有 一个唯一的文件描述符,不会重复,如果 文件被关闭后, 它对应的文件描述 符将会被释放,那么这个文件描述符将 可以再次分配 给其它打开的文件、与对应的文件绑定起来。

每次给打开的文件分配文件描述符都是从最小的没有被使用的文件描述符 (0~1023)开始,当之前打开的文件被关闭之后,那么它对应的文件描述符 会被释放,释放之后也就成为了一个没有被使用的文件描述符了。

0、1、2 这三个文件描述符已经默认被系统占用 了,分别分配给了系统标准输入(0)、标准输出(1)以及标准错误(2)

Linux 系统下,一切皆文件,也包括各种硬件设备

open

打开文件(举着一个例子,后面的read,write,close不详细说明):在 Linux 系统中要操作一个文件,需要先打开该文件,得到文件描述符,然后再对文件进行相应的读写操作(或其他操作),最后在关闭该文件;open 函数用于打开文件,当然除 了打开已经存在的文件之外,还可以创建一个新的文件。

man 手册

man 命令后面跟着两个参数,数字 2 表示系统调用,man 命令除了可以查看系统调用的帮助信息外,还可以查看 Linux 命令(对应数1)以及标准 C 库函数(对应数字 3)所对应的帮助信息;最后一个 参数 open 表示需要查看的系统调用函数名

lseek

每个打开的文件,系统都会记录它的读写位置偏移量,我们也把这个读写位 置偏移量称为读写偏移 量,记录了文件当前的读写位置,当调用 read()或 write()函数对文件进行读写操作时,就会从当前读写位置偏移量开始进行数据读写。

练习

1.打开一个已经存在的文件(例如 yx_test_file.c),使用只读方式;然后打开一个新建文件(例如 yx_test_file_new.c),使用只写方式,新建文件的权限设置如下: 文件所属者拥有读、写、执行权限; 同组用户与其他用户只有读权限。 从 src_file 文件偏移头部 500 个字节位置开始读取 1Kbyte 字节数据,然后将读取出来的数据写入到 dest_file 文件中,从文件开头处开始写入,1Kbyte 字节大小,操作完成之后使用 close 显式关闭所有文件, 然后退出程序。

在这里插入图片描述
2.通过 open 函数判断文件是否存在(例如 0),并将判断结果显示出来。
在这里插入图片描述
3.新建一个文件(例如 new_file),新建文件的权限设置为: 文件所属者拥有读、写、执行权限;同组用户与其他用户只有读权限。 使用只写方式打开文件,将文件前 1Kbyte 字节数据填充为 0x00,将下 1Kbyte 字节数据填充为 0xFF, 操作完成之后显式关闭文件,退出程序。
在这里插入图片描述
4.打开一个已经存在的文件(例如 test_file),通过 lseek 函数计算该文件的大小,并打印出来
在这里插入图片描述

练习源码部分(完整源码)

练习1

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
    int fd;
    int ret;
    char buffer[1024];
    int i;
    /* 打开文件 */
    fd = open("./yx_test_file_hole.c", O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (-1 == fd)
    {
        perror("open error");
        exit(-1);
    }
    /* 将文件读写位置移动到偏移文件头 4096 个字节(4K)处 */
    ret = lseek(fd, 4096, SEEK_SET);
    if (-1 == ret)
    {
        perror("lseek error");
        goto err;
    }
    /* 初始化 buffer 为 0xFF */
    memset(buffer, 0xFF, sizeof(buffer));
    /* 循环写入 4 次,每次写入 1K */
    for (i = 0; i < 4; i++)
    {
        ret = write(fd, buffer, sizeof(buffer));
        if (-1 == ret)
        {
            perror("write error");
            goto err;
        }
    }

    ret = 0;
    printf("success to it\n");
err:
    /* 关闭文件 */
    close(fd);
    exit(ret);
}

练习2

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
    int fd;
    int ret;
    char buffer[1024];
    int i;
    /* 打开文件 */
    fd = open("./yx_test_file_hole.c", O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (-1 == fd)
    {
        perror("open error");
        exit(-1);
    }

    printf("success to it\n");
    /* 关闭文件 */
    close(fd);
    exit(ret);
}

练习3

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

int main(void)
{
    char buffer[16];
    int fd;
    int ret;
    /* 打开文件 */
    fd = open("./yx_test_file.c", O_RDWR | O_APPEND);
    if (-1 == fd)
    {
        perror("open error");
        exit(-1);
    }
    /* 初始化 buffer 中的数据 */
    memset(buffer, 0x55, sizeof(buffer));
    /* 写入数据: 写入 4 个字节数据 */
    ret = write(fd, buffer, 4);
    if (-1 == ret)
    {
        perror("write error");
        goto err;
    }
    /* 将 buffer 缓冲区中的数据全部清 0 */
    memset(buffer, 0x00, sizeof(buffer));
    /* 将位置偏移量移动到距离文件末尾 4 个字节处 */
    ret = lseek(fd, -4, SEEK_END);
    if (-1 == ret)
    {
        perror("lseek error");
        goto err;
    }
    /* 读取数据 */
    ret = read(fd, buffer, 4);
    if (-1 == ret)
    {
        perror("read error");
        goto err;
    }
    printf("0x%x 0x%x 0x%x 0x%x\n", buffer[0], buffer[1],
           buffer[2], buffer[3]);
    ret = 0;
err:
    /* 关闭文件 */
    close(fd);
    exit(ret);
}

练习4

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    unsigned char buffer1[4], buffer2[4];
    int fd1, fd2;
    int ret;
    int i;
    /* 创建新文件 test_file 并打开 */
    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);
    }
    /* 再次打开 test_file 文件 */
    fd2 = open("./test_file", O_RDWR);
    if (-1 == fd2)
    {
        perror("open error");
        ret = -1;
        goto err1;
    }
    /* buffer 数据初始化 */
    buffer1[0] = 0x11;
    buffer1[1] = 0x22;
    buffer1[2] = 0x33;
    buffer1[3] = 0x44;
    buffer2[0] = 0xAA;
    buffer2[1] = 0xBB;
    buffer2[2] = 0xCC;
    buffer2[3] = 0xDD;
    /* 循环写入数据 */
    for (i = 0; i < 4; i++)
    {
        ret = write(fd1, buffer1, sizeof(buffer1));
        if (-1 == ret)
        {
            perror("write error");
            goto err2;
        }
        ret = write(fd1, buffer2, sizeof(buffer2));
        if (-1 == ret)
        {
            perror("write error");
            goto err2;
        }
    }
    /* 将读写位置偏移量移动到文件头 */
    ret = lseek(fd1, 0, SEEK_SET);
    if (-1 == ret)
    {
        perror("lseek error");
        goto err2;
    }
    /* 读取数据 */
    for (i = 0; i < 8; i++)
    {
        ret = read(fd1, buffer1, sizeof(buffer1));
        if (-1 == ret)
        {
            perror("read error");
            goto err2;
        }
        printf("%x%x%x%x", buffer1[0], buffer1[1],
               buffer1[2], buffer1[3]);
    }
    for (i = 0; i < 8; i++)
    {
        ret = read(fd1, buffer2, sizeof(buffer2));
        if (-1 == ret)
        {
            perror("read error");
            goto err2;
        }
        printf("%x%x%x%x", buffer2[0], buffer2[1],
               buffer2[2], buffer2[3]);
    }
    printf("\n");
    ret = 0;
err2:
    close(fd2);
err1:
    /* 关闭文件 */
    close(fd1);
    exit(ret);
}

如果大家有什么其他好想法,欢迎在讨论区讨论!!!
此系列会持续更新,大家一起加油呀,一起进步呀!!!
谢谢大家的支持!!!点个小赞赞吧!!!

标签:文件,树莓,第二期,buffer,ret,fd,include,open,5B
From: https://blog.csdn.net/2401_87627523/article/details/142531090

相关文章

  • 树莓派5B -零基础应用开发系列
    树莓派5B-零基础应用开发(第一期)一、树莓派的开始1.准备工作①建议在树莓派官网下载相对应的镜像文件,树莓派镜像下载,网站如下所示:https://www.raspberrypi.org/downloads/raspberry-pi-os/②把SD卡格式化,使用SDFormatter进行开始格式化操作。③把在官网下载......
  • 通过jlink连接树莓派4b搭建gdb调试环境
    参考资料jlink使用的是博光微的jlinkv9树莓派使用的是4bjlink与树莓派链接按照表中的内容进行链接,注意链接过程要小心仔细,否则后续排查起来十分麻烦下载openocd由于我是在linux环境下,所以省去了jlink的驱动安装,直接先下载openocdsudoaptinstallopenocd注意不要去......
  • 干货满满!第二期流程挖掘实践训练营圆满落幕~
    2023年7月14日,望繁信科技第二期流程挖掘实践训练营在上海圆满结束!此次训练营以实战为导向,为大家提供了一个深入学习流程挖掘知识和技巧的专业交流平台,得到了广大学员们的一致好评。产品大咖面对面作为本次训练营的亮点环节,望繁信科技产品负责人曹企闻从“如何开启流程挖掘”这个关......
  • 以腾讯云为公网跳板实现树莓派内网穿透
    关键词:内网穿透,frp,ssh,腾讯云,树莓派情况说明不是所有的情况你的ip都可以固定,之前在室外搭的服务器,买的4G卡配路由,但是公网ip无法固定,一般情况下访问只能通过teamviewer或者向日葵等远程登录软件。部署流程确保自己需要内网穿透的设备网络通畅找到一个稳定具有固定公网ip的服务器(VPS......
  • 【春秋招必看】Unity相关笔试面试题(内有完整答案)第二期
    欢迎来到光光的奇妙冒险,我是你们的煎饼光子老师。今天是我们的第二期笔试面试题总结。C#部分:1、请说说你认为C#中==和Equals的区别是什么?答案:(1).==是运算符,Equals是万物之父Object中的虚方法,子类可重写(2).Equals一般在子类中重写后用于比较两个对象中内容是否相同......
  • 科普向-计算机如何生成随机数?(第二期)
    一、引言在上期中,我们介绍了LCG和逆变换法,了解了区间上的均匀分布,多点分布以及一些简单分布函数的生成。本期我们将把情况推向更为一般的情况,讲介绍正态分布的生成,以及舍选法生成一般概率分布函数的分布。二、正态分布对于正态分布  的概率密度和分布函数:直接计算上述......
  • Jina AI 发布 Reader-LM-0.5B 和 Reader-LM-1.5B:为网络数据处理提供多语种、长语境和
    JinaAI发布的Reader-LM-0.5B和Reader-LM-1.5B标志着小语言模型(SLM)技术的一个重要里程碑。这些模型旨在解决一个独特而具体的挑战:将开放网络中原始、嘈杂的HTML转换为干净的标记符格式。这项任务看似简单,却面临着复杂的挑战,尤其是在处理现代网络内容中的大量噪音......
  • 树莓派智能语音助手实现音乐播放
    树莓派语音助手从诞生的第一天开始,我就想着让它能像小爱音箱一样,可以语音控制播放音乐。经过这些日子的倒腾,今天终于实现了。接下里,和大家分享下我的实现方法:首先音乐播放模块用的是我在上一篇博文写的《用sounddevice实现连续的音乐曲库播放》,然后语义解析部分依旧用的是RAS......
  • 树莓派操作系统-利用Makefile创建内核镜像文件kernel8.img
    编写树莓派内核映像的过程可以等同于gcc的编译过程:预处理、编译、汇编、链接,后面还会加一步:将可执行文件转换成二进制的镜像文件。在MakeFile里的构建过程分为3步:1.将.c文件经过预处理、编译、汇编生成.o文件,将.S文件经过汇编生成.o文件。2.将.o文件经过链接生成.elf可执行文件......
  • 【高中数学/基本不等式】已知ab皆为正实数,且(a+5b)*(2a+b)=36,求a+2b之最小值?
    【问题】已知ab皆为正实数,且(a+5b)*(2a+b)=36,求a+2b之最小值?【出处】《高考数学极致解题大招》P12中原教研工作室著【解答】因为(a+5b)+(2a+b)=3a+6b=3*(a+2b)故a+2b=1/3*(3a+6b)=1/3*((a+5b)+(2a+b))>=1/3*2*根号下((a+5b)*(2a+b))=2/3*6=4故a+2b之最小值=4END......