Linux C编程之七(2) 系统IO函数

Stella981
• 阅读 517

一、整体大纲

Linux C编程之七(2) 系统IO函数

二、 系统IO函数
1. 一些概念
    文件描述符
     PCB
     C库函的IO缓冲区

     1) 文件描述符
            int 类型
            一个进程最多可打开多少文件
     2) pcb
           进程控制块
           在其中有一个文件描述符表 -- 数组[1024]

C库IO函数工作流程:

Linux C编程之七(2) 系统IO函数

pcb和文件描述符:

 Linux C编程之七(2) 系统IO函数

2. 虚拟地址空间

    虚拟地址空间就是程序启动起来之后从硬盘上会有一块虚拟内存分配出来。

cpu 为什么要使用虚拟地址空间与物理地址空间映射?解决了什么样的问题?

    1)方便编译器和操作系统安排程序的地址分布。

         程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。通过虚拟地址空间与物理地址空间映射解决不连续的缓冲区的问题。

    2)方便进程之间隔离

        不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程使用的物理内存。 

    3)方便OS使用你那可怜的内存。

        程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,
        内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。

    虚拟地址空间的布局如下:

    0-3G是用户空间        3-4G是内核空间

       用户区                        内核区
       代码段
       已经初始化的全局变量
       未被初始化的全局变量
       堆 -- 从下往上
       共享库
       栈 - 从上往下
       环境变量
       内核区 

 Linux C编程之七(2) 系统IO函数

3. C库函数与系统函数的关系 

    FD:文件描述符 FP_POS:文件指针 BUFFER:缓冲区
    write对0-3G的用户空间进行操作 sys_write()对3-4G的内核空间进行操作

Linux C编程之七(2) 系统IO函数

4. IO函数介绍

1)open

  • 查看 man 2 open

  • 头文件:

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

  • 函数原型:

    int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);

  • 参数说明

 

           pathname 文件名
           flags
                 必选项:
                         O_RDONLY 只读
                         O_WRONLY 只写
                         O_RDWR 读写
                 可选项:
                         O_APPEND 追加
                         O_CREAT 创建文件
                         O_EXCL和O_CREATE一起使用,如果文件存在则报错

O_NONBLOCK 非阻塞
            Mode 权限位,最终(mode&~umask)

  • 返回值:

                 成功:返回最小的可用文件描述符
                 失败:返回-1,并且设置errno

  • open函数中的errno:

Linux C编程之七(2) 系统IO函数

Linux C编程之七(2) 系统IO函数 Linux C编程之七(2) 系统IO函数

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<unistd.h>
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     if (argc != 2)
10     {
11         printf("./a.out filename\n")
12         return -1
13     }
14     int fd = open(argv[1], O_CREAT|O_TRUNC|O_WRONLY, 0666);
15     close(fd);
16 
17     return 0;
18 }

使用open实现一个touch功能

Linux C编程之七(2) 系统IO函数 Linux C编程之七(2) 系统IO函数

 1 #include<stdio.h>
 2 #include<unistd.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<fcntl.h>
 6 #include<strings.h>
 7 
 8 int main(int argc, char *argv[])
 9 {
10     int num = 3;
11     char filename[128] = {0};
12     while(1)
13     {
14         sprintf(filename, "temp_%04d", num++);
15         if (open(filename, O_RDONLY|O_CREAT, 0666) < 0)
16         {
17             perror("open err:");
18                 break;
19         }
20     }
21     printf("num == %d\n", num);
22 
23     return 0;
24 }

一个进程打开的最大文件数(1024)

2)close

  • 作用:关闭文件描述符

  • 头文件:

    #include <unistd.h>

  • 函数原型:

    int close(int fd);

  • 参数说明:

          fd文件描述符

  • 返回值:

          成功:返回0
          失败:返回-1,并且设置errno

3)read读

  • 头文件

    #include <unistd.h>

  • 函数原型

    ssize_t read(int fd, void *buf, size_t count);

  • 参数说明

           fd 文件描述符

           buf缓冲区

           count缓冲区大小

  • 返回值

           失败:返回-1,设置errno
           成功:返回读到的字节数
                      0代表读到文件末尾
                      非阻塞的情况下read返回-1,但是此时需要判断error的值。

4)write写

  • 头文件

    #include <unistd.h>

  • 函数原型

    ssize_t write(int fd, const void *buf, size_t count);

  • 参数说明:

          fd文件描述符
          buf缓冲区
          count缓冲区大小

  • 返回值

          失败:返回-1,设置errno
          成功:返回写入的字节数
          0代表未写入

Linux C编程之七(2) 系统IO函数 Linux C编程之七(2) 系统IO函数

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<unistd.h>
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     if (argc != 2)
10     {
11         printf("./a.out filename\n")
12         return -1
13     }
14     int fd = open(argv[1], O_RDONLY);
15     char buf[256] = {0};
16     int ret = 0;
17     while ((ret = read(fd, buf, ziseof(buf))) != 0)
18     {
19         if (ret == -1)
20         {
21             perror("read err:");
22             return -1;
23         }
24         else
25         {
26             write(STDOUT_FILENO, buf, ret);
27         }  
28     }
29 
30     close(fd);
31 
32     return 0;
33 }

实现一个cat功能

需求:给一个文件中写入内容,写完之后打开该文件再读取写入的内容?

