华清远见嵌入式培训_第六周回顾与反思

本周的课程深入探讨了Linux的IO,包括标准IO、文件IO及其缓冲区机制,以及库和多进程的概念。重点介绍了fopen/fclose、fread/fwrite等IO函数,强调了系统调用与库函数的区别,同时讲解了文件属性操作如stat函数。在多进程部分,阐述了进程的生命周期、PID、状态转换以及进程创建的原理与API。通过课后作业巩固了所学知识。
摘要由CSDN通过智能技术生成

目录

前言

周二

IO简介和标准IO

一、IO简介

1.1 什么是IO

1.2 IO的分类

1.3 什么是系统调用和和库函数

1.4 什么是IO接口

二、标准IO

2.1 fopen/fclose函数

2.1 strerror函数

2.3 perror函数

2.4 fputc/fgetc函数

2.5 fgets/fputs函数

三、缓冲区问题

3.1 缓冲区的大小及类型

3.2 行缓存的刷新机制

3.3 全缓存的刷新机制

四、课后作业

周三

标准IO及文件IO

一、标准IO

1.1 fread/fwrite函数

1.2 sprintf/snprintf/fprintf函数

1.3 获取系统函数

1.4 fseek/ftell/rewind光标操作函数

1.5使用标准IO对bmp格式图片打马赛克

二、文件IO

2.1 open/close函数

周四

文件IO、文件属性操作和目录操作

一、文件IO

1.1read/write函数

二、获取文件属性的函数

2.1 stat函数

2.2 getpwuid/getgrid函数

三、目录操作

3.1 打开目录opendir

3.2 读目录readdir

3.3关闭目录closedir

3.4读取任意文件夹下的所有文件实例

课后作业

周五

库、多进程

一、Linux中的库

1.1 什么是库文件

1.2 库文件的分类

1.3 静态库

1.4动态库

二、多进程

2.1 什么是多进程

2.2 进程和程序的区别

2.3 进程的组成

2.4 什么是PID?

2.5 特殊PID的进程

2.6 进程的种类

2.7 进程相关的命令

2.8 进程的状态

2.9 特殊状态的进程

2.10 进程的创建

课后作业


前言

        从这一周开始学习的课程是IO进程,为时八天;本周的周一是数据结构的考试,考试的总结写在了上一周的末尾;从周二到周五这四天的时间里,主要学习了:文件IO、标准IO、库和多进程。

        老师说从IO进程开始,课程就从基础阶段拔高到应用层面了,经过这四天的学习下来,主要感觉就是:内容多、代码量大、要熟悉和记忆的内容多、易混淆的知识点多。比较重要的内容还是标准IO和文件IO里的海量的函数接口,因此,本次的反思将针对这几方面,进行整理归纳,难点分析和易混淆点辨析。

        同样,写此文章,是想以这种碎碎念的方式回顾重点、重复盲点、加深印象,复习、总结和反思本周的学习,仅供后期自己回顾使用。知识体系不完善,内容也不详细,仅供笔者复习使用。如果是有需要笔记,或者对这方面感兴趣,可以私信我,发你完整的知识体系和详细内容的笔记。如有任何错误请多指正,也欢迎友好交流,定会虚心听取大佬的宝贵意见!

周二

IO简介和标准IO

一、IO简介

1.1 什么是IO

        IO(input&output)是用户空间和内核空间通过API进行的交互。

1.2 IO的分类

        IO分为:标准IO和文件IO

        标准IO:库函数

        文件IO:系统调用

1.3 什么是系统调用和和库函数

        系统调用:系统调用是用户空间进入内核空间的一次过程。

                优点:操作系统不同,系统调用的函数接口也不同,因此系统调用的移植性比较差。

                缺点:系统调用没有缓冲区,所以效率比较低。

        库函数:库函数 = 系统调用 + 缓冲区

                优点:库函数要比系统调用的效率高。

                缺点:库函数的接口比较统一,移植性比较强。

        区别辨析:系统调用对内核的访问是即时的,库函数的缓冲区需要刷新才能访问内核。

1.4 什么是IO接口

        IO接口就是函数调用,系统已经封装好,使用时直接调用。

        例如:

                标准IO:fopen fread fwrite fputc fgetc fputs fgets fclose printf scanf...

                文件IO:open read write close...

二、标准IO

        标准IO:库函数 = 系统调用 + 缓冲区

        标准IO:fopen fread fwrite fputc fgetc fputs fgets fclose printf scanf...

        FILE结构体:

                FILE本身是一个结构体,在调用fopen的时候产生一个结构体指针,这个FILE结构体,就代表打开文件的所有的信息(例如缓冲区的大小,光标的位置等信息),并且以后在读写文件的时候通过FILE指针完成。

                在一个正在执行的程序中,默认已经有了三个FILE指针:stdin、stdout、stderr

                它们分别代表的是标准输入,标准输出,标准出错。

