Linux-内存文件

1. 基础IO操作

1.1 c语言的IO接口

fopen:打开一个文件,按照指定方式

参数:filename 文件名,也可以是路径,mode:打开方式

返回打开的文件指针

fread:从指定流中读数据

参数:从FILE对象中读数据,每次读size字节大小的数据,最多读count次,读的数据写在buffer里

返回实际读的数据个数

fwrite:把数据写到指定的流中

参数 :从buffer中读数据,每次读size字节大小的数据,最多读count次,读的数据写在stream里

返回实际写的数据个数

fclose:关闭打开的文件

不同的语言,比如c,c++,java....都有对应的IO接口,语言的底层封装的其实都是操作系统对应的IO接口,在语言层面

好处有:使用方便,学习成本低,一套接口,在不同的操作系统下都可以使用,具有跨平台可移植性

1.2 Linux的IO接口

open:打开文件

参数:pathname 路径名称,flags:标记位,打开方式,mode:文件属性(新建)

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

参数:

  • O_RDONLY: 只读打开
  • O_WRONLY: 只写打开
  • O_RDWR : 读,写打开 这三个常量,必须指定一个且只能指定一个
  • O_CREAT : 若文件不存在,则创建它。需要使用mode选项(0666-umask),来指明新文件的访问权限
  • O_APPEND: 追加写
  • O_TRUNC:清空写

返回值:成功:新打开的文件描述符 失败:-1

读fd文件,写到buf里,读count字节

读buf内容,写到fd文件里,写count字节

关闭文件


2. 文件描述符fd

open函数返回值是int类型的fd,这个fd是什么呢?

我们在写代码,调用接口,然后文件被编译运行,形成进程,也就是进程在打开文件。

打开文件是进程在执行,打开文件,就是把文件从,磁盘加载到内存上,还需要一个标识符,让进程能够找到这个内存上的文件。这个标识符就是fd,file describe 文件描述符。通过fd,进程就能找到对应的文件,完成下面的操作。

一个进程,可能要打开多个文件,就会有多个fd,如何对fd进行管理呢?

而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来 描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进 程和文件关联起来。

每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件 描述符,就可以找到对应的文件

c库默认会打开3个文件:stdin标准输入,stdout标准输出,stderr标准错误

他们的fd分别对应0(键盘),1(屏幕),2(屏幕)

接下来新建的文件,就会从3开始,依次往下,

文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。


3. 文件重定向

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
    close(1);
    int fd = open("myfile", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    printf("fd: %d\n", fd);
    fflush(stdout);

    close(fd);
    exit(0);
}

此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>, <

那重定向的本质是什么呢?

可以使用系统提供的函数接口:dup2

使用这个函数也可以达到相同的效果

cpp 复制代码
dup2(3,1);

文件重定向:

|-------|---------------------------------------|---------------|
| 输出重定向 | open(O_WRONLY | O_CREAT | O_APPENT) | stdout重定向到新目标 |
| 追加重定向 | open(O_WRONLY | O_CREASE | O_TRUNC) | stdout重定向到新目标 |
| 输入重定向 | open(O_RDONLY ) | stdin重定向到新目标 |

stdout和stderr的使用区分

stdout是用来输出打印信息,stderr一般用来输出程序的报错记录

bash 复制代码
./proc > out.txt 2> err.txt

可以分开把stdou输出的内容重定向到out.txt,stderr输出的内容重定向到err.txt

bash 复制代码
./proc > all.txt 2>&1

把两个都输出到all.txt文件中

myshell实现文件重定向

cpp 复制代码
//整体结构:创建子进程,由子进程获取指令,父进程判断完成的怎么样
//1.打印标识开头
//2.获取指令字符串
//3.分析字符串提取指令到grev[]
//4.部分指令的特殊处理,例如cd
//5.替换进程execvpe
//
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

#define SIZE 1024
#define NUM 32

#define EMPTY 0
#define INPUTDIR 1
#define OUTPUTDIR 2
#define APPPUTDIR 3

int status_dir;

char str[SIZE];
char* _grev[NUM];
char _env[NUM][NUM];

