[Linux]----文件操作(复习C语言+文件描述符)

news2025/2/23 20:53:38

文章目录

  • 前言
  • 一、基础概念
  • 二、回顾C语言
    • 2.1 对文件进行写操作
    • 2.2 追加写文件
    • 2.3 读文件
    • 2.4 简易cat功能
    • 总结
      • stdin&stdout&stderr
      • 打开文件的方式
  • 三、系统文件I/O
    • 接口介绍
      • open介绍
      • 使用open接口
      • close
      • write
      • read
  • 四、文件描述符
    • 先验证0,1,2就是标准的IO
      • 标准输入流
      • 标准输出流
      • 标准错误流
    • 验证0,1,2和stdin,stdout,stderr的对应关系
    • 文件描述符的分配规则
  • 总结


前言

今天这个小结节,我来大家来了解Linux下的文件操作。首先我们来复习一下C语言的文件操作,基于C语言的文件操作我们对Linux的学习就会方便很多了!我带大家首先来了解文件相关系统的接口和文件描述符,并且理解重定向!
最后在基于重定向,在下一小节将我上节写的myshell完善一下!



正文开始!

一、基础概念

  1. 文件=文件内容+文件属性(属性也是数据,即便你创建一个空文件,也要占据磁盘空间)
  2. 文件操作=文件内容的操作+文件属性的操作(有可能再操作文件的时候,即改变内容,又改变属性)
  3. 对于文件操作,我们首先要打开文件。所谓"打开"文件,究竟在干什么?将文件的属性或者内容加载到内存中!(冯诺依曼体系结构决定!)
  4. 是不是所有的文件,都会被处于打开的状态呢?没有被打开的文件,在哪里呢?(只在磁盘上存储!)
  5. 打开的文件(内存文件)和磁盘文件
  6. 通常我们打开文件,访问文件,关闭文件,是谁在进行相关操作?fopen,fwrite,fread,fclose…->代码->程序->当我们文件程序,运行起来的时候,才会执行对应的代码,然后才是真正的对文件进行相关的操作(进程在做相关操作!!!)
  7. 进程和打开文件的关系!

二、回顾C语言

2.1 对文件进行写操作

在这里插入图片描述
当我们以w方式打开文件,准备写入的时候,其实文件已经先被清空了!

#include<stdio.h>
#include<unistd.h>

int main()
{
    FILE* fp=fopen("log.txt","w");
    if(fp==NULL)
    {
        perror("fopen");
        return 1;
    }
    const char* msg="hello rose!";
    int cnt=0;
    while(cnt<10)
    {
        fprintf(fp,"%s %d\n",msg,cnt);
        cnt++;
    }
    fclose(fp);
    return 0;
}

在这里插入图片描述

默认这个"log.txt"文件会在哪里形成呢?—>当前路径

那么什么是当前路径呢?–>进程当前的路径

接下来带大家查看进程的信息

#include<stdio.h>
#include<unistd.h>

int main()
{
    FILE* fp=fopen("log.txt","w");
    if(fp==NULL)
    {
        perror("fopen");
        return 1;
    }
    printf("%d\n",getpid());
    while(1)
    {
        sleep(1);
    }//在这里会一直休眠下去,直到我们杀掉这个进程
    const char* msg="hello rose!";
    int cnt=0;
    while(cnt<10)
    {
        fprintf(fp,"%s %d\n",msg,cnt);
        cnt++;
    }
    fclose(fp);
    return 0;
}

ll /proc/进程id

在这里插入图片描述
在这里我们就可以看到"log.txt"就在当前cwd,也就是进程所处的路径了。

接下来我们有意识的更改当前路径,这里需要用到系统接口chdir();
在这里插入图片描述

#include<stdio.h>
#include<unistd.h>

int main()
{
    chdir("/home/hulu");
    FILE* fp=fopen("log.txt","w");
    if(fp==NULL)
    {
        perror("fopen");
        return 1;
    }
    printf("%d\n",getpid());
    while(1)
    {
        sleep(1);
    }
}

