Linux基础IO

基础IO:输入输出----

学习内容

回顾标准库IO接口:FILE*----文件流指针
系统调用IO接口学习:int----文件描述符
文件描述符和文件流指针之间的关系
文件描述符到底是什么----凭什么操作文件
文件系统----Linux下的ext2
软链接/硬链接
静态库/动态库:生成+使用

回顾标准库IO接口:
fopen       fclose      fread       fwrite      fseek       fgets
printf      fprintf     sprintf
系统调用IO接口学习:
open        close       write       read        lseek
open
  • int open(const char *pathname,int flags,mode_t mode);
  • pathname : 文件名
  • flags : 选项标志
  • 只能选其一/也必选其一

1、 O_RDONLY 只读
2、 O_WRONLY 只写
3、 O_RDWR 读写

  • 可选:

1、O_CREAT 文件不存在则创建,存在则打开
2、O_TRUNC 打开文件的时候清空原有内容
3、O_APPEND 写数据的时候总是追加在文件末尾

  • mode 文件权限

S_IWUSR | S_IXUSR | S_IRWXG
0064

  • 返回值:文件描述符(正整数) 失败:-1
write
  • ssize_t write(int fd,const void *buf,size_t count);
  • fd:打开文件返回的操作句柄
  • buf:要写入的数据
  • count:要写入的数据长度
  • 返回值:实际的写入长度 失败:-1
read
  • ssize_t read(int fd, void *buf, size_t count);
  • fd:打开文件返回的操作句柄—文件描述符
  • buf:读到数据放到buf中
  • count:要读取的数据长度
  • 返回值:实际读到的数据长度 失败:返回-1
lseek
  • off_t lseek(int fd, off_t offset, int whence);
  • fd:打开文件返回的操作句柄—文件描述符
  • offset:偏移量
  • whence:偏移起始位置

SEEK_SET 文件起始位置
SEEK_CUR 文件当前读写位置
SEEK_END 文件末尾位置

  • 返回值:偏移的位置到文件起始位置的偏移量
close
练习
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<fcntl.h>
int main()
{
  //open打开文件,有就打开,没有就创建
  int fd;
  char *file="./tmp.txt";
  umask(0);
  fd=open(file,O_RDWR | O_CREAT,0664);
  if(fd<0)
  {
    perror("open error!\n");
    return -1;
  }

  lseek(fd,10,SEEK_END);

  //write
  char buf[1024]="怎么才能成为一名优秀的程序员?\n";
  int ret=write(fd,buf,strlen(buf));
  if(ret<0)
  {
    perror("write error!\n");
  }

  lseek(fd,0,SEEK_SET);

  //read
  memset(buf,0x00,1024);
  ret=read(fd,buf,1023);
  if(ret<0)
  {
    perror("read error!\n");
    return -1;
  }
  else if(ret==0)
  {
    printf("没有读到数据!\n");
    return -1;
  }
  printf("buf:[%d-%s]\n",ret,buf+30);
  close(fd);
  return 0;
}

文件描述符和文件流指针之间的关系

文件流指针是标准库操作句柄:FILE
文件描述符是系统调用接口句柄:int
库函数和系统调用接口的关系?–库函数封装了系统调用接口
fp=fopen—>fwrite(fp) write(fp)
printf(“helli”)—>hello先被写入缓冲区

标准输入标准输入标准错误
stdinstdoutstderr—文件流指针
012—文件描述符
STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO—文件描述符的宏

[]

文件流指针中包含了文件描述符这么个成员变量
函数库内部封装的就是系统调用接口
缓冲区是用户态的缓冲区,存在于文件流指针结构体中

文件描述符到底是什么----凭什么操作文件

文件描述符是内核中 strcut file * fd_array[] 数组下标,这个数组中存放的就是文件描述信息

文件描述符分配规则:最小未使用
重定向:重定向的是文件描述符,改变一个文件描述符所对应的文件描述信息
改变了文件描述符所对应的文件描述信息,这时候对描述符写入数据,数据就从原本要写入的文件,流向了新的文件

\n刷新缓冲区仅仅针对的是标准输出文件,对于其它磁盘文件并不具备刷新缓冲区效果
系统调用因为没有缓冲区,因此不会执行缓冲区动作

minishell中的重定向实现:

在原有字符串及解析完毕的情况字;通过[ls -l > a/txt]解析之后获取到
argv[]={“ls” , “-l” , “>” , “a.txt” , NULL}
在子进程中判断argv中是否包含>或>>来决定是否需要重定向,以及重定向方式(>清空,>>追加);
fd=open(O_TRUNC | O_APPEND)
dup2(fd,1)将标准输出重定向到指定文件

/*  自主minishell实现
 *      1. 获取标准输入
 *      2. 解析输入得到[命令明]+[运行参数]
 *      3. 创建子进程
 *          子进程中进行程序替换
 *      4. 进程等待
 * */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

char buf[1024] = {0};