typedef struct _IO_FILE FILE;

struct _IO_FILE {
  char* _IO_buf_base; //缓冲区的起始地址
  char* _IO_buf_end; //缓冲区的结束地址
  ...
}

2.1 fopen/fclose函数

#include <stdio.h>

FILE *fopen(const char *pathname, const char *mode);
功能:使用标准IO接口打开文件
参数:
    @pathname:想要打开文件的路径及名字  "/home/linux/1.c"
    @mode    :打开文件的方式  "r" "r+" "w" "w+" "a" "a+"
       r     :以只读的方式打开文件,将光标定位到文件的开头
       r+    :以读写的方式打开文件,将光标定位到文件的开头
       w     :以只写的方式打开文件,如果文件存在就清空文件,如果文件不存在就创建文件
       w+    :以读写的方式打开文件,如果文件存在就清空文件,如果文件不存在就创建文件
       a     :以追加的方式打开文件,如果文件不存在就创建文件,如果文件存在光标定位到结尾进行写
       a+    :以读和追加的方式打开文件,如果文件不存在就创建文件,如果进行读从开头读,如果写
              在结尾写。
返回值:成功返回FILE指针,失败返回NULL,置位错误码

int fclose(FILE *stream);
功能:关闭文件
参数:
    @stream:文件指针
返回值:成功返回0,失败返回EOF(-1),置位错误码

 实例:

#include <stdio.h>

int main(int argc,const char * argv[])
{
    FILE *fp;
    if((fp = fopen("./hello.txt","w")) == NULL){
        printf("fopen file error\n");
        return -1;
    }
    
    //有一个fopen就要对应一个fclose
    if(fclose(fp)){
        printf("fclose file error\n");
        return -1;
    }
    return 0;
}

2.1 strerror函数

#include <string.h>

char *strerror(int errnum);
功能:将错误码转换为错误信息的字符串
参数:
    @errnum:错误码
返回值:错误信息的字符串

 实例:

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(int argc,const char * argv[])
{
    FILE *fp;
    if((fp = fopen("./hello.txt","r")) == NULL){
        printf("fopen file error\n");
        printf("errno = %d,%s\n",errno,strerror(errno));
        return -1;
    }
    return 0;
}

2.3 perror函数

#include <stdio.h>

void perror(const char *s);
功能:将错误信息打印到终端上
参数:
    @s:用户的附加信息
返回值:无

实例:

        注意perror打印时会在结尾部冒号。

#include <stdio.h>

int main(int argc,const char * argv[])
{
    FILE *fp;
    if((fp = fopen("./hello.txt","r")) == NULL){
        perror("fopen file error");
        return -1;
    }
    return 0;
}

2.4 fputc/fgetc函数

        put是往文件中,get是从文件中

#include <stdio.h>

int fputc(int c, FILE *stream);
功能:向文件中写入字符
参数:
    @c:字符的ascii
    @stream:文件指针
返回值:成功返回字符ascii值,失败返回EOF(-1)

int fgetc(FILE *stream);
功能:从文件中读取字符
参数:
    @stream:文件指针
返回值:成功读取到的字符的ascii,读取到文件结尾或者出错,返回EOF(-1)

fputc实例

#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE* fp;

    //以只写的方式打开文件,如果文件不存在就创建文件,如果文件存在就清空文件
    if ((fp = fopen("./hello.txt", "w")) == NULL) {
        perror("fopen file error");
        return -1;
    }
    
    fputc('h', fp); //将h字符写入到文件中,同时光标会向后移动一个字符的位置
    fputc('e', fp); //将e字符写入到文件中,同时光标会向后移动一个字符的位置
    fputc('l', fp);
    fputc('l', fp);
    fputc('o', fp);

    fclose(fp);
    
    return 0;
}
#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE* fp;

    //以只写的方式打开文件,如果文件不存在就创建文件,如果文件存在就清空文件
    if ((fp = fopen("./hello.txt", "w")) == NULL) {
        perror("fopen file error");
        return -1;
    }
    //将26个英文字符写入到文件中
    for(int i=0;i<26;i++){
        fputc('A'+i,fp);
    }

    fclose(fp);
    
    return 0;
}

fgetc实例

#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE* fp;
    int ch;
    //以只读的方式打开文件
    if ((fp = fopen("./hello.txt", "r")) == NULL) {
        perror("fopen file error");
        return -1;
    }

    //循环读取文件中的内容,如果没有到EOF,就就一直读取。
    //并把读取到的内容显示到终端上(fgetc每读取一次光标会向后移动一个字节)
    while ((ch = fgetc(fp)) != EOF) {
        printf("%c ", ch);
    }
    printf("\n");
    fclose(fp);
    return 0;

 1.使用fgetc统任意文件的行号