在这里插入图片描述

在这里插入图片描述

2.2 追加写文件

#include<stdio.h>
#include<unistd.h>

int main()
{
    FILE* fp=fopen("log.txt","a");
    if(fp==NULL)
    {
        perror("fopen");
        return 1;
    }
    const char* msg="hello rose!";
    int cnt=0;
    while(cnt<5)
    {
        fprintf(fp,"%s %d\n",msg,cnt);
        cnt++;
    }
    fclose(fp);
    return 0;
}

在这里插入图片描述

追加写入,不断的往文件中新增内容—>追加重定向!

在这里插入图片描述

2.3 读文件

在这里插入图片描述

#include<stdio.h>
#include<unistd.h>

int main()
{
    FILE* fp=fopen("log.txt","r");
    if(fp==NULL)
    {
        perror("fopen");
        return 1;
    }
    char buffer[64];
    while(fgets(buffer,sizeof(buffer),fp)!=NULL)
    {
        printf("echo: %s",buffer);
    }
    fclose(fp);
    return 0;
}

在这里插入图片描述

2.4 简易cat功能

#include<stdio.h>
#include<unistd.h>

//myfile filename
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        printf("Usage: %s filename\n",argv[0]);
        return 1;
    }
    FILE* fp=fopen(argv[1],"r");
    if(fp==NULL)
    {
        perror("fopen");
        return 1;
    }
    char buffer[64];
    while(fgets(buffer,sizeof(buffer),fp)!=NULL)
    {
        printf("%s",buffer);
    }
}

在这里插入图片描述
在这里插入图片描述

当我们向文件写入的时候,最终是不是向磁盘写入?

因为磁盘是硬件,所以只有OS有资格向硬件写入!

那么能绕开操作系统吗?

答:不能!那么所有上层的访问文件的操作,都必须贯穿操作系统!

操作系统是如何被上层使用的呢?

因为操作系统不相信任何人,所以必须使用操作系统提供的相关系统调用!

那么为什么要进行封装文件操作接口呢?

  • 原生系统接口,使用成本比较高!
  • 语言不具备跨平台性!

那么封装是如何解决跨平台性的问题呢?

  • 穷举所有底层的接口+条件编译!

C库提供的文件访问接口是来自于系统调用!
那么就能解释不同的语言有不同的文件访问接口!!
所以无论什么语言的底层的接口是不变的!

所以这就要求我们必须学习文件级别的系统接口!

总结

stdin&stdout&stderr

  • C默认会打开三个输入输出流,分别是stdin,stdout,stderr
  • 仔细观察发现,这三个流的类型都是FILE*,fopen返回值类型,文件指针

打开文件的方式

在这里插入图片描述

三、系统文件I/O

接口介绍

open介绍

在这里插入图片描述

返回值

在这里插入图片描述

打开文件的选项

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_CREAT!这些都是宏!

系统传递标记位,使用位图结构来进行传递的!

每一个宏标记,一般只需要有一个比特位为1,并且和其他宏对于的值不能重叠。

代码模拟实现

#include<stdio.h>

#define PRINT_A 0x1
#define PRINT_B 0x2
#define PRINT_C 0x4
#define PRINT_D 0x8
#define PRINT_DFL 0x0


void Show(int flags)
{
    if(flags&PRINT_A)
        printf("hello A\n");
    
    if(flags&PRINT_B)
    printf("hello B\n");
    
    if(flags&PRINT_C)
    printf("hello C\n");
    
    if(flags&PRINT_D)
    printf("hello D\n");

    if(flags==PRINT_DFL)
        printf("hello Default\n");

}

int main()
{
    Show(PRINT_DFL);
    Show(PRINT_A);
    Show(PRINT_B);
    Show(PRINT_A|PRINT_B);
    Show(PRINT_C|PRINT_D);
    Show(PRINT_A|PRINT_B|PRINT_C|PRINT_D);
	return 0;
}

