Linux系统中的错误码与缓冲区

一、Linux内核中错误码

1.1 错误码的概念

在Linux内核中有4K(4096)个错误码
在这里插入图片描述
当出现错误时返回给上层的是负值,定义的正数,返回时是负值。
也就是相当于return -ENOENT;

上层进行拿错误码的时候拿的是正值,会将负号强制去掉。

那上层是怎么拿到这个值的呢?

#include <errno.h>
extern int errno

直接从errno中拿到错误码的值

1.2 在应用程序中获取错误码

#include <stdio.h>
#include <errno.h>
int main(int argc,const char * argv[])
{
    FILE *fp;
    fp=fopen("./hello.txt","r");
    if(fp==NULL)
    {
        printf("errno=%d\n",errno);
        return -1;
    }
    return 0;
}

结果展示:
在这里插入图片描述
应用层的工程师拿到2这个错误码之后其实并不知道是因为什么原因错误了,所以需要调用函数将这个错误码转化为错误信息,才能够看的懂。

1.3 错误转化错误信息(strerror函数)

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;
    fp=fopen("./hello.txt","r");
    if(fp==NULL)
    {
        printf("errno=%d\n",errno);
        printf("%s\n",strerror(errno));
        return -1;
    }
    return 0;
}

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

1.4 直接打印错误信息(perror函数)

#include <stdio.h>
void perror(const char *s);
功能:直接输出错误信息
参数:
    @s:你给出错时候附加的信息
返回值:无

代码示例:

#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc,const char * argv[])
{
    FILE *fp;
    /*fp=fopen("./hello.txt","r");
    if(fp==NULL)
    {
        perror("打开文件失败");
        return -1;
    } 优化代码*/
    if((fp=fopen("./hello.txt","r"))==NULL)
    {
        perror("打开文件失败了");
    }
    return 0;
}

继续优化

#include <stdio.h>
#include <errno.h>
#include <string.h>
#define PRINT_ERR(msg) \
    do                 \
    {                  \
        perror(msg); \
        return -1;     \
    } while (0)

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

    if ((fp = fopen("./hello.txt", "r")) == NULL)
        PRINT_ERR("打开文件失败");
    return 0;
}

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

1.5 将错误打印封装宏放到.h文件中

head.h

#ifndef __HEAD_H__
#define __HEAD_H__

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

#define PRINT_ERR(msg) \
    do                 \
    {                  \
        printf("%s %s %d\n",__FILE__,__func__,__LINE__);      \
        perror(msg);   \
        return -1;     \
    } while (0)

#endif

打印文件名,函数,行数
printf("%s %s %d\n",__FILE__,__func__,__LINE__);

我们将head.h文件直接放到系统头文件目录下
执行命令将head.h放到/usr/include/目录下
cd /usr/include/
sudo mv head.h的路径

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

二、缓冲区

标准IO中包含缓冲区,文件IO是不包含缓冲区的。

2.1 为什么使用缓冲区机制

减少CPU对磁盘的读写次数;CPU读取磁盘中的数据并不是直接读取磁盘,而是先将磁盘的内容读入到内存,也就是缓冲区,然后CPU对缓冲区进行读取,进而操作数据;计算机对缓冲区的操作时间远远小于对磁盘的操作时间,大大的加快了运行速度。

2.2 缓冲区的类型及其大小

标准I/O库提供缓冲的目的是尽可能减少使用read和write调用的次数。它也对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的麻烦。遗憾的是,标准I/O库最令人迷惑的也是它的缓冲。
缓冲区有三类
全缓存:和文件相关的操作就是全缓存(fp),大小是4096
行缓存:和终端相关的操作是行缓存(stdin、stdout),大小是1024
无缓存:标准出错stderr,大小是0

代码验证:

#include <head.h>