char* getfile(char* str, int end)
{
    while(str[end] != ' ')
    {
        if(str[end] == '>')
        {
            if(str[end - 1] == '>')
            {
                status_dir = APPPUTDIR;
                str[end-1] = '\0';
                return &str[end+1];
            }
            status_dir = OUTPUTDIR;
            str[end] = '\0';
            return &str[end+1];
        }
        else if(str[end] == '<')
        {
            status_dir = INPUTDIR;
            str[end] = '\0';
            return &str[end+1];
        }
        else{
            end--;
        }
    }
    return NULL;
}

int main()
{
    int num_env = 0;
    status_dir = EMPTY;
    while(1)
    {
            //1.
            printf("[root$loadhost myshell]# ");
            fflush(stdout);
            //2.
            memset(str,SIZE,'\0');
            fgets(str, SIZE, stdin);
            int sz = strlen(str);
            str[sz - 1] = '\0';
            //3.
            int end = sz - 2;
            char* file_end = getfile(str, end);
            
            _grev[0] = strtok(str, " ");
            int index = 1;
            //4.
            if(strcmp(_grev[0],"ls") == 0)
            {
                _grev[index++] = (char*)"--color=auto";
            }
            if(strcmp(_grev[0], "ll") == 0)
            {
                _grev[0] = (char*)"ls";
                _grev[index++] = (char*)"--color=auto";
                _grev[index++] = (char*)"-l";
            }
            
            while(_grev[index++] = strtok(NULL, " "));
            if(strcmp(_grev[0], "cd") == 0)
            {
                if(_grev[1]) chdir(_grev[1]);
                continue;
            }
            if(strcmp(_grev[0], "export") == 0 && _grev[1])
            {
                memcpy(_env[num_env],_grev[1],strlen(_grev[1])+1);
                putenv(_env[num_env]);
                num_env++;
                continue;
            }
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork");
            exit(1);
        }
        else if(id == 0)
        {
            //child
            //5
            int fd;
            switch (status_dir)
            {
                case INPUTDIR:
                    fd = open(file_end, O_RDONLY);
                    dup2(fd,0);
                    break;
                case OUTPUTDIR:
                    fd = open(file_end, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                    dup2(fd,1);
                    break;
                case APPPUTDIR:
                    fd = open(file_end, O_WRONLY | O_CREAT | O_APPEND, 0666);
                    dup2(fd,1);
                    break;
                case EMPTY:
                    break;
                default:
                    printf("bug?\n");
                    break;
            }

            
            execvp(_grev[0], _grev);
            exit(2);
        }
        else {
            //father
            int status = 0;
            pid_t ret = waitpid(id, &status, 0);
            if(ret < 0)
            {
                printf("等待子进程失败\n");
                exit(2);
            }
            else{
                if(WIFEXITED(status))
                {
                    printf("子进程退出码:%d\n",WEXITSTATUS(status));
                }
                else if(WIFSIGNALED(status))
                {
                    printf("子进程终止信号:%d\n",WTERMSIG(status));
                }
            }
        }
    }
    return 0;
}
相关推荐
苹果醋316 分钟前
大模型实战--FastChat一行代码实现部署和各个组件详解
java·运维·spring boot·mysql·nginx
萧鼎40 分钟前
Python调试技巧:高效定位与修复问题
服务器·开发语言·python
GodK7771 小时前
IP 数据包分包组包
服务器·网络·tcp/ip
梁诚斌1 小时前
VSOMEIP代码阅读整理(1) - 网卡状态监听
运维·服务器·网络
深情废杨杨1 小时前
服务器几核几G几M是什么意思?如何选择?
运维·服务器
康熙38bdc1 小时前
Linux 进程优先级
linux·运维·服务器
Web极客码1 小时前
常见的VPS或者独立服务器的控制面板推荐
运维·服务器·控制面板
hhzz1 小时前
Linux Shell编程快速入门以及案例(Linux一键批量启动、停止、重启Jar包Shell脚本)
android·linux·jar
只是有点小怂1 小时前
parted是 Linux 系统中用于管理磁盘分区的命令行工具
linux·运维·服务器
代码雕刻家2 小时前
数据结构-3.10.队列的应用
服务器·数据结构