./a.out filename        //命令行传参

#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE* fp;
    int ch, line = 0;
    // 1.校验命令行参数的个数
    if (argc != 2) {
        printf("input error,try again\n");
        printf("usage: ./a.out filename\n");
        return -1;
    }
    // 2.以只读的方式打开文件
    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return -1;
    }

    // 3.循环读取文件中的字符,判断是否等于'\n'
    //让line++
    while ((ch = fgetc(fp)) != EOF) {
        if (ch == '\n') {
            line++;
        }
    }

    // 4.将行号打印到终端上
    printf("line = %d\n",line);

    // 5.关闭文件
    fclose(fp);
    return 0;
}

2.使用fgetc/fputc实现文件拷贝

​ ./a.out srcfile destfile

#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE *fp1, *fp2;
    int ch;
    // 1.校验命令行参数的个数
    if (argc != 3) {
        printf("input error,try again\n");
        printf("usage: ./a.out srcfile destfile\n");
        return -1;
    }
    // 2.只读方式打开源文件,以只写方式打开目标文件
    if ((fp1 = fopen(argv[1], "r")) == NULL) {
        perror("fopen src error");
        return -1;
    }
    if ((fp2 = fopen(argv[2], "w")) == NULL) {
        perror("fopen dest error");
        return -1;
    }

    // 3.循环拷贝
    while ((ch = fgetc(fp1)) != EOF) {
        fputc(ch,fp2);
    }

    // 4.关闭文件
    fclose(fp1);
    fclose(fp2);
    return 0;
}

2.5 fgets/fputs函数

char *fgets(char *s, int size, FILE *stream);
功能:从stream对应的文件中最多读取size-1个字符到s中
    读停止:当遇到EOF或者换行符时候会停止,如果是换行符停止的,它会将换换行符存储到s中
    s的结束:在s存储的最后一个字符之后通过添加'\0'的形式表示结束
    s=0123456789'\n''\0'  读结束的原因是读到'\n','\n'也会读出,并在末尾补'\0'
    s=01234'\0'           读结束的原因是读到结尾,返回EOF(-1),读结束,且在末尾补'\0'
参数:
    @s:用于存储读取到的字符串的首地址
    @size:读取字符串中字符的个数
    @stream:文件指针
返回值:成功返回s,失败返回NULL

int fputs(const char *s, FILE *stream);
功能:将s中的内容写入到stream对应的文件中(不包含'\0')
参数:
    @s:被写字符串的首地址
    @stream:文件指针
返回值:成功返回大于0的值,失败返回EOF

fgets函数的实例(fgets读取文件中的内容)

#include <stdio.h>

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

int main(int argc, const char* argv[])
{
    FILE* fp;
    char buf[20] = {0};

    if (argc != 2) {
        printf("input error,try again\n");
        printf("usage: ./a.out filename\n");
        return -1;
    }

    if ((fp = fopen(argv[1], "r")) == NULL)
        PRINT_ERR("fopen error");

    //读取文件第一行的内容(不保证全部读取到)
    fgets(buf,sizeof(buf),fp);

    printf("buf = %s\n",buf);

    fclose(fp);
    return 0;
}

fgets读取标准输入的内容

        fgets一般用来上程序输入字符串,

        因为scanf("%s")不能读取空格,gets在读的时候有越界的风险;

        所以fgets是最常用来读取字符串的

#include <stdio.h>
#include <string.h>

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

int main(int argc, const char* argv[])
{
    char buf[20] = {0};
    
    //fgets一般用来上程序输入字符串,因为scanf("%s")不能读取空格
    //gets在读的时候有越界的风险,所以fgets是最常用来读取字符串的

    // 从标准输入中读取字符到buf中,最多读取sizeof(buf)-1个字符
    fgets(buf,sizeof(buf),stdin);
    //hello'\n''\0'  //将字符串中的'\n'设置为'\0'
    buf[strlen(buf)-1]='\0';


    //将读取到的内容显示到终端上
    printf("buf = %s\n",buf);

    return 0;
}

使用fgets统计文件的行号

#include <stdio.h>
#include <string.h>

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

