系统IO和标准IO

一、系统IO和标准IO

  • I/O:是一切实现的基础
  • stdio标准IO
  • sysio系统调用IO(文件IO)

1、stdio:FILE类型贯穿始终
2、sysio:fd是在文件IO中贯穿始终的类型,fd是文件描述符
文件IO与标准IO的关系
3、文件描述符的概念:
(1)整型数,数组的下标,文件描述符优先使用当前可用范围内最小

二、标准IO

(一)fopen()和fclose()

1、fopen()

#include <stdio.h>
FILE *fopen(const char *path,const char *mode);

  • (1)fopen()函数返回值:是栈、静态区、堆的哪一个?
FILE *fopen(const char *path,const char *mode)//栈上猜测
{
    FILE  tmp; 
    tmp. = ;
    ...........
    return &tmp; 
}

FILE *fopen(const char *path,const char *mode)//静态区猜测
{
    static FILE  tmp; 
    tmp. = ;
    ...........
    return &tmp; 
}

FILE *fopen(const char *path,const char *mode)//放在堆上
{
    FILE  *tmp = NULL;

    tmp = malloc(sizeof(FILE));
    tmp-> = ;
    ...........

    return tmp; 
}
  • 小结
    栈(F)    静态区(F)    堆(Ture)
    fopen();和fclose();是互逆操作,都是放在堆上 ———> 有互逆操作一定放在
    如果没有互逆操作的,就要自己用程序去判断是放在静态区还是堆上。
  • (2)fopen()只读方式,查看文件报错信息
int main()
{
   FILE *fp;
   fp = fopen("tmp","r");
   if (fp == NULL)
   {
       fprintf(stderr,"fopen() faild! errno = %d\n",errno);//errno已经被私有化
       perror("fopen()");
       fprintf(stderr, "fopen()%s\n", strerror(errno));
       exit(1);
   }
   else
   {
       fputs("ok!\n",stdout);
       fputs("OK\n",fp);
   }
   exit(0);
}

2、fclose()

int main()
{
   FILE *fp;
   fp = fopen("tmp","w");
   if (fp == NULL)
   {
       fprintf(stderr,"fopen() faild! errno = %d\n",errno);//errno已经被私有化
       perror("fopen()");
       fprintf(stderr, "fopen()%s\n", strerror(errno));
       exit(1);
   }
   puts("ok!");
   fclose(fp);
   exit(0);
}

3、文件打开信息

  • (1)一个进程中,默认打开3个流 stream —> stdin、stdout、stderr
  • (2)ulimit -a 控制打开的个数
  • (3)新文件权限 产生的公式: 0666(8进制) & ~umask
  • (4)umask 的值默认是0002(8进制),新建文件权限默认是:0664(8进制)
//maxfopen(),w  查看文件最多可以打开多少个
int main()
{
    int count = 0;
    FILE *fp = NULL;
    
    while (1)
    {
        fp = fopen("tmp", "w");
        if (fp == NULL)
        {
            perror("fopen()");
            break;
        }
        count++;
    }
    printf("count=%d\n", count);//输出count=1021,因为默认打开3个流 stream ---> stdin stdout stderr
    exit(0);
}

4、比较文件是否相同

  • diff   文件名1   文件名2

(二)fgetc()和fputc()

1、fgetc()

//这节主要讲fgetc 可以用来统计文件的大小
int main(int argc,char **argv)
{
   FILE *fp = NULL;
   int count = 0;

   if (argc < 2)
   {
       fprintf(stderr,"Usage:.....\n");
       exit(1);
   }
   fp = fopen(argv[1],"r");
   if (fp == NULL)
   {
       perror("fopen()");
       exit(1);
   }
   while(fgetc(fp) != EOF)
   {
       count++;
   }
   printf("%d\n",count);
   fclose(fp);
   exit(0);
}

2、fgetc()和fputc()联合使用

  • 通过fgetc()和fputc()函数章节,实现文件内容以字符操作方式
int main(int argc,char **argv)
{
   FILE *fps = NULL;
   FILE *fpd = NULL;
   int ch;

   if (argc < 3) //必须是3个参数
   {
       fprintf(stderr,"Usage:<s_file> <d_file>\n");
       exit(1);
   }
   
   fps = fopen(argv[1],"r");
   if (fps == NULL)
   {
       perror("fopen()");
       exit(1);
   }
   
   fpd = fopen(argv[2],"w");
   if (fpd == NULL)
   {
       fclose(fps);
       perror("fopen()");
       exit(1);
   }    
   
   while (1)
   {
       ch = fgetc(fps);
       if(ch == EOF)
       {
           break;
       }
       fputc(ch, fpd);
   }

   fclose(fpd);//先关目标文件,再关源文件
   fclose(fps);
   exit(0);
}