我们通过传入不同的选项,打印出不同的语句。

在这里插入图片描述

使用open接口

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

int main()
{
  int fd = open("log.txt",O_WRONLY|O_CREAT);
  if(fd<0)
  {
    perror("open error");
    return 1;
  }
  printf("fd=%d\n",fd);

      return 0;
}

在这里插入图片描述
在这里插入图片描述
所以我们要打开曾经不存在的文件,我们要用到第二个open函数,带有权限的设置!

int open(const char *pathname, int flags, mode_t mode);
int fd = open(“log.txt”,O_WRONLY|O_CREAT,0666);

在这里插入图片描述
可以看到我们创建文件的权限是0666,可是实际显示的是0664呢?

这就和我们之前学的掩码umask有联系了!

在这里插入图片描述

不清楚的话可以去看看这篇博客权限的理解!

close

在这里插入图片描述

write

在这里插入图片描述

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

int main()
{
    int fd = open("log.txt",O_WRONLY|O_CREAT,0666);
    if(fd<0)
    {
      perror("open error");
      return 1;
    }
    printf("fd=%d\n",fd);
    int cnt=0;
    const char* str="hello file!\n";
    while(cnt<5)
    {
      write(fd,str,strlen(str));
      cnt++;
    }

    close(fd);
    return 0;
}

在这里插入图片描述
C在w方式打开文件的时候,会清空的!

int main()
{
    int fd = open("log.txt",O_WRONLY|O_CREAT,0666);
    if(fd<0)
    {
      perror("open error");
      return 1;
    }
    printf("fd=%d\n",fd);
    int cnt=0;
    const char* str="aaaa";
    //const char* str="hello file!\n";
    while(cnt<5)
    {
      write(fd,str,strlen(str));
      cnt++;
    }

    close(fd);
    return 0;
}

修改我们的代码后

在这里插入图片描述
我们发现此处直接覆盖曾经的数据,但是曾经的数据为什么保留呢了?

因为我们没有带有截断选项O_TRUNC

在这里插入图片描述

接下来我们带上这个选项后

int main()
{
    int fd = open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
    if(fd<0)
    {
      perror("open error");
      return 1;
    }
    printf("fd=%d\n",fd);
    int cnt=0;
    const char* str="hello rose!\n";
    while(cnt<5)
    {
      write(fd,str,strlen(str));
      cnt++;
    }

    close(fd);
    return 0;
}

在这里插入图片描述
现在我们就发现之前的数据被截断了!

所以我们现在可以类比与C语言的fopen,底层的open的选项就是"O_WRONLY|O_CREAT|O_TRUNC"!!!

接下来我们验证追加选项"O_APPEND"
代码如下

int main()
{
    int fd = open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
    if(fd<0)
    {
      perror("open error");
      return 1;
    }
    printf("fd=%d\n",fd);
    int cnt=0;
    const char* str="hello hulu!\n";
    while(cnt<5)
    {
      write(fd,str,strlen(str));
      cnt++;
    }

    close(fd);
    return 0;
}

在这里插入图片描述

read

我们打开文件就默认他是存在的,不需要携带"O_CREAT"选项
如果文件不存在会返回-1;
在这里插入图片描述

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define NUM 1024
int main()
{
    int fd = open("log.txt",O_RDONLY);
    if(fd<0)
    {
      perror("open error");
      return 1;
    }
    printf("fd=%d\n",fd);
    char buffer[NUM];
    while(1)
    {
      ssize_t s=read(fd,buffer,sizeof(buffer)-1);
      if(s>0)
      {
        buffer[s]='\0';
        printf("%s",buffer);
      }
      else
        break;
    }

    close(fd);
    return 0;
}

在这里插入图片描述

四、文件描述符

在上面的实验中我们了解到打开文件后返回给文件描述符fd=3,这是为什么?

