linux基础IO

fopen fread fwrite fseek fclose

  1 /*回顾标准库IO接口*/
  2 
  3 #include <stdio.h>
  4 #include <string.h>
  5 
  6 int main()
  7 {
  8     FILE *fp = NULL;
  9     fp = fopen("./tmp.txt", "w+");
 10     if (fp == NULL) {
 11         perror("fopen error");
 12         return -1;
 13     }
 14 
 15     char buf[1024] = "jintianrenhaoshao~~\n";
 16     //size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
 17     //返回值为 nmemb而不是size
 18     fwrite(buf, strlen(buf), 1, fp);
 19 
 20     //int fseek(FILE *stream, long offset, int whence);
 21     //对fp文件读写位置从whence开始偏移offset个字节
 22     //  whence: 
 23     //      SEEK_SET    从文件起始位置开始偏移    
 24     //      SEEK_CUR    从当前读写位置开始偏移
 25     //      SEEK_END    从文件末尾位置开始偏移
 26     //  返回值:从文件起始位置到当前跳转位置的偏移量
 27     fseek(fp, 0, SEEK_SET);
 28     //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 29     memset(buf, 0x00, 1024);
 30     int ret = fread(buf, 1, 1023, fp);
 31     if (feof(fp)) {
 32         printf("at end of file\n");
 33     }else  {
 34         perror("fread error");
 35     }
 36     printf("ret:%d--buf:[%s]\n", ret, buf);
 37     fclose(fp);
 38     return 0;
 39 }

 文件结构体

struct FILE
{
    char *_ptr;//文件输入的下一个位置
    int _cnt;//当前缓冲区的相对位置
    char *_base;//指基础位置(文件的起始位置)
    int _flag;//文件标志
    int _file;//文件的有效性验证
    int _charbuf;//检查缓冲区状况,如果缓冲区则不读取
    int _bufsiz;//文件的大小
    char *_tmpfname;//临时文件名
};

文件描述符:

进程通过struct file_struct 结构体来描述打开的文件----------------------使用struct file*  fd_array[],文件描述符就是数组的下标

用户打开文件,操作系统通过file结构体描述文件,并将指针添加到fd_array[]中。向用户返回这个文件描述信息在数组下标中的位置,用户操作文件的时候,将下标床给操作系统,系统通过下标找到文件

进程的描述符是有上限的

文件描述符的分配原则:最小未使用原则

一个进程在运行起来默认会打开三个文件:

  • 标准输入   0(文件描述符)  stdin(文件流指针)
  • 标准输出   1(文件描述符)  stdout(文件流指针)
  • 标准错误   2(文件描述符)  stderr(文件流指针)
  1 /*系统调用接口使用*/
  2 
  3 #include <stdio.h>
  4 #include <unistd.h>
  5 #include <string.h>
  6 #include <fcntl.h>
  7 
  8 int main()
  9 {
 10     //mode_t umask(mode_t mask);
 11     //设置调用进程的文件创建权限掩码
 12     umask(0);
 13     //int open(const char *pathname, int flags, mode_t mode);
 14     //  pathname:  要打开的文件路径名
 15     //  flags: 选项参数
 16     //      必选其一:
 17     //          O_RDONLY    只读
 18     //          O_WRONLY    只写
 19     //          O_RDWR      读写
 20     //      可选项:
 21     //          O_CREAT     文件存在则打开,不存在则创建
 22     //          O_TRUNC     将文件长度截断为0(清空原有内容)
 23     //          O_APPEND    追加
 24     //  mode:   权限 0664
 25     //      (mode & ~umask)
 26     //  返回值:文件描述符(正整数)(系统调用接口的操作句柄)失败:-1
 27     int fd = open("./test.txt", O_RDWR | O_CREAT | O_APPEND | O_TRUNC, 0664);
 28     if (fd < 0) {
 29         perror("open error");
 30         return -1;
 31     }
 32     char *ptr = "jintianzhilaile 20 ren\n";
 33     //ssize_t write(int fd, const void *buf, size_t count);
 34     //  fd:     open返回的文件描述符
 35     //  buf:   要写入的数据
 36     //  count: 要写入的数据长度
 37     //  返回值:实际写入的数据长度(字节)  失败:-1
 38     int ret = write(fd, ptr, strlen(ptr));
 39     if (ret < 0) {
 40         perror("write error");
 41         return -1;
 42     }
 43 
 44     //off_t lseek(int fd, off_t offset, int whence);
 45     //  fd:     open返回的文件描述符
 46     //  offset: 偏移量
 47     //  whence:偏移起始位置
 48     //          SEEK_SET    文件起始位置
 49     //          SEEK_CUR    当前读写位置
 50     //          SEEK_END    文件末尾位置
 51     //  返回值:从文件起始位置到当前读写位置的偏移量 失败:-1
 52     lseek(fd, 0, SEEK_SET);
 53 
 54     char buf[1024] = {0};
 55     //ssize_t read(int fd, void *buf, size_t count);
 56     //  fd:     open返回的文件描述符
 57     //  buf:    内存首地址,用于存储读取的数据
 58     //  count: 要读取的数据长度
 59     //  返回值:实际读取的数据长度(字节) 失败:-1
 60     ret = read(fd, buf, 1023);
 61     if (ret < 0) {
 62         perror("read error");
 63         return -1;
 64     }
 65     printf("read buf:[%d-%s]\n", ret, buf);
 66 
 67     //int close(int fd);
 68     close(fd);
 69     return 0;
 70 }