int main(int argc, const char* argv[])
{
    FILE* fp;
    char buf[10] = { 0 };
    int line = 0;
    // 1.校验命令行参数的个数
    if (argc != 2) {
        printf("input error,try again\n");
        printf("usage: ./a.out filename\n");
        return -1;
    }
    // 2.以只读的方式打开文件
    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return -1;
    }

    // 3.循环读取文件中的字符串
    while (fgets(buf, sizeof(buf), fp) != NULL) {
        //如果buf=sizeof(buf)-1说明读满了
        if (strlen(buf) == (sizeof(buf) - 1)) {
            //读满之后判断倒数第二个字符,如果不是换行就执行下次循环
            if (buf[sizeof(buf) - 2] != '\n')
                continue;
        }
        //如果没有读满line++,如果读满了倒数第二个字符是'\n'也让line++
        line++;
    }

    // 4.将行号打印到终端上
    printf("line = %d\n", line);

    // 5.关闭文件
    fclose(fp);
    return 0;
}

fputs向文件中写入字符串的实例

#include <stdio.h>
#include <string.h>

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

int main(int argc, const char* argv[])
{
    FILE* fp;
    char buf[] = "i am test fputs API.....";

    if ((fp = fopen("hello.txt", "w")) == NULL) {
        perror("fopen error");
        return -1;
    }

    fputs(buf,fp);

    fclose(fp);

    return 0;
}

fputs向标准输出或者标准错误中写

#include <stdio.h>
#include <string.h>

int main(int argc, const char* argv[])
{
    char buf[] = "i am test fputs API.....stdout";
    char buf1[] = "i am test fputs API.....stderr";

    //最终看到的现象都是在终端上显示一句话
    //区别stdout有缓冲区,stderr没有缓冲区
    fputs(buf,stdout); 
    fputs(buf1,stderr);
    while(1);

    return 0;
}

练习:使用fgets/fputs实现文件的拷贝

#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE *fp1, *fp2;
    char buf[20] = {0};
    // 1.校验命令行参数的个数
    if (argc != 3) {
        fputs("input error,try again\n",stderr);
        fputs("usage: ./a.out srcfile destfile\n",stderr);
        return -1;
    }
    // 2.只读方式打开源文件,以只写方式打开目标文件
    if ((fp1 = fopen(argv[1], "r")) == NULL) {
        perror("fopen src error");
        return -1;
    }
    if ((fp2 = fopen(argv[2], "w")) == NULL) {
        perror("fopen dest error");
        return -1;
    }

    // 3.循环拷贝 fgets成功返回buf(非0就会进入while),
    //失败返回NULL(void *)0,假退出循环
    while (fgets(buf,sizeof(buf),fp1)) {
        fputs(buf,fp2);
    }

    // 4.关闭文件
    fclose(fp1);
    fclose(fp2);
    return 0;
}

三、缓冲区问题

3.1 缓冲区的大小及类型

        全缓存:和文件相关的缓冲区(fp)4096(4K)

        行缓存:和终端相关的缓冲区 (stdin stdout)  1024(1k)

        无缓存:和标准出错 (stderr)  0

#include <stdio.h>

int main(int argc, const char* argv[])
{
    int num;
    scanf("%d", &num);

    printf("stdin size = %ld\n",
        stdin->_IO_buf_end - stdin->_IO_buf_base);

    printf("stdout size = %ld\n",
        stdout->_IO_buf_end - stdout->_IO_buf_base);

    printf("stderr size = %ld\n",
        stderr->_IO_buf_end - stderr->_IO_buf_base);

    FILE* fp;

    if ((fp = fopen("./hello.txt", "r+")) == NULL) {
        perror("fopen error");
        return -1;
    }
    fputs("hello",fp);
    printf("fp size = %ld\n",
        fp->_IO_buf_end - fp->_IO_buf_base);

    return 0;
}

3.2 行缓存的刷新机制

        1.行缓存遇到换行符的时候会刷新缓冲区

        2.当程序结束的时候会刷新行缓存

        3.当输入输出发生切换的时候

        4.当关闭文件的时候会刷新缓冲区

        5.缓冲区满也会刷新缓冲区

        6.主动刷新缓冲区fflush

#include <stdio.h>

int main(int argc, const char* argv[])
{
    // 1.行缓存遇到换行符的时候会刷新缓冲区
    //  printf("hello\n");
    //  while(1);

    // 2.当程序结束的时候会刷新行缓存
    //  printf("hello");

    // 3.当输入输出发生切换的时候
    //  printf("hello");
    //  fgetc(stdin);
    //  while(1);

    // 4.当关闭文件的时候会刷新缓冲区
    //  printf("hello");
    //  fclose(stdout);
    //  while (1);

    // 5.缓冲区满也会刷新缓冲区
    // for(int i=0;i<1025;i++){
    //     fputc('a',stdout);
    // }
    // while(1);

    // 6.主动刷新缓冲区 int fflush(FILE *stream);
    printf("hello");
    fflush(stdout);
    while (1);
    
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值