int do_face()
{
    memset(buf, 0x00, 1024);
    printf("[user@localhost]$ ");
    fflush(stdout);
    //   ls   -l\n
    //%[^\n]        从缓冲区取数据的时候,遇到\n为止:[   ls   -l]
    //经过这一步之后,缓冲区中留下了\n,但是\n取不出来,导致scanf非阻塞
    //但又取不出最后的\n字符
    //%*c   从缓冲区中取出一个字符(丢弃)
    if (scanf("%[^\n]%*c", buf) != 1) {
        getchar();
        return -1;
    }
    return 0;
}
int argc = 0;
char *argv[32];
int do_parse()
{
    //[   ls    -l   ]  ---> [ls] + [-l]
    argc = 0;
    char *ptr = buf;
    while(*ptr != '\0') {
        //将指针走到非空白字符处 l      -
        if (!isspace(*ptr)) {
            argv[argc] = ptr;
            argc++;
            //将ls走完,认为ls是一个完整的字符串
            //不能仅仅将l字符串作为第0个参数
            while(!isspace(*ptr) && *ptr != '\0') {
                ptr++;
            }
            //在ls之后添加一个字符串结尾标志
            //因为我们向获取的是ls,而不是[ls    -l   ]
            *ptr = '\0';
        }
        ptr++;
    }
    //argv[] = {"ls", "-l", ">", "a.txt", NULL }
    argv[argc] = NULL;
    return 0;
}

int main()
{
    while(1) {
        //获取标准输入,显示shell提示
        if (do_face() < 0) continue;
        //对字符串进行解析,去掉空格,将字符串解析成字符串指针数组
        //[  ls   -l  >> a.txt  ] ->
        //argv[] = {ls, -l, >>, a.txt}
        if (do_parse() < 0) continue;

        //实现shell内建命令
        if (strcmp(argv[0], "cd") == 0) {
            // int chdir(const char *path);
            // 改变当前工作路径
            chdir(argv[1]);
            continue;
        }

        int pid = fork();
        if (pid < 0) {
            perror("fork error");
            return -1;
        }else if (pid == 0) {
          
            //重定向不能在父进程中完成,父进程是需要打印数据的
            int i;
            //ls   -l  > a.txt
            //argv[] = {ls , -l , NULL , a.txt}
            for (i = 0; i < argc; i++) {
                if (strcmp(argv[i], ">") == 0) {
                    int fd = open(argv[i+1], O_WRONLY|O_CREAT|O_TRUNC);
                    dup2(fd, 1);
                    argv[i] = NULL;
                }else if (strcmp(argv[i], ">>") == 0) {
                    int fd = open(argv[i+1], O_WRONLY|O_CREAT|O_APPEND);
                    dup2(fd, 1);
                    argv[i] = NULL;
                }
            }
            execvp(argv[0], argv);
            exit(-1);
        }
        wait(NULL);

    }
    return 0;
}

文件系统:

在这里插入图片描述
超级块,inode table,数据区域,inode_bitmap,data_bitmap
inode(超级块): fsize,fmode,user,time,addr
存储一个文件流程: 从data_bitmap中获取空间数据块,写入文件数据,从inode_bitmap中获取空间inode节点,写入文件详细信息以及数据块位置;将文件目录项写入所在的目录文件中

目录文件中存放的是一张表: 目录下的文件信息(目录项:文件名+inode节点号)

读取文件流程: cat a.txt,通过文件名在目录项中获取到inode节点号,进而在inode区域中通过inode节点号获取到inode节点,进而获取到文件数据块存储位置,进而读取到文件数据

硬链接/软链接

软链接文件(独立文件): 是一个单独文件,通过记录源文件路径进而访问到源文件
硬链接文件(文件别名): 跟源文件没区别,通过与所有文件名使用相同的inode节点访问到文件数据

创建:

ln tmp.txt tmp.hard 给源文件创建硬链接文件
ln -s tmp.txt tmp.soft 给源文件创建软链接文件
在这里插入图片描述
删除源文件,硬链接文件,连接数-1;软链接文件失效
软链接文件可以跨分区建立,硬链接不可以
软链接文件可以对目录创建,硬链接不可以

动态库和静态库:生成+使用

gcc -c b.c -o b.o
库文件: 打包了大量机器代码的文件–供别人的程序使用
生成动态库: 动态库命名:lib是前缀 .so是后缀 中间是库名称

gcc -fPIC -c b.c -o b.o 将每个c语言代码编译成自己的目标代码
gcc --share b.o -o libmytest.so 将所有的目标代码合到一起生成动态库
fPIC:编译选项–产出位置无关代码
–share:链接选项–将所有的目标代码链接到一起生成动态库而不是生成可执行程序

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

gcc -c b.c -o b.o 将每个c语言代码编译成自己的目标代码
ar -cr libmytest.a b.o 将所有的目标代码合到一起生成静态库
ar:打包静态库的命令 -c创建 -r模块替换

使用静态库: 链接静态库使,不需要静态编译,只需要将库文件放到指定路径下,设置一下搜索路径即可,在linux下库文件只有被放入到指定路径下,才能被找到:/lib/lib64 /usr/lib /usr/lib64

1.向/etc/ld.so.conf.d/添加配置文件,在配置文件中添加库的搜索路径
2.将库文件直接放到指定路径下
3.设置环境变量:LIBRARY_PATH 库的链接搜索路径环境变量
设置环境变量:LD_LIBARARY_PATH 程序运行时库的加载路径环境变量
4.使用gcc的-L选项指定库的链接搜索路径

gcc -L 用于指定库的链接搜索路径
-l指定链接库名称
gcc a.c -o main -L.-lmytest 命令 -ldd 查找库

通常用户链接一个操作系统没有自带的库,都会链接静态库,用户喜欢使用-L指定库的链接搜索路径,因为程序链接静态库不需要依赖静态库存在,因此可以不用拷贝到/lib下因为没有其他程序使用这个第三方库,因此也不用考虑代码的冗余问题

链接静态库不需要使用静态链接-static,只需要保证指定路径下只有静态库就可以;链接器优先去指定的路径下链接库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值