【Linux学习】基础IO1

简介: 【Linux学习】基础IO

前言

该文章基于Linux环境用于介绍基础IO。本文涉及C语言文件IO相关操作、认识文件相关的系统调用接口、认识文件操作符,理解重定向、通过对比fd和FILE,理解系统调用与库函数之间的关系、理解文件系统中的inode相关概念、认识软硬连接以及认识动静态库,结合gcc制作动静态库等。


一、C语言文件IO

1. C语言文件接口以及打开方式

  1. 文件接口
文件操作函数 功能说明
fopen 打开文件
fclose 关闭文件
fwrite 以二进制形式写入文件
fread 以二进制形式读取文件
fputc 写入一个字符
fgetc 读取一个字符
fputs 写入一个字符串
fgets 读取一个字符串
fprintf 格式化写入数据
fscanf 格式化读取数据
fseek 设置文件指针的位置
ftell 计算当前文件指针相对于起始位置的偏移量
rewind 设置文件指针到文件的起始位置
ferror 判断文件操作过程中是否发生错误
feof 判断文件指针是否读取到文件末尾
  1. 打开方式
打开方式 说明
r 打开文件用于只读
r+ 打开文件用于读和写
w 打开文件用于写文件,若文件存在,则会清空文件内容,若文件不存在,则会创建之
w+ 与 “w” 的区别在于,增加了读
rb 打开文件,用二进制形式读
rb+ 与"rb" 的区别在于,增加了写
wb 打开文件,以二进制形式写
wb+ 与"wb" 的区别在于,增加了读
a 以尾部追加的方式打开一个文本文件用于只写
a+ 与"a" 的区别在于,增加了读
ab 以尾部追加的方式打开一个二进制文件用于只写
ab+ 与"a" 的区别在于,增加了读

下面是用C语言进行读写操作的例子:

  • 写文件
#include <stdio.h>
#include <string.h>
int main()
{
  FILE* fp = fopen("myfile.txt", "w");
  if (!fp) {
    printf("fopen error!\n");
  }
  const char* msg = "hello world!\n";
  int count = 5;
  while (count--) {
    fwrite(msg, strlen(msg), 1, fp);
  }
  fclose(fp);
  return 0;
}


结果:

  • 读文件
#include <stdio.h>
#include <string.h>
int main()
{
  FILE* fp = fopen("myfile.txt", "r");
  if (!fp)
  {
    printf("fopen error!\n");
  }
  char buf[1024];
  const char* msg = "hello world!\n";
  while (1)
  {
    //注意返回值和参数,此处有坑,仔细查看man手册关于该函数的说明
    size_t s = fread(buf, 1, strlen(msg), fp);
    if (s > 0) {
      buf[s] = 0;
      printf("%s", buf);
    }
    if (feof(fp)) {
      break;
    }
  }
  fclose(fp);
  return 0;
}


结果:

2. 对当前路径的理解


学习了C语言文件操作过后,我们知道当用fopen以写入的方式打开一个文件时,如果该文件不存在,则会在当前路径下创建该文件,那么什么是当前路径呢?

例如,我们在test_file目录下运行mytest,发现该目录出现了一个myfile.txt文件。

那么,是否可以下结论说,当前路径就是我的可执行程序所在的路径呢?现在我们将myfile.txt删除,回退到上一级目录下再次执行mytest。

我们可以发现,myfile.txt最终出现在了当前所执行mytest的路径中。

当该可执行程序执行起来变成进程之后,我们可以通过获取该进程的PID,然后根据PID在根目录下的proc目录查看该进程的信息。

在这里我们可以看到两个软链接文件cwd和exe,cwd就是进程运行时我们所处的路径,而exe就是该可执行程序的所处路径。

由此我们可以得出结论:我们这里所说的当前路径不是指可执行程序所处的路径,而是指该可执行程序运行成为进程时我们所处的路径

3. 默认打开的三个流

Linux下一切皆文件,意思是在Linux中我们可以把任何东西都看成文件,那么显示器和键盘等也就相当于是文件。因为我们能在显示器中看到数据,是因为我们向显示器里面写入了数据,电脑能够得到我们从键盘敲入的字符,是因为电脑从键盘中读取了数据。


那么既然显示器和键盘等都是文件,为什么我们不需要提前打开"显示器文件" 和 “键盘文件”,就能直接进行键盘和显示器的相关操作呢?


需要注意的是,打开文件一定是进程运行的时候打开的,而任何进程在运行的时候都会默认打开三个输入输出流,即标准输入流、标准输出流以及标准错误流,对应到C语言当中就是stdin、stdout以及stderr。