接下来先来看代码,让我们去了解文件描述符

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
    int fda=open("loga.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
    int fdb=open("logb.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
    int fdc=open("logc.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
    int fdd=open("logd.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
    int fde=open("loge.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
    printf("fda=%d\n",fda);
    printf("fdb=%d\n",fdb);
    printf("fdc=%d\n",fdc);
    printf("fdd=%d\n",fdd);
    printf("fde=%d\n",fde);
    return 0;
}

在这里插入图片描述

  1. 为什么文件描述符默认是从3开始的呢?那么0,1,2去哪了?
    因为0,1,2被默认打开了
  • 0:标准输入,键盘

  • 1:标准输出,显示器

  • 2:标准错误,显示器
    在这里插入图片描述
    这就与我们C语言联系起来了!因为C语言封装的系统接口。

    首先我们之前在C语言中学到FILE*–>文件指针—>FILE是什么呢?—>C语言提供的结构体!–>封装了多个成员

    因为对于文件操作而言,系统接口只认识fd;(FILE内部必定封装了fd)

  1. 0,1,2,3,4…,我们之前见过什么样的数据是这个样子的呢?

    这和我们之前学习的C/C++的数组下标相似

    进程:内存文件的关系—>内存—>被打开的文件是存在内存里面的!!!

    一个进程可不可以打开多个文件?–>当然可以,所以在内核中,进程:打开的文件=1:n–>所以系统在运行中,有可能会存在大量的被打开的文件!—>OS要不要对这些被打开的文件进行管理呢??—>操作系统如何管理这些被打开的文件呢??—>答案是先描述,在组织。

    一个文件被打开,在内核中,要创建该被打开的文件的内核数据结构—先描述

    在这里插入图片描述
    在这里插入图片描述
    那么进程如何和打开的文件建立映射关系呢??

在这里插入图片描述

先验证0,1,2就是标准的IO

标准输入流

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

int main()
{
  char buffer[1024];
  ssize_t s=read(0,buffer,sizeof(buffer)-1);
  if(s>0)
  {
    buffer[s]='\0';
    printf("echo:%s",buffer);
  }
  return 0;
} 

在这里插入图片描述

标准输出流

int main()
{
  const char* s="hello write!\n";
  write(1,s,strlen(s));
} 

在这里插入图片描述

标准错误流

int main()
{
  const char* s="hello write!\n";
  write(2,s,strlen(s));
} 

在这里插入图片描述
我们看出标准错误流也打印到了显示器上面。

至于标准输出和标准错误的区别,我们稍后带大家了解。

验证0,1,2和stdin,stdout,stderr的对应关系


int main()
{
    printf("stdin: %d\n",stdin->_fileno);
    printf("stdout: %d\n",stdout->_fileno);
    printf("stderr: %d\n",stderr->_fileno);
} 

在这里插入图片描述

由上面的实验我们可以得出,FILE结构体中的fileno就是封装了文件描述符fd!!!

在这里插入图片描述

0,1,2—>stdin,stdout,stderr—>键盘,显示器,显示器(这些都是硬件呀!)也用你上面的struct file来标识对应的文件吗??
在这里插入图片描述
如何证明呢?

我来带大家看看LInux下的内核结构!!!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文件描述符的分配规则

int main()
{
    close(0);
    //close(1);
    //close(2);
    int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
    if(fd<0)
    {
        perror("open");
        return 1;
    }
    printf("fd=%d\n",fd);
} 

在这里插入图片描述
我们发现把1关掉后什么都没有了!

因为printf->stdout->1虽然不在指向对应的显示器了,但是已经指向了log.txt的底层struct_file对象!

在这里插入图片描述

遍历fd_array[],找到最小的没有被使用的下标,分配给新的文件!!


总结

下小节我来给大家讲述关于重定向的本质,和缓冲区的概念,等讲完重定向后,我们再把myshell完善!
(本章完!)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2814.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于寄生-捕食算法的函数寻优算法

文章目录一、理论基础1、寄生-捕食算法&#xff08;1&#xff09;初始化&#xff08;2&#xff09;筑巢阶段(鸟窝)&#xff08;3&#xff09;寄生阶段(乌鸦-布谷鸟)&#xff08;4&#xff09;捕食阶段(乌鸦-猫)2、PPA算法伪代码二、仿真实验与结果分析三、参考文献一、理论基础…

QCC51XX---QACT用户指南

更新记录链接:QCC51XX---系统学习目录_嵌入式学习_force的博客-CSDN博客 QACT安装包不要放在有中文路径下,否则—直会安装报错。适用V7,V7.1 V7.2版本 打开QACT. 打开QACT. 点击 connection configuration 进去之后 点击1,然后点2,选择kalaccess.dll文件, workspace …

【C++】vector的模拟实现不会怎么办?看过来

&#x1f308;欢迎来到C专栏~~vector的模拟实现 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort&#x1f393;&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&#x1f914;&…

MySQL是如何保证主从一致的

一&#xff1a;什么是binlog Binary log(二进制日志)&#xff0c;简称Binlog。 Binlog是记录所以数据表结构变更以及表数据修改的二进制日志&#xff0c;不会记录select和show这类操作。Binlog是以事件形式记录&#xff0c;还包括语句所执行的消耗时间。Binlog是MySql Server自…

0082 时间复杂度,冒泡排序

/* * 排序也称排序算法&#xff08;Sort Algorithm&#xff09; * 排序是将一组数据&#xff0c;依指定的顺序进行排列的过程。 * * 排序分类 * 1.内部排序&#xff1a;将需要处理的所有数据都加载到内存存储器中进行排序&#xff08;使用内存&#xff09; * 插…

Keil MDK的sct分散加载文件详解

sct 分散加载文件简介 MDK 生成一个以工程名命名的后缀为 *.sct 的分散加载文件 (Linker Control File&#xff0c;scatter loading)&#xff0c;链接器根据该文件的配置分配各个节区地址&#xff0c;生成分散加载代码&#xff0c;因此我们通过修改该文件可以定制具体节区的存…

Spring源码:Spring源码阅读环境搭建

本篇内容包括&#xff1a;Mac 环境下 gradle 的安装和配置、源码克隆、新建测试类&#xff0c;测试Spring源码 等内容&#xff01; 第一步&#xff1a;Mac 环境下 gradle 的安装和配置 1、下载安装包 # 到 GitHub 的 Spring 仓库选定 Spring 版本&#xff0c;查看对应版本 Sp…

Linux项目自动化构建工具make/makefile

1.背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的源文件不计其数&#xff0c;其按类型&#xff0c;功能&#xff0c;模块分别放在若干目录中&#xff0c;makefile定义了一系列的规则来制定&#xff0c;那些文件需要先编译&a…

C艹笔记--面向对象程序设计

文章目录类与对象简介类与结构的区别定义成员函数继承继承小总结[C中::和:&#xff0c; .和->的作用和区别](https://zhuanlan.zhihu.com/p/165992745)符号::和&#xff1a;的作用和区别:::一般用来表示继承符号.和->的作用和区别#include#include""和#include…

STM32入门——基本 GPIO 的输出控制

文章目录1 什么是 GPIO &#xff1f;1.1 GPIO 简介1.2 GPIO 硬件解析1.2.1 保护二极管1.2.2 P-MOS、N-MOS 管1.2.3 数据输入输出寄存器1.2.4 复用功能输出1.2.5 模拟输入输出1.3 GPIO 的工作模式1.3.1 输入模式 (模拟/浮空/上拉/下拉)1.3.2 输出模式 (推挽/开漏)1.3.3 复用功能…

基于Nodejs+vue开发实现酒店管理系统

作者简介&#xff1a;Java、前端、Pythone开发多年&#xff0c;做过高程&#xff0c;项目经理&#xff0c;架构师 主要内容&#xff1a;Java项目开发、毕业设计开发、面试技术整理、最新技术分享 项目编号&#xff1a;BS-QD-KS-002 一&#xff0c;项目简介 本项目使用纯前端技…

mysql约束

文章目录mysql约束非空约束唯一性约束主键约束使用自增列&#xff1a;AUTO_INCREMENTFOREIGN KEY约束CHECK约束mysql约束 为什么需要约束&#xff1f;为了保证数据的完整性什么叫约束&#xff1f;对表中字段的限制约束的分类&#xff1a; 角度1&#xff1a;约束的字段个数&…

吴峰光杀进 Linux 内核

【编者按】吴峰光&#xff0c;Linux 内核守护者&#xff0c;学生时代被同学戏称为“老神仙”&#xff0c;两耳不闻窗外事&#xff0c;一心只搞 Linux。吴峰光的 Linux 内核之路&#xff0c;是天赋、兴趣、耐心、坚持的综合&#xff0c;这从一个补丁前后迭代了 16 个版本后还进行…

【初识Netty使用Netty实现简单的客户端与服务端的通信操作Netty框架中一些重要的类以及方法的解析】

一.Netty是什么&#xff1f; Netty 由 Trustin Lee(韩国&#xff0c;Line 公司)2004 年开发 本质&#xff1a;网络应用程序框架 实现&#xff1a;异步、事件驱动 特性&#xff1a;高性能、可维护、快速开发 用途&#xff1a;开发服务器和客户端 Netty的性能很高&#xff0…

字符串匹配算法(BF、KMP)

目录 1、暴力匹配&#xff08;BF&#xff09;算法 2、KMP算法 1、暴力匹配&#xff08;BF&#xff09;算法 BF算法&#xff0c;即暴力(Brute Force)算法&#xff0c;是普通的模式匹配算法&#xff0c;BF算法的思想就是将目标串S的第一个字符与模式串T 的第一个字符进行匹配&a…

【树莓派不吃灰】配置samba,文件夹目录配置在闲置U盘,实现局域网文件共享

目录1. 前言2. 安装 Samba2.1 安装samba 和 samba-common-bin2.2 配置/etc/samba/smb.conf文件2.3 配置登录账号和密码2.4 重启 samba 服务2.5 回到windows&#xff0c;就可以在网络当中发现共享的文件夹3. 在Windows上挂载smb的共享目录3.1 打开windows的smb功能3.2 添加网络映…

Java --- springMVC实现RESTFul案例

一、使用springMVC实现RESTFul小案例 1.1、项目目录图&#xff1a; 1.2、代码实现&#xff1a; pom.xml文件&#xff1a; <packaging>war</packaging><!--添加依赖--><dependencies><!--SpringMVC--><dependency><groupId>org.spr…

黑马C++ 03 提高4 —— STL常用容器_string容器/vector容器/deque容器

文章目录一、string容器1. string基本概念2. string构造函数3. string赋值操作4. string字符串拼接5. string查找和替换6. string字符串比较7. string字符存取8. string字符串的插入和删除9. string子串二、vector容器(尾插尾删)1. vector基本概念2. vector构造函数3. vector赋…

【目标检测】基于yolov3的血细胞检测(无bug教程+附代码+数据集)

多的不说,少的不唠,先看检测效果图: 共检测三类:红细胞RBC、白细胞WBC、血小板Platelets Hello,大家好,我是augustqi。今天给大家带来的保姆级教程是:基于yolov3的血细胞检测(无bug教程+附代码+数据集) 1.项目背景 在上一期的教程中,我们基于yolov3训练了一个红细…

韩顺平linux(1-11小节)

运维工程师 服务器的规划、调试优化、日常监控、故障处理 物联网linux Linux主要指的是内核 ubuntu&#xff08;python偏爱&#xff09;&#xff0c;centos 发行版本 内核进行包装 1.4服务器领域 linux在服务器领域的应用是最强的。 linux免费、稳定、高效等特点在这里得到了很…