文件流指针和文件描述符比较:

  • 库函数的操作句柄是文件流指针
  • 系统调用接口的句柄是文件描述符       

进程运行在用户态/内核态:

    用户态切换到内核态:发起系统调用  

重定向:

改变数据流向,将写入(newfd)指定文件的数据,改变写入到(oldfd)另一个文件当中

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <fcntl.h>
  4 
  5 int main()
  6 {
  7     //close(1);
  8 
  9     int fd = open("./test.txt", O_RDWR | O_CREAT);
 10     if (fd < 0) {
 11         perror("open error");
 12         return -1;
 13     }
 14     //将要打印的数据,不写入标准输出,而是写入test.txt
 15     dup2(fd, 1);
 16     //\n刷新缓冲区,仅仅针对标准输出文件才有效
 17     //其它文件,\n仅仅具备换行效果
 18     printf("fd:%d\n", fd);//stdout  ->   1 ---> test.txt
 19     //fflush(stdout);
 20 
 21     //close是系统调用接口,关闭文件时不会刷新缓冲区(因为我们所说的这个     缓冲区是stdout的缓
    冲区(用户态的缓冲区))
 22     close(fd);
 23     return 0;
 24 }

文件系统

在linux下为了更好的对文件管理,所以要对文件进行分区。每一个文件系统分区当中会将磁盘划分为数据块。

文件存储过程:通过inode_bitmap在inode区域获取空闲inode节点,通过data_bitmap获取空闲数据块,在inode节点中记录文件信息以及数据块位置,并且将文件数据写入到数据块中,将自己的目录项添加到所在目录项当中。

目录文件:文件中记录着目录下的文件信息(文件名+inode节点)-----目录项

文件读取过程:在目录项当中通过文件名获取inode节点(文件唯一),通过inode节点号在inode节点区域找到inode节点,通过inode节点中数据块地址信息,在指定数据块中读取数据。

查看文件大小:sudo dump2fs -h /dev/sda1 | grep " lnode size"

软连接/硬链接

          软链接文件:  ln  -s  srcfile   destfile          (就像文件的快捷方式,是一个独立的文件)

          硬链接文件:  ln  srcfile  destfile                 (一个文件的名字(目录项),与源文件公用一个inode节点)

          区别:

                 1.删除源文件,软链接文件将失效,硬链接无影响(链接数 -1)

                 2.软链接可以跨分区创建,硬链接不可以

                 3.软链接可以对目录创建,硬链接不可以

