文件读取 linux_Linux 进程、线程、文件描述符的底层原理

说到进程,恐怕面试中最常见的问题就是线程和进程的关系了,那么先说一下答案:在 Linux 系统中,进程和线程几乎没有区别

Linux 中的进程其实就是一个数据结构,顺带可以理解文件描述符、重定向、管道命令的底层工作原理,最后我们从操作系统的角度看看为什么说线程和进程基本没有区别。

一、进程是什么

首先,抽象地来说,我们的计算机就是这个东西:

79967b7967a19ecd13ff1479675bd5a7.png

这个大的矩形表示计算机的内存空间,其中的小矩形代表进程,左下角的圆形表示磁盘,右下角的图形表示一些输入输出设备,比如鼠标键盘显示器等等。另外,注意到内存空间被划分为了两块,上半部分表示用户空间,下半部分表示内核空间

用户空间装着用户进程需要使用的资源,比如你在程序代码里开一个数组,这个数组肯定存在用户空间;内核空间存放内核进程需要加载的系统资源,这一些资源一般是不允许用户访问的。但是注意有的用户进程会共享一些内核空间的资源,比如一些动态链接库等等。

我们用 C 语言写一个 hello 程序,编译后得到一个可执行文件,在命令行运行就可以打印出一句 hello world,然后程序退出。在操作系统层面,就是新建了一个进程,这个进程将我们编译出来的可执行文件读入内存空间,然后执行,最后退出。

你编译好的那个可执行程序只是一个文件,不是进程,可执行文件必须要载入内存,包装成一个进程才能真正跑起来。进程是要依靠操作系统创建的,每个进程都有它的固有属性,比如进程号(PID)、进程状态、打开的文件等等,进程创建好之后,读入你的程序,你的程序才被系统执行。

那么,操作系统是如何创建进程的呢?对于操作系统,进程就是一个数据结构,我们直接来看 Linux 的源码:

struct task_struct {
    // 进程状态
    long              state;
    // 虚拟内存结构体
    struct mm_struct  *mm;
    // 进程号
    pid_t             pid;
    // 指向父进程的指针
    struct task_struct   *parent;
    // 子进程列表
    struct list_head      children;
    // 存放文件系统信息的指针
    struct fs_struct      *fs;
    // 一个数组,包含该进程打开的文件指针
    struct files_struct   *files;
};

task_struct就是 Linux 内核对于一个进程的描述,也可以称为「进程描述符」。源码比较复杂,我这里就截取了一小部分比较常见的。

我们主要聊聊mm指针和files指针。mm指向的是进程的虚拟内存,也就是载入资源和可执行文件的地方;files指针指向一个数组,这个数组里装着所有该进程打开的文件的指针。

二、文件描述符是什么

先说files,它是一个文件指针数组。一般来说,一个进程会从files[0]读取输入,将输出写入files[1],将错误信息写入files[2]

举个例子,以我们的角度 C 语言的printf函数是向命令行打印字符,但是从进程的角度来看,就是向files[1]写入数据;同理,scanf函数就是进程试图从files[0]这个文件中读取数据。

每个进程被创建时,files的前三位被填入默认值,分别指向标准输入流、标准输出流、标准错误流。我们常说的「文件描述符」就是指这个文件指针数组的索引,所以程序的文件描述符默认情况下 0 是输入,1 是输出,2 是错误。

我们可以重新画一幅图:

003beb463b812121b239f2dda1859780.png

对于一般的计算机,输入流是键盘,输出流是显示器,错误流也是显示器,所以现在这个进程和内核连了三根线。因为硬件都是由内核管理的,我们的进程需要通过「系统调用」让内核进程访问硬件资源。

PS:不要忘了,Linux 中一切都被抽象成文件,设备也是文件,可以进行读和写。

如果我们写的程序需要其他资源,比如打开一个文件进行读写,这也很简单,进行系统调用,让内核把文件打开,这个文件就会被放到files的第 4 个位置,对应文件描述符 3:

3c0c333bca36da979682af556d2b9157.png

明白了这个原理,输入重定向就很好理解了,程序想读取数据的时候就会去files[0]读取,所以我们只要把files[0]指向一个文件,那么程序就会从这个文件中读取数据,而不是从键盘:

8a9b3f6b8fb0ba46042196d9364dcf51.png

同理,输出重定向就是把files[1]指向一个文件,那么程序的输出就不会写入到显示器,而是写入到这个文件中:

8e97055a55c6174236a9d6fb851b1868.png

错误重定向也是一样的,就不再赘述。

管道符其实也是异曲同工,把一个进程的输出流和另一个进程的输入流接起一条「管道」,数据就在其中传递,不得不说这种设计思想真的很巧妙:

83739356f3e275867e0c3217e701b9cb.png

