LinuxIO----基础

系统调用的文件操作

一、接口

open():打开文件:
    int open(const *pathname, int flags, mode_t mode);

pathname: 要打开文件的路径+名称
flags: 必须指定参数,O_RDONLY只读,O_WRONLY只写,O_RDWR读写; 可附加参数:O_CREAT打开文件不存在则创建,O_TRUNC打开文件后截断,O_APPEND以追加方式打开; 需要组合使用时,可以采用按位或方式“|”
mode: 对于新创建的文件,设置文件权限
返回值: 打开成功,返回文件描述符

write():写入文件
    ssize_t write(int fd, const void *buf, size_t count);

fd: 文件描述符
buf: 写入的数据
count: 写入数据大小

read():读取文件
    ssize_t read (ind fd, char *buf, size_t count);

buf: 数据存储空间,读到的数据存到哪
count: 最大读取内容,一般是指buf空间大小减1

lseek():根据文件指针的位置和偏移量来定位文件指针
    off_t lseek(int fd, off_t offset, int whence);

offset: 偏移量
whence: 定位文件流指针 SEEK_SETSEEK_ENDSEEK_CUR

close():退出文件
    close(int fd);
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main() {
    int fd = open("./tmp.txt", O_RDWR | O_CREAT | O_APPEND, 0664);
    if(fd < 0) {
        perror("open");
        return 0;
    } 
    int ret = write(fd, "linux-66", 8);  //写
    if(ret < 0) {
        perror("write");
        return 0;
    }
	lseek(fd, 5, SEEK_SET);
    char buf[1024] = {0};  //读
    ret = read(fd, buf, sizeof(buf) - 1);
    if(ret < 0) {
        printf("read");
        return 0;
    }
    else if(ret == 0)
        printf("read size [%d]\n", ret);
    printf("%s\n", buf);
    close(fd);
    return 0;
}
重定向及dup2
    int dup2(int oldfd,int newfd);  oldfd和newfd都是文件描述符

重定向的本质就是newfd文件描述符拷贝oldfd文件描述符;若oldfd是一个无效的文件描述符,newfd在重定向时不会关闭但调用dup2函数会失败。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
    int fd = open("./tmp.txt", O_RDWR | O_CREAT, 0664);
    if(fd < 0) {
        perror("open");
        return 0;
    }
    dup2(fd, 1);  //通过重定向,将本应该输出在标准输出上的内容输出到文件当中去
    printf("%s\n", "linux-66");
    while(1)
        sleep(1);
    return 0;
}
命令操作的重定向:'>''>>''<'
eg. echo  "hello world" > hello.txt

二、文件描述符与FILE

  1. 理解: 文件描述符是内核为了高效的管理已经被打开的文件所创建的索引,它是一个非负整数,用于指代被打开的文件,所有执行I/O操作的系统调用都是通过文件描述符完成的。因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。
  2. 文件描述符的分配原则: 最小未分配原则,在files_struct数组当中,找到当前没有被使用
    的最小的一个下标,作为新的文件描述符。
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    int main() {
       close(0);  //关闭标准输入
       int fd = open("myfile", O_RDONLY);
       if(fd < 0) {
           perror("open");
           return 1;
       }
       printf("fd: %d\n", fd);  //fd为0
       close(fd);
       return 0;
    }
  1. 查看当前操作系统为进程所分配的文件描述符信息:|| /proc/[pid]/fd
  2. 排查文件描述符泄露: ulimit -a ll /proc/[pid]/fd
  3. 文件流指针与文件描述符的关系: 封装关系。文件流指针(接口都是库函数)封装文件描述符(接口都是系统调用)
  4. 当我们使用fwrite进行操作时,最先写到缓冲区,然后当刷新缓冲区时,才通过_IO-FILE当中保存的文件描述符将写缓冲区当中的内容到文件中去,若程序崩溃有可能导致使用文件流指针写入的数据丢失.。
    当使用文件描述符时,没有这个问题,由于立即读取,立即写入的操作非常消耗性能,所以一般不重要的数据采用文件流指针操作
    刷新缓冲区:
    ①main函数return返回时;
    ②fflush强制刷新;
    ③缓冲区满会自动刷新;
    ④\n刷新缓冲区。

静态库和动态库

    静态库                      动态库
    win:  .lib后缀             win:  .all后缀
    linux:  .a后缀             linux:  .so后缀
    不管是使用静态库还是使用动态库,这些库文件都是由*.o文件生成的,在给编译器gcc传不同的参数,就会生成相应的
    静态库或者是动态库了
静态库的生成

静态库会将编译的代码当中所有函数的代码全部编入静态库;如果程序链接静态库生成可执行程序时,会将静态库中所有代码全部编译到可执行程序,当程序执行时,不需要依赖静态库。

#include "print.h"  //print.c
void print() {
	printf("I am print.c file code\n");
}
#include "print.h"  //main.c
int main() {
    print();
    return 0;
}
#include <stdio.h>  //print.h
void print();

ar -rc lib[库文件名称].a xxx.o  生成静态库
gcc bbb.c -o bbb -L [path](指定库路径) -l[库文件名称(去前缀和后缀)]  使用静态库

eg.
gcc -c print.c -o print.o  生成.o文件
ar -rc libprint.a print.o  生成静态库
gcc main.c -o main -L . -lprint  使用静态库
./main  执行
I am print.c file code
动态库的生成
#include "print.h"  //main.c
int main() {
    print();
    return 0;
}
#include "print.h"  //print.c
void print() {
    printf("i am print.c file code\n");
}
#include <stdio.h>  //print.h
void print();

gcc -shared(生成共享库的命令行参数) -fPIC(产生位置无关的代码) xxx.c -o lib[库文件名称].so  生成动态库
gcc bbb.c -o bbb -L [path](指定库路径) -l[库文件名称(去前缀和后缀)]  使用动态库

eg.
gcc -shared -fPIC print.c -o libprint.so
gcc main.c -o main -L . -lprint

注:如果程序依赖一个动态库,在程序运行时,它能正常找到库的方式:

  1. 在当前可执行程序的目录下;
  2. 在环境变量中设置动态库的搜索路径,设置环境变量LD_LIBRARY_PATH;
  3. 库在操作系统库(一般不使用)。

三、文件系统

ext2文件系统

在这里插入图片描述

  1. Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成;

  2. 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck和inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了 ;

  3. GDT,Group Descriptor Table:块组描述符,描述块组属性信息 ;

  4. 块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用 ;

  5. inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用;

  6. i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等 ;

  7. 数据区:存放文件内容。

  8. 如何存储文件数据:
    将文件分成不同的block块,从block bitmap当中查找data blocks区域当中空闲的块,将文件存储在空闲块中;
    需要对文件进行表述,否则在获取文件的时候不知道文件分成的块存储再data blocks区域当中的哪一个位置,使用inode bitmap节点去描述文件存储信息,从inode table当中获取inode节点,使用inode节点描述文件存储的信息;
    文件名称+inode节点名称作为目录的目录项存储起来 ll -i

  9. 如何获取文件数据:
    在目录中根据文件名称+inode节点号,找到inode节点;根据inode节点信息,查找文件对应的信息,将块信息合并,就获取了文件信息。

软链接
    in -s [源文件名称] [软链接文件]   源文件和软链接文件inode节点号不同但描述信息都是一样的      

若删除源文件,则软链接文件会闪烁,若此时修改软链接文件,则会重新创建源文件(一般不要用)

硬链接
    in [源文件名称] [软链接文件]    

源文件和软链接文件inode节点号,描述信息都是一样的类似于C++引用(别名)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值