int main(int argc, const char *argv[])
{
    char h;
    //行缓存
    scanf("%c",&h);
    printf("stdin buffer size:%ld\n",stdin->_IO_buf_end-stdin->_IO_buf_base);
    printf("stdout buffer size:%ld\n",stdout->_IO_buf_end-stdout->_IO_buf_base);
    //无缓存
    fputs("666",stderr);
    printf("stderr buffer size:%ld\n",stderr->_IO_buf_end-stderr->_IO_buf_base);
    //全缓存
    FILE *fp;
    if ((fp = fopen("./hello.txt", "w")) == NULL)
        PRINT_ERR("打开文件失败");
    fputc('1',fp);
    printf("fp buffer size:%ld\n",fp->_IO_buf_end-fp->_IO_buf_base);
    return 0;
}

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

  1. 如果在一个程序中不使用stdin,它的缓冲区的大小就是0。
    如果使用stdin,此时验证的大小就是1024(1k)。
  2. 如果在一个程序中不使用stdout,它的缓冲区的大小就是0。
    如果使用stdout,此时验证的大小就是1024(1k)。
  3. stderr打印的1只是标识有没有使用stderr,大小是0
  4. 全缓存的缓冲区的大小是4096(4K)。

2.3 缓冲区的刷新时机

2.3.1 行缓存的刷新时机

在这里插入图片描述

#include <stdio.h>

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

    //printf("hello world");//程序结束刷新行缓冲区

    //printf("hello world\n");//行缓存遇见换行时刷新缓冲区
    //while(1);
    
    //printf("hello world");//当使用fflush的时候,会刷新行缓冲区
    //fflush(stdout);
    //while(1);

    //printf("hello world");//当关闭文件指针的时候,会刷新行缓冲区
    //fclose(stdout);
    //while(1);

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

    //当输入输出转换时刷新行缓冲区
    printf("hello world");
    char ch;
    scanf("%c",&ch);
    while(1);
    return 0;
}

总结:

  1. 当程序结束的时候会刷新行缓存
  2. 当行缓存遇到换行符的时候会刷新缓冲区
  3. 当使用fflush的时候,会刷新行缓冲区
  4. 当关闭文件指针的时候也会刷新行缓冲区
  5. 当行缓存满(1024)也会刷新缓冲区
  6. 当输入和输出发生切换的时候也会刷新行缓冲区

2.3.2 全缓存的刷新时机

在这里插入图片描述

#include <head.h>

int main(int argc, const char *argv[])
{   
    FILE* fp;
    if ((fp = fopen("./hello.txt", "w+")) == NULL)
        PRINT_ERR("fopen error");
    
    //fputs("hello world\n",fp);//全缓存遇见换行时不能刷新缓冲区
    //while(1);

    //fputs("hello world\n",fp);//程序结束刷新全缓冲区

    //fputs("hello world1\n",fp);//当使用fflush的时候,会刷新全缓冲区
    //fflush(fp);
    //while(1);

    //fputs("hello world2\n",fp);//当关闭文件指针的时候,会刷新全缓冲区
    //fclose(fp);
    //while(1);

    //当全缓存满会刷新全缓冲区
    //for(int i=0;i<4097;i++){
    //    fputc('a',fp);
    //}
    //while(1);

    //当输入输出转换时刷新全缓冲区
    char ch;
    fputs("hello world3\n",fp);
    fgetc(fp);
    while(1);
    return 0;
}

总结:

  1. 程序结束会刷新全缓存
  2. 关闭文件也会刷新全缓存
  3. fflush也会刷新全缓存
  4. 当缓冲区满(4906)会刷新全缓存
  5. 输入输出发生切换的时候也会刷新全缓冲区

三、fflush函数

fflush是一个在C语言标准输入输出库中的函数,功能是冲洗流中的信息,该函数通常用于处理磁盘文件。fflush()会强迫将缓冲区内的数据写回参数stream 指定的文件中。

函数名: fflush
功 能: 清除读写缓冲区,在需要立即把输出缓冲区的数据进行物理写入时
头文件:stdio.h
原型:int fflush(FILE *stream)
其中stream是要冲洗的文件指针

fflush(stdin);刷新标准输入缓冲区,把输入缓冲区里的东西丢弃。(非标准)
fflush(stdout);刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上。
注:
C和C++的标准里从来没有定义过 fflush(stdin)。也许有人会说:“可是我用 flush(stdin) 解决了这个问题,你怎么能说是错的呢?”的确,某些编译器(如VC6)支持用 fflush(stdin) 来清空输入缓冲,但是并非所有编译器都要支持这个功能,因为标准中根本没有定义fflush(stdin)。

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜猫徐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值