(三)fgets()和fputs()

  • 功能:实现文件内容以字符串操作方式
    1、fgets()函数详解
//gets()函数不要使用,C基础中有讲
//char *fgets(char *s, int size, FILE *stream);


#define SIZE 5
char bug[SIZE];
fgets(buf,SIZE,stream);

//fgets遇到 size-1 or '\n'停止

(1)文件内容abcdef  读SIZE-1
1  ----->  a b c d '\0'

(2)文件内容ab      读SIZE-1
2  ----->  a b '\n' '\0'

(3)文件内容abcd    擦边球  用fgets(buf,SIZE,stream);来读取,读取两次
1-> a b c d '\0'
2-> '\n' '\0'  ....

2、fgets()和fputs()函数进行读写

#define BUFSIZE 1024

int main(int argc,char **argv)
{
    FILE *fps = NULL;
    FILE *fpd = NULL;

    char buf[BUFSIZE];

    if (argc < 3) //必须是3个参数
    {
        fprintf(stderr,"Usage:<s_file> <d_file>\n");
        exit(1);
    }

    fps = fopen(argv[1],"r");
    if (fps == NULL)
    {
        perror("fopen()");
        exit(1);
    }

    fpd = fopen(argv[2],"w");
    if (fpd == NULL)
    {
        fclose(fps);
        perror("fopen()");
        exit(1);
    }

    while (fgets(buf, BUFSIZE, fps) != NULL)
    {
        fputs(buf,fpd);
    }

    fclose(fpd);//先关目标文件,再关源文件
    fclose(fps);
    exit(0);
}

(四)fread()和fwrite()函数

1、fread()函数详解

  • fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • fread保险起见,size=1个字节方式读
//1、知识点草稿
//fread(buf, size, nmemb, fp);
1 ---> 数据量足够  读一次   保险起见多1个字节方式  ********
fread(buf,1,10,fp)  ---> 10个对象,读到10个字节
fread(buf,10,1,fp)  ---> 1个对象, 读到10个字节

2 ---> 只有5个字节
fread(buf,1,10,fp)  ---> 5个对象,读到5个字节
fread(buf,10,1,fp)  ---> 0个对象,文件剩余多少???不知道

2、fread()和fwrite()函数进行读写

#define BUFSIZE 1024

int main(int argc,char **argv)
{
    FILE *fps = NULL;
    FILE *fpd = NULL;

    char buf[BUFSIZE];
    int n;

    if (argc < 3) //必须是3个参数
    {
        fprintf(stderr,"Usage:<s_file> <d_file>\n");
        exit(1);
    }

    fps = fopen(argv[1],"r");
    if (fps == NULL)
    {
        perror("fopen()");
        exit(1);
    }

    fpd = fopen(argv[2],"w");
    if (fpd == NULL)
    {
        fclose(fps);
        perror("fopen()");
        exit(1);
    }

    while ((n = fread(buf, 1, BUFSIZE, fps)) > 0) //返回读到的字节数,然后写n个字节
    {
        printf("%d\n", n);
        fwrite(buf, 1, n, fpd);
    }

    fclose(fpd);//先关目标文件,再关源文件
    fclose(fps);
    exit(0);
}

(五)printf()和scanf()函数

1、引申   sprintf   atoi**
(1)atoi函数使用

int main()
{
    char str[] = "123a456";

    printf("%d\n", atoi(str));

    exit(0);
}

(2)sprintf函数使用

int main()
{
    char buf[1024];
    int y = 2022, m = 3, d = 26;
    
    sprintf(buf,"%d-%d-%d",y,m,d);
    puts(buf);

    exit(0);
}

(六)fseek()和ftell()函数

1、fseek()和ftell()函数的缺陷

  • int fseek(FILE *stream, long offset, int whence);//设置文件指针位置 offset是偏移位置
  • long ftell(FILE *stream);//返回文件指针位置
  • 大小有局限性,因为long的大小在不同编译器中不确定

(1)通过 fseek()和ftell() 两个函数可以读取 文件大小

int main(int argc,char **argv)
{
    FILE *fp = NULL;
    long count;

    if (argc < 2)
    {
        fprintf(stderr,"Usage:.....\n");
        exit(1);
    }

    fp = fopen(argv[1],"r");
    if (fp == NULL)
    {
        perror("fopen()");
        exit(1);
    }

    fseek(fp, 0, SEEK_END); //SEEK_SET, SEEK_CUR, or SEEK_END
    count = ftell(fp);

    printf("%ld\n",count);
    fclose(fp);

    exit(0);
}

2、fwind()函数

  • void rewind(FILE *stream);//使得文件指针回到文件开始位置
  • rewind是对fseek的封装:(void) fseek(stream, 0L, SEEK_SET)

3、文件指针
(1)何谓文件指针
     就像读书时眼睛移动一样,文件指针逐行移动