到这里,你可能也看出「Linux 中一切皆文件」设计思路的高明了,不管是设备、另一个进程、socket 套接字还是真正的文件,全部都可以读写,统一装进一个简单的files数组,进程通过简单的文件描述符访问相应资源,具体细节交于操作系统,有效解耦,优美高效。

三、线程是什么

首先要明确的是,多进程和多线程都是并发,都可以提高处理器的利用效率,所以现在的关键是,多线程和多进程有啥区别。

为什么说 Linux 中线程和进程基本没有区别呢,因为从 Linux 内核的角度来看,并没有把线程和进程区别对待。

我们知道系统调用fork()可以新建一个子进程,函数pthread()可以新建一个线程。但无论线程还是进程,都是用task_struct结构表示的,唯一的区别就是共享的数据区域不同

换句话说,线程看起来跟进程没有区别,只是线程的某些数据区域和其父进程是共享的,而子进程是拷贝副本,而不是共享。就比如说,mm结构和files结构在线程中都是共享的,我画两张图你就明白了:

705b3874f01c46c8148085c78ee44273.png

708d4be97513d0dad5d57048061ea032.png

所以说,我们的多线程程序要利用锁机制,避免多个线程同时往同一区域写入数据,否则可能造成数据错乱。

那么你可能问,既然进程和线程差不多,而且多进程数据不共享,即不存在数据错乱的问题,为什么多线程的使用比多进程普遍得多呢

因为现实中数据共享的并发更普遍呀,比如十个人同时从一个账户取十元,我们希望的是这个共享账户的余额正确减少一百元,而不是希望每人获得一个账户的拷贝,每个拷贝账户减少十元。

当然,必须要说明的是,只有 Linux 系统将线程看做共享数据的进程,不对其做特殊看待,其他的很多操作系统是对线程和进程区别对待的,线程有其特有的数据结构,我个人认为不如 Linux 的这种设计简洁,增加了系统的复杂度。

在 Linux 中新建线程和进程的效率都是很高的,对于新建进程时内存区域拷贝的问题,Linux 采用了 copy-on-write 的策略优化,也就是并不真正复制父进程的内存空间,而是等到需要写操作时才去复制。所以 Linux 中新建进程和新建线程都是很迅速的

以上就是全部内容,如果有帮助的话,不妨点个在看,我看看操作系统相关的文章阅读数据怎么样,不错的话以后可以再写写操作系统方面的小知识。

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

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

相关文章

pom.xml中依赖的<optional>true</optional>标签

项目A的pom.xml文件中某个依赖添加了true标签&#xff0c;如下所示 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional><!--防止将此依赖传递到其它模块中--> <…

中如何拉取git代码_git使用教程4pycharm拉取git仓库项目代码

前言当我们在github上看到别人写的项目&#xff0c;想拉到本地学习下。如何用pycharm把git仓库的代码拉取到本地电脑呢&#xff1f;环境准备&#xff1a;1.本地电脑已经安装了git2.已经注册过github账号3.pycharmpycharm配置先自己注册github账号&#xff0c;本地安装git环境&a…

lombok之@Slf4j注解

应用背景&#xff1a;如果不想每次都在实体类中写private final Logger logger LoggerFactory.getLogger(当前类名.class); 可以使用注解Slf4j Sl4j注解是是属于lombok中的一个注解&#xff0c;所以在使用该注解之前一定要引入lombok的依赖&#xff0c;同时IDEA还需要已经安装…

回归指令_用一条指令在新款 Mac 上找回经典的开机启动声

多年使用 Mac 的老用户肯定知道&#xff0c;Mac 在启动时会发出一声「噔&#xff5e;」的启动声音&#xff0c;伴随着 Apple logo 在屏幕上亮起&#xff0c;Mac 正在安全地启动。但如果你在最近几年换了新款 Mac 电脑&#xff0c;你会发现这个熟悉的启动音消失不见了。就像 Mag…

lombok常用注解整理

idea需要先安装好lombok插件&#xff0c;不会的可以参考这里https://blog.csdn.net/qq_43842093/article/details/115426380 引入依赖 <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency><groupId>org.projectlombok<…

C/C++ const

看微软的文章&#xff1a; const (C) | Microsoft Learn

gpu浮点计算能力floaps_基准测试移动 GPU 中的浮点精度 - 第 2 部分

投稿人&#xff1a;&#xff0c;2013年6月11日这是有关GPU中浮点质量的一系列博文中的第二篇&#xff0c;我的灵感源自 发表于 的文章。在中&#xff0c;我宣称许多程序员其实并不真正了解浮点数字&#xff0c;也指出如果您准备将它用于比较棘手的东西&#xff0c;那么最好先准…

@DateTimeFormat注解

