yuchuan_Linux_C 编程之七系统IO函数

一、整体大纲

二、 系统IO函数

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

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

C库IO函数工作流程:

pcb和文件描述符:

2. 虚拟地址空间

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

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

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

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

2)方便进程之间隔离

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

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

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

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

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

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

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

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

4. IO函数介绍

1)open

  • 查看 man 2 open
  • 头文件:
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
  • 函数原型:
1 int open(const char *pathname, int flags);
2 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:

 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 }
19
20 使用open实现一个touch功能

使用open实现一个touch功能

 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 }
25
26 一个进程打开的最大文件数(1024)

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

2)close

  • 作用:关闭文件描述符
  • 头文件:
1 #include <unistd.h>
  • 函数原型:
1 int close(int fd);
  • 参数说明:

fd文件描述符

  • 返回值:

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

3)read读

    • 头文件
1 #include <unistd.h>
  • 函数原型
1 ssize_t read(int fd, void *buf, size_t count);
  • 参数说明

fd 文件描述符

buf缓冲区

count缓冲区大小

  • 返回值

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

4)write写

    • 头文件
1 #include <unistd.h>
  • 函数原型
1 ssize_t write(int fd, const void *buf, size_t count);
  • 参数说明:

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

  • 返回值

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

 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 }
34
35 实现一个cat功能

实现一个cat功能

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

 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 }
38
39 bug版本

bug版本

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

5)lseek写

    • 头文件
1 #include <sys/types.h>
2 #include <unistd.h>
  • 函数原型
1 off_t lseek(int fd, off_t offset, int whence);
  • 参数说明

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

  • 返回值

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

  • lseek作用

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

示例:

a. 移动文件读写位置

 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 }
41
42 修改上例的bug(写入文件内容并读取文件内容打印到屏幕)

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

b. 计算文件大小

 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 }
23
24 计算文件大小(输出文件字节数)

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

c. 拓展文件

 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 }
25
26 拓展文件

拓展文件

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

6)fcntl

    • 头文件
1 #include <unistd.h>
2 #include <fcntl.h>
  • 函数原型
1 int fcntl(int fd, int cmd, ... /* arg */ );
  • 参数说明:

fd文件描述符
           cmd 命令

  • 返回值

不同的cmd返回值不同

 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 }
39
40 使用fcntl函数实现读非阻塞

使用fcntl函数实现读非阻塞

原文地址:https://www.cnblogs.com/YuchuanHuaying/p/11141263.html

时间: 2024-08-01 02:28:48

yuchuan_Linux_C 编程之七系统IO函数的相关文章

Linux中的系统IO函数

一.整体大纲 二. 系统IO函数1. 一些概念    文件描述符     PCB     C库函的IO缓冲区 1) 文件描述符            int 类型            一个进程最多可打开多少文件     2) pcb           进程控制块           在其中有一个文件描述符表 -- 数组[1024] C库IO函数工作流程: pcb和文件描述符: 2. 虚拟地址空间 虚拟地址空间就是程序启动起来之后从硬盘上会有一块虚拟内存分配出来. cpu 为什么要使用虚拟地址

第一天20150829:标准IO 和 系统IO -----stdio and sysio

IO 的实现包括两种实现: 1.stdio标准IO 2.sysio系统IO IO的两种实现方式正常都可以使用.但是,在使用的过程中优先使用stdio标准IO. 首先要了解这两种实现的原理: 1.sysio系统IO:我们作为USER 要与内核对话,那么系统为我们提供了一个sysio,可以直接对话KERNAL. 那么问题就产生了:如果USER使用的平台环境不一样(有的人有linux,有的人用windows),那么由于KERNAL不一样,那么提供给USER的sysio也会不一样 2.所以引申出来一个标

UNIX高级环境编程(7)标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流

? 1 二进制IO(Binary IO) 在前一篇我们了解了逐字符读写和逐行读写函数. 如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低. 为了应对这种场景,标准IO库提供了fread和fwrite函数. 函数声明: #include <stdio.h> size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp); size_t fw

Linux网络编程--IO函数以及示例

网络数据能够正确到达用户并被用户接收是进行网络数据传输的基本目的, 网络数据的接受和发送有很多种方案,例如:直接发送和接收,通过向量发送和接收,使用消息发送和接收等.本篇文章主要介绍常用的IO函数以及用法,如:最常用的read()/write()函数,和其他标准的套接字专用函数recv()/send(),readv()/writev(),recvmsg()/sendmsg(). 各个函数原型以及介绍如下: ssize_t read(int fd, void *buf, size_t count)

在C语言中以编程的方式获取函数名

调试常用的 __FILE__, __FUNCTION__, __LINE__ 调试常用的 __FILE__, __FUNCTION__, __LINE__ 没想到 VC6 不支持 __FUNCTION__ 所以我写了如下的奇怪代码 //用来记录当前行和当前函数//也可说是记录 堆栈void log_stack(const char *file, int line, const char * function); //当然还要对 __FUNCTION__ 宏作点修饰,因为这个宏只是在函数里面才起作

数据库编程1 Oracle 过滤 函数 分组 外连接 自连接

[本文谢绝转载原文来自http://990487026.blog.51cto.com] <大纲> 数据库编程1 Oracle 过滤 函数 分组 外连接 自连接 本文实验基于的数据表: winsows安装好Oracle11g之后,开始实验 SQLplus 登陆 ORacle sqlplus 退出的方式 查看用户之下有什么表 查看表的所有记录,不区分大小写 设置SQLplus行宽,页宽,列宽: 清屏命令 select as 语法 1,as别名的使用 2,没有引号带有空格的别名,无法识别: 3,带有

数据库编程2 Oracle 过滤 函数 分组 外连接 自连接

[本文谢绝转载原文来自http://990487026.blog.51cto.com] 续:数据库编程1 Oracle 过滤 函数 分组 外连接 自连接 where like模糊查询,查询员工姓名是4个字母 SQL> select * from emp where ename like '____';      EMPNO ENAME                JOB                       MGR HIREDATE          SAL       COMM    

Linux 高性能服务器编程——高级I/O函数

重定向dup和dup2函数 [cpp] view plaincopyprint? #include <unistd.h> int dup(int file_descriptor); int dup2(int file_descriptor_one, int file_descriptor_two); dup创建一个新的文件描述符, 此描述符和原有的file_descriptor指向相同的文件.管道或者网络连接. dup返回的文件描述符总是取系统当前可用的最小整数值. dup2函数通过使用参数f

【C/C++多线程编程之七】pthread信号量

多线程编程之信号量 Pthread是 POSIX threads 的简称,是POSIX的线程标准. 互斥量用来处理一个共享资源的同步访问问题,当有多个共享资源时,就需要用到信号量机制.          信号量机制用于保证两个或多个共享资源被线程协调地同步使用,信号量的值对应当前可用资源的数量.          1.信号量(samaphore):         信号量机制通过信号量的值控制可用资源的数量.线程访问共享资源前,需要申请获取一个信号量,如果信号量为0,说明当前无可用的资源,线程无