(2)什么时候用?
     对一个文件先写后读的时候,比如:

  • FILE *fp = fopen();
  • fputc(fp); //放入10个字节,文件中有个文件位置指针,是指向最后一个字节的
  • fgetc(fp); //所以,再读取10个字节,无法得到刚刚写入的东西(指针已经向后偏移了)

(七)fseeko()和ftello()函数的出现

  • int fseeko(FILE *stream, off_t offset, int whence);
  • off_t ftello(FILE *stream);

1、弥补了fseek()和ftell()缺陷

2、off_t的大小可以在makefile中加入宏定义: CFLAGS+=-D_FILE_OFFSET_BITS=64

(八)标准IO的缓冲区和fflush()函数

缓冲区的作用:大多数情况下是好事,合并系统调用

1、行缓冲
     换行时候刷新,满了的时候刷新,强制刷新(标准输出都是这样的,因为是终端设备)   stdout

2、全缓冲
(1)满了的时候刷新,强制刷新(默认,只要不是终端设备都是全缓冲)
(2)现在的"\n"只是起到了换行作用
(3)缓冲区可以改的函数    setvbuf()

int setvbuf(FILE *stream, char *buf, int mode, size_t size);

3、无缓冲
     如:stderr,需要立即输出的内容

4、缓冲区的实例说明

(1) 缓冲区不够,不输出

//无法输出Before while
int main()
{
    printf("Before while");
    while(1)
    {
        ;
    }
    printf("After while");
    exit(0);
}

(2) 行缓冲输出方式

//末尾加换行符,可以输出Before while
int main()
{
    printf("Before while\n");
    while (1)
    {
        ;
    }
    printf("After while\n");
    exit(0);
}

(3)无缓冲输出方式

//使用fflush()函数可以输出Before while
int main()
{
    printf("Before while");
    fflush(stdout);
    while (1)
    {
        ;
    }
    printf("After while");
    fflush(NULL);
    exit(0);
}

(九)getline()函数

  • 作用:取到完整的一行函数
  • getline()内部封装形式: malloc() realloc() realloc()…realloc()
  • getline()属于可控内存泄漏   还有不可控的内存泄漏:服务器一直运行下去
  • 使用getline()时,在makefile里面加-D_GNU_SOURCE   或者   函数里引用 #define _GNU_SOURCE
int main(int argc, char **argv)
{
    FILE *fp = NULL;
    char *linebuf;
    size_t linesize;

    if (argc < 2)
    {
        fprintf(stderr,"Usage:<s_file> <d_file>\n");
        exit(1);
    }

    fp = fopen(argv[1], "r");
    if(fp == NULL)
    {
        perror("fopen()");
        exit(1);
    }

    printf("%s——%d——%s\n", __FILE__, __LINE__, __FUNCTION__);

    linebuf = NULL; //加这句很重要!!!!!
    linesize = 0;   //加这句话很重!!!!!

    while (1)
    {
        if(getline(&linebuf, &linesize, fp) < 0)//getline()存在可控内存泄漏
        {
            break;
        }
        printf("%ld\n", strlen(linebuf));
        printf("%ld\n", linesize);
    }

    fclose(fp);
    exit(0);
}

(十)临时文件

1、记住两点,①如何不冲突,②及时销毁
(1)tmpnam()不合适,容易多个人同时拿到
(2)tmpfile(): 合适,无文件名,不冲突,只要进程不是无休止的运行,不用fclose()也可以释放
(3)用fclose()可以及时销毁!!!!!

三、系统IO

(一)open()和close()

在这里插入图片描述

1、文件的拷贝

#define  BUFSIZE 1024

int main(int argc, char **argv)
{
    int sfd, dfd;
    char buf[BUFSIZE];
    int len, ret, pos;

    if (argc < 3)
    {
        fprintf(stderr, "Usage ....\n");
        exit(1);
    }

    sfd = open(argv[1], O_RDONLY);
    if (sfd < 0)
    {
        fprintf(stderr, "sfd open = %d\n", errno);
        perror("open()");
        exit(0);
    }

    dfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0600);
    if (dfd < 0)
    {
        close(sfd);
        fprintf(stderr, "dfd open = %d\n", errno);
        exit(0);
    }

    while (1)
    {
        len = read(sfd, buf, BUFSIZE);
        if(len < 0)
        {
            perror("read()");
            break;
        }
        if(len == 0)
            break;

        pos = 0;
        while (len > 0)
        {
            ret = write(dfd, buf + pos, len);
            if (ret < 0)
            {
                perror("write()");
                close(dfd);
                close(sfd);                
                exit(1);
            }
            pos = ret;
            len -= ret;
        }
    }

    close(dfd);
    close(sfd);
    exit(0);
}
//139运行测试
//1、   ./139ReadWrite /etc/services ./out
//2、   diff /etc/services ./out  ————> 比较文件是否一样 