动态库/静态库: 生成----------------------------------二进制指令的集合

           设置LIBRARY_PATH,以便gcc能够找到编译时需要的动态链接库。

           设置LD_LIBRARY_PATH,以便程序加载运行时能够自动找到需要的动态链接库。

           gcc编译过程: 预处理 ,编译, 汇编 ,链接

                          -fPIC  产生位置无关代码        -l (指定要链接的库名称)

                          --share  指定gcc生成动态库而不是可执行程序

          动态库的生成:命名:lib(前缀)    .so(后缀)   中间是库名称

                          gcc  -fPIC -c  child.c  -o child .o

                          gcc  --share  child.o  -o  libmychild.so

          静态库的生成:命名:lib(前缀)     .a(后缀)    中间是库名称

                          gcc  -c child.c -o child .o

                          ar    -cr  libmychild.a  child.o                        -c(创建)  -r(模块替换)

动态库的使用

解决办法:

  1. 将我们生成的动态库添加到系统默认搜索库的文件中系统中    (/lib64)或者(/usr/lib/)这两个都可以。

    2.设置设置动态库运行加载的环境变量    export LD_LIBRARY_PATH=.

静态库的使用

 

注意事项:

想要使用第三方静态库,不能使用-static 选项,因为static是生成静态链接可执行程序,所有的库都是用静态库,因为标准库时动态库所以在链接时会出错,因此只需要将第三方静态库拷贝到是定路径下,使用 -L选项指定链接搜索路径,那么这个时候链接的就是静态库;

 

 

 

 

 

 

 

