Linux基础IO

简介: Linux基础IO

shell执行的命令通常有两种


第三方提供的对应的在磁盘中有具体二进制文件的可执行程序(由子进程程序执行

shell内部,自己实现的方法,由自己(父进程)来执行

什么叫做文件

站在系统的角度,只要是能够被读取或者能够被写出的设备都可以叫做文件。

当前路径

进程运行起来所处的路径为当前路径。

直接清空

命令行>一个为文件

系统的文件访问的接口

open

包含3个头文件

第一个参数为打开的目标文件,第二个表示打开文件时所需要的参数,参数的传入用或运算|

参数:

O_RDONLY:只读打开

O_WRONLY:只写打开

O_RDWR:读写打开

以上三个常数,必须指定一个且只能指定一个

O_CREAT:若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限(注意默认的权限)

O_APPEND:追加写

打开成功则返回文件的描述符

打开失败返回:-1

write

read

close

lseek

文件描述符——fd

fd就是文件描述符的小整数。

下面三个是系统默认打开的

0:键盘

1:显示器

2:显示器


文件

被进程打开的文件(内存文件)

没有被打开的文件(磁盘上,文件=内容+属性)(磁盘文件)

文件描述符的本质是数组的下标

fd的分配原则是:最小的,没有被占用的文件描述符

看下面这个代码就没有验证上面的结论

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    close(2);
    int fd=open("test.txt",O_RDWR|O_CREAT,0666);
    if(fd<0)
    perror("open");

    printf("%d\n",fd);
    close(fd);
    return 0;
}

结果输出的就是2

上面关闭的2.

当关闭1的时候,那么发生的就是重定向。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    close(1);
    int fd=open("test.txt",O_RDWR|O_CREAT,0666);
    if(fd<0)
    perror("open");

    printf("%d\n",fd);
    fflush(stdout);
    close(fd);
    return 0;
}

我们发现1就没有在显示器上打印出来,而是写到了test.txt

为什么会这样呢?看下面的这个图

就是因为把1号文件描述符关上之后,打开的新的文件就会占用1。导致原本可以输入到显示器中的,现在显示到文件中。

重定向的本质:

像上面那样我们还需要手动的去关闭——close(1)。其实系统提供了这样的接口

dup2

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main()
{
    int fd=open("test.txt",O_RDWR|O_CREAT,0666);
    if(fd<0)
    perror("open");
    dup2(fd,1);
    printf("hhhhhhh\n");
    close(fd);
    return 0;
}

hhhhhhh就被写到了文件中。

语言的缓存区

看代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main()
{
    int fd=open("test.txt",O_RDWR|O_CREAT,0666);
    if(fd<0)
    perror("open");
    fprintf(stdout,"maole ");
    fprintf(stdout,"is cool\n");
    char str[]="emmmmmmmmmmm\n";
    //系统接口
    write(1,str,sizeof(str));
    fork();
    close(fd);
    return 0;
}
#当我们直接把结果打印到显示器上的时候
./myfile
#结果就是
maole is cool
emmmmmmmmmmm
#当我们把结果重定向到test.txt中的时候
#test.txt文件的内容为
emmmmmmmmmmm
maole is cool
maole is cool

为什么c语言提供的接口打印2次,而系统提供的接口打印1次?

C语言它有缓存区的概念,当执行fork的时候,当代码执行完的时候,数据还没有刷新,当刷新的时候,父进程的数据就会进行写实拷贝

就会刷新到文件,而刷新到文件就是更新数据,所以要进行写实拷贝——即子进程就要对原来缓存区的数据进行拷贝。

系统的接口直接进入内核的缓存区中,此时父进程就没有数据了,那么子进程也就不能没有数据进行拷贝,那么最后的结果就只有一份数据

为什么显示到显示器上的时候就是一次呢?

因为显示到显示器中行刷新,当执行fork的时候,数据已经刷新到显示器中,fork再进行创建子进程的时候也就没缓存区的数据了。

如果把上面代码中的\n去掉的话,结果就和写到文件中是一样的,因为没有进行行刷新

看代码:

int main()
{
    int fd=open("test.txt",O_RDWR|O_CREAT,0666);
    if(fd<0)
    perror("open");
    fprintf(stdout,"maole ");
    fprintf(stdout,"is cool");
    char str[]="emmmmm";
    write(1,str,sizeof(str));

    fork();
    close(fd);
    return 0;
}