//140运行测试
//1、   time    ./139ReadWrite /etc/services ./out   当前进程执行所要的时间
//(1)运行结果:
//      real	0m0.001s   
//      user	0m0.001s
//      sys	    0m0.000s
//(2)时间:
//      real = user + sys + (调度等待时间)
//      程序员解决是user和sys的时间

(二)文件IO与系统IO的区别

1、举例
  快递员送快递,分拣后批量送快递、单个快递送
2、区别
  响应速度(系统IO)&吞吐量(标准IO的缓冲区)
3、如何使一个程序更快
  结合响应速度和吞吐量回答
4、转换
  fileno()和fdopen()
5、注意:标准IO与文件IO不可以混用
  (1)FILE *fp; fputc —> pos++ ; fputc —> pos++
  (2)标准IO的pos有缓冲区,pos指针移动,不会影响系统IO的pos。
    举例:word保存与不保存。
  (3)系统IO的pos是等到标准IO缓冲区满,才会使系统IO的pos指针移动
6、通过strace  ./xxx
  strace可以用来查看一个可执行文件的系统调用

//1、 strace ./140StdioSysioDiff  运行后可以看到标准IO存在缓冲区,最后输出项
int main()
{
    printf("a");
    write(1, "b", 1);

    printf("a");
    write(1, "b", 1);

    printf("a");
    write(1, "b", 1);

    printf("\n");

    exit(0);

}
//更好的验证stdio的缓冲区
//2、 ./140StdioSysioDiff  可以验证ulimit -a  内容里面的数值
//    ulimit -a中的限制open files   (-n) 1024

int main()
{
    for (int i = 0; i < 1025; i++)
    {
        printf("a");
        write(1, "b", 1);
    }

    printf("\n");
    exit(0);    
}

(三)IO效率问题

1、习题
  将mycpy.c程序进行更改,将BUFSIZE的值一直放大,并观察进程所消耗的时间,注意性能最佳拐点出现时的BUFSIZE值,以及何时程序会出现问题。

(四)文件共享

1、多个任务共同操作一个文件或者协同完成任务
(1)面试:写程序删除一个文件的第10行
   补充函数:truncate() / ftruncate

(五)原子操作

1、不可分割的操作

  • 作用: 解决竞争和冲突,比如tmpnam的操作,不是原子

(六)程序的重定向dup和dup2

1、dup的作用

int dup(int oldfd); 将传入的文件描述符复制到(未使用的)最小新文件描述符

(1)close(1)是关闭stdout流 原始办法来实现输出到文件中

#define FNAME "./out"

int main()
{
    int fd;

    close(1);

    fd = open(FNAME, O_WRONLY | O_CREAT | O_TRUNC, 0600);
    if(fd < 0)
    {
        perror("open()");
        exit(0);
    }

    /********************************************/

    puts("hello!");

    exit(0);
}

(2)使用dup函数,这个程序存在两种情况,主要因为dup函数不是原子操作
 ①dup() 假设天生只有0、2,没有1,那么函数中fd本身创建好就是1
 ②旁边的兄弟也在运行,也突然创建一个,那么就到1上去了,你本身是3最多副本占据到4

#define FNAME "./out"

int main()
{
    int fd;

    fd = open(FNAME, O_WRONLY | O_CREAT | O_TRUNC, 0600);
    if(fd < 0)
    {
        perror("open()");
        exit(0);
    }

    close(1);
    dup(fd);
    close(fd);

    /********************************************/
    puts("hello!");

    exit(0);
}

2、dup2的函数
(1)、dup2属于原子操作方式

//实现输出到终端信息的内容,输出到对应的文件中

#define FNAME "./out"

int main()
{
    int fd;

    fd = open(FNAME, O_WRONLY | O_CREAT | O_TRUNC, 0600);
    if(fd < 0)
    {
        perror("open()");
        exit(0);
    }

    dup2(fd, 1);//相当于这两句操作:close(1);dup(fd);

    if (fd != 1)
    {
        close(fd);
    }      

    /********************************************/
    puts("hello!");

    exit(0);

(七)同步

1、sync设备即将解除挂载时进行全局催促,将buffer cache的数据刷新
2、fsync刷新文件的数据,并更新文件的属性
3、fdatasync函数只刷新文件的数据部分,而除数据外的文件属性(文件的时间)不更新

(八)Linux的ioctl和fcntl

1、fcntl函数
 管家级函数,文件描述符所变得魔术几乎都来源于该函数
2、ioctl函数
 (1)设备相关的内容
 (2)ioctl_list是跟设备打交道

(九)虚目录

1、/dev/fd/ 显示的是当前进程的文件描述符信息
2、ls -l /dev/fd/      看到谁就是查看谁,即在看ls

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值