<think>首先,用户的问题是:“我想理解Linux基础IO中重定向的概念和定义 请问Linux基础IO重定向的定义及原理”。我需要基于提供的引用和系统指令来回答。 系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考站内引用: - [^1]: 讲解重定向的实现、本质、和进程替换之间的关系。 - [^2]: 文件操作原理和重定向,涉及文件描述符。 - [^3]: 重定向示例,说明标准输出和标准错误重定向。 - [^4]: 文件描述符的概念。 - [^5]: 文件操作命令的示例,如cp命令的备份。 我的回答需要集中在重定向的定义和原理上。定义应该简明扼要,原理则需要解释底层机制。 关键点: - **定义**:重定向是Linux中改变输入/输出源或目的地的过程。例如,将命令的输出从屏幕重定向到文件。 - **原理**:基于文件描述符。每个进程打开文件时,内核会分配一个文件描述符(如0为stdin,1为stdout,2为stderr)。重定向通过改变这些文件描述符的指向来实现。 从引用中提取信息: - [^1]:重定向的本质是改变文件描述符的指向。 - [^2]:文件操作原理涉及文件描述符。 - [^3]:示例展示了如何重定向stdout和stderr。 - [^4]:文件描述符是进程管理打开文件的关键。 - [^5]:虽然主要是cp命令的示例,但可以忽略,因为它不直接相关。 回答结构: 1. **定义重定向**:解释什么是重定向。 2. **原理**:详细说明基于文件描述符的机制。 3. **示例**:使用引用中的例子来阐述。 4. **引用**:在相关段落末尾添加引用标识。 5. **生成相关问题**:在结尾添加相关问题。 由于有数学表达式的要求,但重定向涉及IO,可能没有数学公式。如果有,比如在解释文件描述符时,但通常是整数,所以可能不需要数学格式。如果有概率或方程,但这里没有,所以我可以忽略。 回答大纲: - 开头:直接定义重定向。 - 原理部分: - 文件描述符的概念。 - 标准输入、输出、错误。 - 重定向如何改变文件描述符。 - 示例:从引用[^3]中取例子。 - 结尾:总结并生成相关问题。 生成相关问题: - 基于主题,如重定向的优化、应用场景等。 - 例如: 1. 如何重定向标准错误? 2. 重定向和管道有什么区别? 3. 在脚本中如何使用重定向? 确保回答清晰、逐步: - 步骤1: 定义 - 步骤2: 原理 - 步骤3: 示例 - 步骤4: 总结</think>### Linux基础IO重定向的定义及原理 在Linux系统中,重定向是一种基础IO操作,它允许用户改变命令的输入/输出源或目的地。下面我将逐步解释其定义、原理及相关机制,确保回答基于可靠的Linux内核机制和标准实践。 #### 1. **重定向的定义** - **基本概念**:重定向是指将命令的标准输入(stdin)、标准输出(stdout)或标准错误(stderr)从默认设备(如终端)重新定向到其他位置,例如文件或另一个命令。例如: - 输出重定向:将命令的输出从屏幕重定向到文件(如 `ls > file.txt`)。 - 输入重定向:从文件读取输入而非键盘(如 `sort < input.txt`)。 - **核心目的**:提升灵活性和效率,例如保存日志、批量处理数据或错误隔离[^1][^2]。 #### 2. **重定向的原理** 重定向的底层原理基于Linux的**文件描述符**机制和**进程管理**。以下是关键步骤的分解: - **步骤1: 文件描述符的基础** 每个进程启动时,内核会分配三个默认文件描述符(file descriptors): - `0`:标准输入(stdin),对应键盘输入。 - `1`:标准输出(stdout),对应屏幕输出。 - `2`:标准错误(stderr),对应屏幕错误输出。 文件描述符是进程访问打开文件的索引,内核通过它管理文件操作[^4][^2]。 - **步骤2: 重定向的实现机制** 当用户执行重定向命令(如 `command > file`),内核会: 1. **创建或打开目标文件**:例如,打开 `file` 用于写入。 2. **复制文件描述符**:使用系统调用(如 `dup2`)将目标文件的描述符复制到默认描述符的位置。例如: - `command > file` 将 `stdout`(描述符1)指向 `file` 的文件句柄。 - `command 2> error.log` 将 `stderr`(描述符2)指向 `error.log`。 3. **替换指向**:进程的默认描述符被替换为新的指向,所有输出或输入操作自动重定向到新位置。 这一过程本质是**修改进程的文件描述符表**,而非改变命令本身[^1][^3][^4]。 - **步骤3: 底层原理图解** 以 `ls > output.txt` 为例: - 初始状态:`stdout` 描述符1指向终端设备文件。 - 重定向后:内核将描述符1重定向到 `output.txt` 的文件句柄。 - 结果:`ls` 的输出写入文件而非屏幕。 这一机制依赖于Linux的**虚拟文件系统(VFS)**,确保所有IO操作统一处理[^2][^4]。 - **数学表示(如适用)** 在描述符管理中,文件描述符的复制可抽象为: $$ \text{dup2}(old\_fd, new\_fd) $$ 其中,`old_fd` 是目标文件的描述符,`new_fd` 是标准描述符(如1或2)。该调用将 `new_fd` 指向 `old_fd` 的文件对象,实现重定向[^1][^4]。 #### 3. **示例说明** 引用中的代码示例展示了重定向的常见问题: - 默认重定向仅影响 `stdout`(描述符1),如 `./a.out > stdout.txt`。 - 如果需要重定向 `stderr`(描述符2),必须显式指定: ```bash ./a.out 1> stdout.txt 2> stderr.txt ``` 这确保了错误输出独立保存,避免混合输出[^3]。 此示例验证了原理:内核通过描述符编号区分IO流。 #### 4. **总结** 重定向的本质是**通过修改进程的文件描述符表来改变IO流的指向**。它不改变命令的行为,而是改变其输入/输出的目的地。理解这一点有助于诊断IO问题(如输出未重定向)或优化脚本设计[^1][^2][^4]。重定向广泛应用于日志记录、数据管道和错误处理中。 ### 相关问题 1. 如何同时重定向标准输出和标准错误到同一个文件? 2. 重定向和管道(pipe)在原理上有何区别? 3. 在Shell脚本中,如何实现输入重定向以批量处理数据?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值