结果就是:

emmmmmmaole is coolmaole is cool

缓存区在哪里?

缓存区就在系统的内核中,系统的内核有该结构体存储。

语言的缓存区,是语言自己封装的。


为什么要用缓冲区?

缓存区只是语言上存在的


缓冲区的刷新策略

立即刷新

行刷新(遇到\n)

满刷新

还有一些特殊的情况:


用户强制刷新:比如fflush

进程退出

缓存区的存在可以提高效率,减少I/O操作

看下面这段代码

int main()
{
    printf("hjhgfdfghj");
    close(1);
    return 0;
}

我们发现什么都没有打印。

为什么会这样呢?

是因为在关闭标准输出之后close(1),数据还没有进入标准输出的文件之中。那么最后程序执行完毕之后,也就不会显示什么内容。

:::info

标准输出stdout标准错误stderr都是显示到显示器上,那么他们之间有什么差别

:::

虽然1,2都是对应的打开显示器文件,但是他们是不同的,可以认为是同一个文件被打开了两次。

看下面这个代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
    errno=1;
    printf("stdout 1\n");
    perror("stderr 1");
    errno=2;
    printf("stdout 2\n");
    perror("stderr 2");
    return 0;
}

在命令行中输入:

  • 当运行./myfile的时候,发现都输出到显示器中了。
  • 把./myfile重定向到ok.txt文件中的时候,只要标准输出的显示到文件中,标准错误的还是显示到显示器上

再在后面添加2 >err.txt,就把错误的信息打印到err.txt中了

如果把所有的信息打印到同一个文件中:看下面的命令

其实对上面的命令,我们要知道它的本质——把新建fd替换1、2文件描述符

模拟实现缓存区

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

struct MyFile_
{
    int fd;
    char buffer[1024];
    int end;
};//缓冲区
typedef struct MyFile_  MyFile;


MyFile* fopen_(const char* path,const char* mode)
{
    assert(path);
    assert(mode);

    MyFile* ret=NULL;
    if(strcmp(mode,"r")==0)
    {

    }
    else if(strcmp(mode,"w")==0)
    {

    }
    else if(strcmp(mode,"r+")==0)
    {

    }
    else if(strcmp(mode,"w+")==0)
    {
        int fd=open(path,O_WRONLY|O_CREAT,0666);
        if(fd>=0)
        {
            ret=(MyFile*) calloc(1,sizeof(MyFile));

            ret->fd=fd;
            ret->end=0;
        }
    }
    else if(strcmp(mode,"a")==0)
    {

    }
    else if(strcmp(mode,"a+")==0)
    {

    }
    else{

    }
    return ret;
}
void fputs_(const char* str,MyFile* ret)
{
    assert(ret);

    strcpy(ret->buffer+ret->end,str);
    ret->end+=strlen(str);

    if(ret->fd==0)
    {

    }
    else if(ret->fd==1)
    {
        //标准输出

        if(ret->buffer[ret->end-1]=='\n')
        {
            write(ret->fd,ret->buffer,ret->end);
            ret->end=0;
        }
    }
    else if(ret->fd==2)
    {

    }
    else{

    }

}
//刷新
void fflush_(MyFile* ret)
{
    assert(ret);
    write(ret->fd,ret->buffer,ret->end);
    //刷新到内核的缓存区
    syncfs(ret->fd);
    ret->end=0;

}
//关闭
//关闭的时候也要进行刷新
void fclose_(MyFile* ret)
{
    assert(ret);
    fflush_(ret);
    close(ret->fd);
    free(ret);

}
int main()
{
    MyFile* p=fopen_("test.txt","w+");
    fputs_("mao le id cool",p);
    fork();
    fclose_(p);
    return 0;
}

更新模拟shell

在模拟的shell中添加重定向

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define INPUT 1
#define OUTPUT 2
#define APPEND 3
#define NOOPT 0

int option=NOOPT;

char* inspaced(char* start)
{
    assert(start);

    char* end=start+strlen(start)-1;
    while(start<=end)
    {
        if(*end=='>')
        {
            option=INPUT;
            *end='\0';
            if(*(end-1)=='>')
            {
                option=APPEND;
            }
            end++;
            break;
        }
        else if(*end=='<')
        {
            option=OUTPUT;
            *end='\0';
            end++;
            break;
        }
        else
        {
            --end;
        }
    }
    if(start<=end)
    return end;
    else
    return NULL;
}


