设为首页 加入收藏

TOP

进程和线程(一)
2023-07-23 13:29:41 】 浏览:53
Tags:程和线

为什么要引入进程?

我们知道,最早出现的OS是单道批处理系统,由于它是顺序执行程序的,即一个一个地按先到先执行的顺序依次执行。因此,CPU的高速性与I/O的低速性之间的矛盾很明显(IO低速是指这个程序在运行过程中需要频繁的访问IO资源,如需要输入数据,如读取硬盘文件,使用打印机,在这个程序进行IO操作的时候,一般来说不占用CPU资源,此时把CPU资源分配给其他的进程,这样就提高了CPU的利用率)。为了缓解这个矛盾,人们引入了多道批处理系统,该系统让程序并发执行,即在一个程序发起I/O请求时CPU不再选择等待I/O完成,而是转去执行下一个程序。

并发执行特点:

  • 间断性:多个程序宏观上并发,微观上轮流使用CPU,所以每一个程序都是走走停停,具有间断性,这样做提高了资源的利用率
  • 失去封闭性:多个程序共享系统的资源,所以资源的状态不专属于一个程序,失去了封闭性
  • 不可再现性:由于多个程序共享资源,每个程序都有对资源修改的权利,所以程序的输出结果是不一定的

操作系统设计的最终目的是不但要提高资源的利用率,而且还要具备封闭性和可再现性,如何解决封闭性的问题?

解决封闭性有两种方法

1.Bernstein条件:既然所有运行的程序不能并发运行,那么能否让一部分互不干扰的程序并发运行,在一部分程序上实现并发

具体内容:

其实要表达的意思是这样的,将程序的所有读的集合和写的集合挪列出来,如果两个程序仅仅只有读集有交集,说明两个程序是可以并发的,也就是说两个程序可以同时读,但一个读一个写和同时写都是不能并发的

2.引入进程

那么能否有一个东西将资源封闭起来呢?

进程就被引入了,进程是系统进行资源分配和处理机调度的独立单位,所以进程之间是可以并发的。(意思就是以前每个程序都共享所有的资源,现在就是让每个程序都只能拥有一部分资源,这就解决了封闭性,解决了封闭性也就实现了再现性)

进程 = 数据段 + 程序段 + PCB

为什么要引入线程?

进程是一个可拥有资源的独立单位,进程同时又是一个可独立调度和分派的基本单位。正是由于进程有这两个基本属性,才使进程成为一个能独立运行的基本单位,从而也就构成了进程并发执行的基础。

为使程序能并发执行,系统必须对进程进行以下的一系列操作:创建进程、撤销进程以及进程间切换。据此可知,由于进程是一个资源的拥有者,因而在创建、撤消和切换中,系统必须为之付出较大的时空开销。这就限制了系统中所设置进程的数目,而且进程切换也不宜过于频繁,从而限制了并发程度的进一步提高。

要设法将进程的上述两个属性分开,由OS分开处理,亦即并不把作为调度和分派的基本单位也同时作为拥有资源的单位,以做到“轻装上阵”,而对于拥有资源的基本单位,又不对之施以频繁的切换。正是在这种思想的指导下,形成了线程的概念。

自己理解:

  1. 尽可能使资源分配和CPU调度分开,因为,因为进程间切换需要保存进程CPU环境(栈、寄存器、页表和文件句柄等)以及新调度的进程CPU环境的设置,而线程切换保存和设置程序计数器、少量寄存器和栈的内容
  2. 应用程序功能越来越多,可以用多线程来实现(比如QQ可以用一个线程来接受文件,一个线程来进行语音通话,其实也可以通过创建新的进程来实现,第一点才是引入的根本目的)

进程、线程和协程的区别和联系

image

一个进程可以创建多少个线程?

进程的虚拟内存空间大小为4G,其中 3G-4G为内核空间,用户可用的的空间为3G,具体可以创建多少个要看线程的大小,Linux下一个线程大约为10M,可以创建300个线程左右。

虚拟地址

对于每一个进程都会对应一个虚拟地址空间,对于32位的操作系统(其指令的位数最大为32位,因此地址码最多32位),虚拟地址空间的大小为B即0~4GB的虚拟地址空间,其中内核空间为1GB,如下所示:

https://cdn.nlark.com/yuque/0/2022/png/28379192/1651891572065-7eb9f3ca-713e-45ad-af25-9a30b3bf5a6d.png

每一个进程的进程控制块PCB都位于内核区,在每一个进程的PCB中有一个文件描述符表(是一个数组),用于标记该进程所打开的所有文件。从文件描述符表可以看出每一个进程最多能打开1024个文件,其中有三个文件默认是一直处于打开状态的(即进程创建完成时就处于打开状态),分别是:标准输入 STDIN_FILENO,其文件描述符为0;标准输出 STDOUT_FILENO,其文件描述符为1;错误输出 STDERR_FILENO,其文件描述符为2,其中文件描述符0和1可以省略不写。供我们用户打开的文件,只能够占据从3开始的位置(即其文件描述符为3以后的数字,3~1023)。每打开一个文件就会占用一个文件描述符,且使用的是空闲的最小的一个文件描述符。

https://cdn.nlark.com/yuque/0/2022/png/28379192/1651891587692-ad97a754-de67-44af-ac42-70112c32ea76.png

Linux下可执行文件的格式为ELF:[root@localhost Calc]# file zsx

zsx: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x14ef2d34126e7c54141b73c31968bd825ca522ba, not stripped //可以看出zsx为64位(即机器指令位数为64位,OS位数)的可执行文件,其格式为ELF。

对于每一个程序在执行时(如上图中的a.out),此时会产生一个相应的进程,系统都会自动为其分配一个04G的虚拟地址空间,其中1G的内核空间用于:进程管理、内存管理、设备管理和虚拟文件系统等。下面详细介绍03G的用户空间。

强调一点:以下说明的各段都是与编程相关的,不包括虚拟地址空间的全部。

0~3G的用户空间。从小到大(从下往上)依次为:保留区(受保护的地址)、代码段、数据段(.data段)、.bss段、堆空间、内存映射段、栈空间、命令行参数和环境变量。下面依次对每一个段做简单的介绍:

1.保留区(受保护的地址)

保留区即为受保护的地址,大小为0~4K,位于虚拟地址空间的最低部分,未赋予物理地址(不会与内存地址相对应,因此其不会放任何内容)。任何对它的引用都是非法的,用于捕捉使用空指针和小整型值指针引用内存的异常情况。大多数操作系统中,极小的地址通常都是不允许访问的,如NULL。C语言将无效指针赋值为0也是出于这种考虑,因为0地址上正常情况下不会存放有效的可访问数据。将指针赋值为0,意味着该指针将永远不会被使用,从而不会出现野指针情况。#define NULL 0 与 #define NULL (void)0 在C语言中是等效的,而在C++中,只能用#define NULL 0,后面 #define NULL (void)0的使用会出错。

2.代码段

代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。一般C语言执行语句都编译成机器代码保存在代码段。通常代码段是可共享的,因此频繁执行的程序只需要在内存中拥有一份拷贝即可。代码段通常属于只读,以防止其他程序意外地修改其指令(对该段的写操作将导致段错误)。某些架构也允许代码段为可写,即允许修改程序。

3.数据段(.data段)

数据段通常用于存放程序中已初始化的全局变量和静态局部变量。数据段属于静态内存分配(静态存储区),可读可写。由于全局变量未初始化时,其默认值为0,因此值为0的全局变量位于.bbs段(不位于数据段)

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇基于容器的方式做一个apache编译.. 下一篇基于容器部署一个web站点

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目