其中,stdin对应的是键盘,stdout和stderr对应的是显示器。

通过查看man手册,我们会发现stdin、stdout以及stderr都是FILE* 类型的,也就是文件类型。

extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

当我们的C程序被运行起来时,操作系统就会默认使用C语言的相关接口将这三个输入输出流打开,之后我们才能调用类似于scanf和printf之类的函数向键盘和显示器进行相应的输入输出操作。


值得注意的是: 不止是C语言当中有标准输入流、标准输出流和标准错误流,C++当中也有对应的cin、cout和cerr,其他所有语言当中都有类似的概念。实际上这种特性并不是某种语言所特有的,而是由操作系统所支持的。

二、 系统文件IO

1. 系统接口

open

系统接口中使用open函数打开或者创建目标文件,man手册中的open:

参数解读:

pathname:要打开或创建的目标文件

flags: 打开文件时,传入的参数选项,用一个或者多个常量进行 “或” 运算,构成flags。

mode:给目标文件设置的权限

flags参数:

常量名 说明
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读,写打开
O_CREAT 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND 追加写

注意:前面三个常量,必须指定一个且只能指定一个。

返回值:

打开成功返回目标文件的文件描述符fd,失败则返回 -1。

write

系统接口中使用write向目标文件写入数据,man手册中的write:

参数解读:


fildes:文件描述符

buf:数据缓冲区

nbyte:向文件中写入数据的字节数

返回值:

ssize_t:有符号整型,在32位机器上等同于int,在64位机器上等同于long

写入成功,则返回实际写入数据的字节数;

写入失败,则返回 -1。

利用系统接口写文件的例子:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
    umask(0);
    int fd = open("myfile.txt", O_WRONLY | O_CREAT, 0664);
    if(fd < 0)
    {
        perror("open");
        return 1;
    }
    int count = 5;
    const char* msg = "hello world !\n";
    int len = strlen(msg);
    while(count--)
    {
        write(fd, msg, len);
    }
    close(fd);
    return 0;
}

read

系统接口中使用read函数读取文件中的数据,man手册中的read:


参数解读:

fildes:文件描述符

buf:数据缓冲区

nbyte:向文件中写入数据的字节数

返回值:

ssize_t:有符号整型,在32位机器上等同于int,在64位机器上等同于long

读取成功,则返回实际读取数据的字节数;

读取失败,则返回 -1。


利用系统接口读文件的例子:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
  int fd = open("myfile.txt", O_RDONLY);
  if(fd < 0)
  {
    perror("open");
    return 1;
  }
  const char* msg = "hello world !\n";
  char buf[1024];
  while(1)
  {
    ssize_t s = read(fd, buf, strlen(msg));//类比write
    if(s > 0)
    {
      printf("%s", buf);
    }
    else
    {
      break;
    }
  }
  close(fd);
  return 0;
}

close

系统接口中使用close关闭文件,man手册中的close:

参数解读:

fildes:文件描述符

返回值:

关闭成功,则返回 0;

关闭失败,则返回 -1。

【Linux学习】基础IO2:https://developer.aliyun.com/article/1383893

目录
相关文章
|
1月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
89 0
|
12天前
|
存储 安全 Linux
|
1月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
73 1
Linux C/C++之IO多路复用(aio)
|
14天前
|
Linux Shell 数据安全/隐私保护
|
1月前
|
Linux 编译器 C语言
【Linux快速入门(一)】Linux与ROS学习之编译基础(gcc编译)
【Linux快速入门(一)】Linux与ROS学习之编译基础(gcc编译)
|
1月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
23 0
Linux C/C++之IO多路复用(poll,epoll)
|
1月前
|
网络协议 Linux
linux学习之套接字通信
Linux中的套接字通信是网络编程的核心,允许多个进程通过网络交换数据。套接字提供跨网络通信能力,涵盖本地进程间通信及远程通信。主要基于TCP和UDP两种模型:TCP面向连接且可靠,适用于文件传输等高可靠性需求;UDP无连接且速度快,适合实时音视频通信等低延迟场景。通过创建、绑定、监听及读写操作,可以在Linux环境下轻松实现这两种通信模型。
34 1
|
1月前
|
Linux 开发工具
【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)
【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)
|
2月前
|
Linux
使用qemu来学习Linux的休眠和唤醒
使用qemu来学习Linux的休眠和唤醒
|
2月前
|
Linux
linux内核原子操作学习
linux内核原子操作学习