char temp[20];
int main()
{
    extern char** environ;
    //查看环境变量
    // int i=0;
    // while(environ[i])
    // {
    //     printf("%s\n",environ[i++]);
    // }
    static char* opt[100];
    static char sstr[100];

    while(1)
    {
        printf("[root@ml is cool root]# ");
        fflush(stdout);

        memset(opt,0,100);
        //scanf("%s",sstr);
        if(fgets(sstr,100,stdin)==NULL)
        continue;
        sstr[strlen(sstr)-1]='\0';
        // 检查字符是否为重定向
        char* file_name=inspaced(sstr);
        int i=0;
        char* str;
        for(str=strtok(sstr," ");str!=NULL;str=strtok(NULL," ")){
            opt[i++]=str;
        }
        if(strcmp(opt[0],"ls")==0)
        opt[i++]="--color=auto";
        // printf("i=%d,opt[0]=%s\n",i,opt[0]);
        else if(strcmp(opt[0],"ll")==0)
        {
            i=0;
            opt[i++]="ls";
            opt[i++]="-l";
            opt[i++]="--color=auto";
        }
        else if(strcmp(opt[0],"cd")==0)
        {
            if(opt[1]) chdir(opt[1]);
        }
        else if(strcmp(opt[0],"export")==0&&opt[1]!=NULL)
        {
            // strcpy(temp,opt[1]);
            // putenv(temp);
            putenv(opt[1]);
        }
        opt[i]=NULL;
        // for(i=0;opt[i];i++)
        // printf("%s\n",opt[i]);

        // sleep(100);

        pid_t id=fork();
        if(id==0)
        {
            // printf("ML:%s\n",getenv("ML"));
            // printf("PATH:\n%s\n",getenv("PATH"));
            if(file_name)
            {
                int fd=-1;
                switch(option)
                {
                    case INPUT:
                        fd=open(file_name,O_WRONLY | O_TRUNC | O_CREAT, 0666);
                        dup2(fd,1);
                        break;
                    case OUTPUT:
                        fd=open(file_name,O_CREAT|O_RDWR);
                        dup2(fd,0);
                        break;
                    case APPEND:
                        fd=open(file_name, O_WRONLY | O_APPEND | O_CREAT, 0666);
                        dup2(fd,1);
                        break;
                    default:
                    break;
                }
            }
            // execvpe(opt[0],opt,environ);
            execvp(opt[0], opt);
            exit(1);    
            
        }
        pid_t status=0;
        pid_t ret=waitpid(id,&status,0);
        if(ret)
        {
            if(WIFEXITED(status))
            {
                printf("return no error %d\n",WEXITSTATUS(status));
            }
            else{
                printf("return error %d\n",status&0x7f);
            }
        }
    }
    return 0;
}
相关文章
|
1月前
|
网络协议 安全 Linux
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
92 0
|
1月前
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
86 1
Linux C/C++之IO多路复用(aio)
|
3月前
|
缓存 安全 Linux
Linux 五种IO模型
Linux 五种IO模型
|
1月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
25 0
Linux C/C++之IO多路复用(poll,epoll)
|
3月前
|
小程序 Linux 开发者
Linux之缓冲区与C库IO函数简单模拟
通过上述编程实例,可以对Linux系统中缓冲区和C库IO函数如何提高文件读写效率有了一个基本的了解。开发者需要根据应用程序的具体需求来选择合适的IO策略。
34 0
|
3月前
|
存储 IDE Linux
Linux源码阅读笔记14-IO体系结构与访问设备
Linux源码阅读笔记14-IO体系结构与访问设备
|
4月前
|
Linux 数据处理 C语言
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(下)
【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(下)
77 0
|
4月前
|
Linux 编译器 C语言
【Linux】基础IO----理解缓冲区
【Linux】基础IO----理解缓冲区
70 0
【Linux】基础IO----理解缓冲区
|
4月前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
219 2
|
5月前
|
Linux 编译器 C语言
【Linux】基础IO_4
【Linux】基础IO_4
29 3
下一篇
无影云桌面