Linux C编程之七(2) 系统IO函数 Linux C编程之七(2) 系统IO函数

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<unistd.h>
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     if (argc != 2)
10     {
11         printf("./a.out filename\n");
12         return -1;
13     }
14     int fd = open(argv[1], O_RDWR|O_CREAT, 0666);
15 
16     char data[12] = "hello world";
17     write(fd, data, sizeof(data));
18 
19     char buf[256] = {0};
20     int ret = 0;
21     while ((ret = read(fd, buf, sizeof(buf))) != 0)
22     {
23         if (ret == -1)
24         {
25             perror("read err:");
26             return -1;
27         }
28         else
29         {
30             write(STDOUT_FILENO, buf, ret); //STDIN_FILENO, STDERR_FILENO
31         }
32     }
33 
34     close(fd);
35 
36     return 0;
37 }

bug版本

结果:内容写入到文件中,但是并未按预期输出到屏幕上。
原因:是由于write完成之后,fd到了文件末尾,因此read时到了文件末尾,无法读取文件数据
解决方法:写完之后将文件指针设置到文件开头,使用请看下文要介绍的lseek函数。

5)lseek写

  • 头文件

    #include <sys/types.h> #include <unistd.h>

  • 函数原型

    off_t lseek(int fd, off_t offset, int whence);

  • 参数说明

          fd文件描述符
          offset偏移量
          whence:
                SEEK_SET 文件开始位置
                SEEK_CUR 文件当前位置
                SEEK_END 文件结尾

  • 返回值

           失败:返回-1,设置errno
           成功:返回当前位置到文件开头的长度

  • lseek作用

          移动文件读写位置
          计算文件大小
          拓展文件

 示例:

a. 移动文件读写位置

Linux C编程之七(2) 系统IO函数 Linux C编程之七(2) 系统IO函数

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<unistd.h>
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     if (argc != 2)
10     {
11         printf("./a.out filename\n");
12         return -1;
13     }
14     int fd = open(argv[1], O_RDWR|O_CREAT, 0666);
15 
16     char data[12] = "hello world";
17     write(fd, data, sizeof(data));
18    //文件读写位置此时在末尾
19    //需要移动读写位置
20     lseek(fd, 0, SEEK_SET); //将fd移动到文件头
21 
22     char buf[256] = {0};
23     int ret = 0;
24     while ((ret = read(fd, buf, sizeof(buf))) != 0)
25     {
26         if (ret == -1)
27         {
28             perror("read err:");
29             return -1;
30         }
31         else
32         {
33             write(STDOUT_FILENO, buf, ret); //STDIN_FILENO, STDERR_FILENO
34         }
35     }
36 
37     close(fd);
38 
39     return 0;
40 }

修改上例的bug(写入文件内容并读取文件内容打印到屏幕)

b. 计算文件大小

Linux C编程之七(2) 系统IO函数 Linux C编程之七(2) 系统IO函数

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<unistd.h>
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     if (argc != 2)
10     {
11         printf("./a.out filename\n");
12         return -1;
13     }
14     int fd = open(argv[1], O_RDONLY);
15 
16     int ret = lseek(fd, 0, SEEK_END); //将fd移动到文件头
17     printf("file size is %d\n", ret); //注意实际读到的文件大小为ret-1
18 
19     close(fd);
20 
21     return 0;
22 }

计算文件大小(输出文件字节数)

c. 拓展文件

Linux C编程之七(2) 系统IO函数 Linux C编程之七(2) 系统IO函数

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<unistd.h>
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     if (argc != 2)
10     {
11         printf("./a.out filename\n");
12         return -1;
13     }
14     int fd = open(argv[1], O_WRONLY|O_CREAT, 0666);
15     //拓展文件
16     int ret = lseek(fd, 1024, SEEK_END); //将fd移动到文件头
17     //需要至少写一次,否则不能保存
18     write(fd, "a", 1);
19     printf("file size is %d\n", ret);
20 
21     close(fd);
22 
23     return 0;
24 }

拓展文件

阻塞的概念:
       read函数在读设备或者读管道,或者读网络的时候。
       输入输出设备对应的/dev/tty。

6)fcntl

  • 头文件

    #include <unistd.h> #include <fcntl.h>

  • 函数原型

    int fcntl(int fd, int cmd, ... /* arg */ );

  • 参数说明:

           fd文件描述符
           cmd 命令

  • 返回值

           不同的cmd返回值不同

Linux C编程之七(2) 系统IO函数 Linux C编程之七(2) 系统IO函数

 1 #include<stdio.h>
 2 #include<fcntl.h>
 3 #include<sys/types.h>
 4 #include<sys/stat.h>
 5 #include<unistd.h>
 6 
 7 int main(int argc, char *argv[])
 8 {
 9     //O_NONBLOCK设置为非阻塞
10     int fd = open("/dev/tty", O_RDWR);
11     //fcntl()函数,设置非阻塞
12     int flags = fcntl(fd, F_GETFL);
13     flags |= O_NONBLOCK;
14     fcntl(fd, F_SETFL, flags);
15 
16     char buf[256] = {0};
17     int ret = 0;
18     while(1)
19     {
20         //如果没有设置O_NONBLOCK
21         ret = read(fd, buf, sizeof(buf));
22         if (ret < 0)
23         {
24             perror("read err:");
25             printf("ret is %d\n", ret);
26         }
27 
28         if (ret)
29         {
30             printf("buf is %s\n", buf);
31         }
32         printf("haha\n");
33         sleep(1);
34     }
35     close(fd);
36 
37     return 0;
38 }

使用fcntl函数实现读非阻塞

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
2年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
ES6 新增的数组的方法
给定一个数组letlist\//wu:武力zhi:智力{id:1,name:'张飞',wu:97,zhi:10},{id:2,name:'诸葛亮',wu:55,zhi:99},{id:3,name:'赵云',wu:97,zhi:66},{id:4,na
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这