DateTimeFormat注解位于spring-context-5.0.10.RELEASE.jar包中 import org.springframework.format.annotation.DateTimeFormat; import java.util.Date;public class User {//姓名private String name;//出生日期DateTimeFormat(pattern "yyyy-MM-dd")private Da…

h5页面如何预览excel文件_移动端页面,如何解析预览 word/excel/PDF文件?

展开全部利用Office2007以上版本的一个PDF插件SaveAsPDFandXPS.exe可以导出PDF文件&#xff0c;然后再利用免费的swftools.exe工具生成swf格式的Flash文件&#xff0c;网页中加载flexpaper免费开源工具(32313133353236313431303231363533e59b9ee7ad9431333365633934有广告)实现…

Gson之toJson和fromJson方法

Gson是Google的一个开源项目&#xff0c;可以将Java对象转换成JSON&#xff0c;也可能将JSON转换成Java对象。 Gson里最重要的对象有2个Gson 和 GsonBuilder Gson有2个最基本的方法 toJson() – 转换java 对象到JSONfromJson() – 转换JSON到java对象 引入依赖&#xff1a;…

android 手机内存uri_android 的各种目录详解

App独立文件app独立文件就是那些不依赖于某特定app的文件这类文件当我们删除应用之后&#xff0c;还应该保留在手机上的&#xff0c;例如拍照的照片&#xff0c;不应该随着删除应用而被删除掉。这类文件应该是随着app删除而一起删除的&#xff0c;它们可以被存储在两个地方&…

python3 tkinter详解_python tkinter基本属性详解

1.外形尺寸尺寸单位&#xff1a;只用默认的像素或者其他字符类的值&#xff01;&#xff0c;不要用英寸毫米之类的内容。btn tkinter.Button(root,text 按钮)# 设置按钮尺寸&#xff0c;绝大多数默认单位是像素btn.pack(ipadx 100,ipady 20)2.坐标系btn tkinter.Button(ro…

Gson详解(二)

1.复杂Json转成对象 比如&#xff1a;jsonString:{“response”:{“content”:"\n\t",“msg”:“成功”,“code”:“0”,“data{“content”:”\n\t",“VIN”:“LDC12345678901234”},“cmd”:“ScanVINCode”}} Gson解析 Gson gson new Gson();roodBean gs…

linux mysql 备份脚本_linux 之mysql备份脚本

#date 2020.2.20#author zhang#描述 用户自己选择使用什么工具进行备份#$1 账户; $2 密码 $3 地址stty erase "^H"[ -f /etc/init.d/functions ] && . /etc/init.d/functions#备份文件夹路径backup_path"/usr/local/src/backup_mysql"[ -d $backup…

Java使用GSON对JSON进行解析——IDEA引入jar包方式

GSON GSON是Google公司开发的用于解析json的类库。可以很轻松地让程序员将java对象转换成JSON格式&#xff0c;或者将JSON格式的对象转换成Java对象。 使用方法很简单&#xff1a; 首先&#xff0c;需要将GSON类库的jar包引入到自己的IDE中&#xff0c;本教程使用IDEA为例子…

mysql查询姓王的信息代码_MySQL查询语句练习题

1.创建student和score表CREATE TABLE student (id INT(10) NOT NULL UNIQUE PRIMARY KEY ,name VARCHAR(20) NOT NULL ,sex VARCHAR(4) ,birth YEAR,department VARCHAR(20) ,address VARCHAR(50));创建score表。SQL代码如下&#xff1a;CREATE TABLE score (i…

IoT -- (四) 物联网系统架构介绍

物联网系统框架介绍 下面将谈到几个关键问题&#xff1a; 设备如何接入网络&#xff1f;设备间如何通信&#xff1f;物联网数据的用途?如何搭建起一个物联网系统框架呢&#xff1f;它的技术架构又是怎么样呢&#xff1f;物联网终端软件系统架构&#xff1f;物联网云平台系统…

mysql 删除not null_从MySQL的列中删除NOT NULL限制?

要从MySQL的列中删除NOT NULL限制&#xff0c;请使用ALTER命令。语法如下&#xff1a;ALTER TABLE yourTableName MODIFY COLUMN yourColumnName dataType;为了理解上述语法&#xff0c;让我们创建一个表。创建表的查询如下&#xff1a;mysql> create table NotNullDemo->…

IoT -- (五) IoT都有哪些通信协议

IOT都有哪些通信协议&#xff1f; 在物联网协议中&#xff0c;我们一般分为两大类&#xff0c;一类是传输协议&#xff0c;一类是通信协议。那么&#xff0c;物联网都有哪些通信协议呢? 在物联网协议中&#xff0c;我们一般分为两大类&#xff0c;一类是传输协议&#xff0c…

IoT -- (六) MQTT和CoAP对比分析

IoT物联网需要标准协议&#xff0c;针对小设备最有前景的两种是MQTT和CoAP。 MQTT和CoAP两者均&#xff1a; 开放标准&#xff1b; 比HTTP更适合于受限环境&#xff1b; 提供异步传输机制&#xff1b; 在IP上运行&#xff1b; 有很多种实现 MQTT在传输模式上更为灵活&am…