聊聊操作系统-进程与线程
来源:互联网 发布:php课程简介 编辑:程序博客网 时间:2024/06/14 04:00
工作也有两年了,在研究很多项目时发现很多问题追根溯源都会到计算机底层的知识,也是越来越发现编程语言只是一层外壳,这个外壳需要去和操作系统协商使用后者管理的的计算机资源,包括存储资源和计算资源。如果计算机底层知识不牢靠,遇到一些问题还真是不好分析,也很容易成为职业上升的瓶颈。现在回想起大学时学习这么课时是比较抽象的,那时没有太多编程经验,知识很难落地,造成了只知其然不知其所以然的情况,所以现在重新梳理一下计算机操作系统的一些底层知识,在这里记录一下。
我们说操作系统是运行在内核态的一套软件,它的主要作用是隐藏硬件,呈现给程序或者程序员良好的、优雅的、一致的计算机抽象模型,并且负责将计算机的存储资源和计算资源有序分配给应用程序。
这篇文章我先总结一下计算机的进程和线程相关知识。
(一)进程相关
(1)什么是进程
我个人觉得理解进程需要从两个角度来看,第一从动态角度讲“进程”是一个正在运行程序的实例,它包括程序计数器、寄存器和变量的当前值。
第二从静态角度讲“进程”是一个容器,该容器用于聚集相关的计算机资源,线程,打开的文件等。
在单CPU系统中,CPU在多个进程之间快速来运行的,这是一种伪并行模式。所以在对进程编程时绝不能对时序做任何确定的假设。
(2)什么时候进程被创建
- 系统初始化:必须linux系统启动时init进程会被创建,所有其他进程都是这个进程的子进程
- 正在运行的进程通过系统调用创建另一个进程:比如我在这篇博客Redis持久化机制原理分析中提到的Redis主进程通过系统调用fork创建一个子进程来执行RDB
- 用户请求创建一个进程:如用户在Window系统中双击桌面上的word快捷方式
在linux系统中进程的创建时通过系统调用fork进行创建,调用fork的进程成为父进程,被创建的进程成为子进程。父进程和子进程都有私有的内存地址空间,如果父进程修改了属于自己的变量,这个修改对子进程是不可见的。
pid = fork(); //创建成功pid>0,实际为子进程的pidif(pid < 0){hand_error(); // pid <0 创建进程失败} else if (pid > 0){// 父进程逻辑} else {// 子进程逻辑}
系统调用fork调用一次返回两次,在父进程中返回子进程的pid,在子进程中返回0,可以通过这个返回值来判断执行父进程代码还是子进程代码,系统调用fork创建一个完全与父进程相同的子进程,包括相同的文件描述符,相同的寄存器内容和其他所有内容,当然现代操作系统使用了 写时复制Copy on write的方式来优化fork的性能,fork刚创建的子进程采用了共享的方式,只用指针指向了父进程的物理资源。当子进程真正要对某些物理资源写操作时,才会真正的复制一块物理资源来供子进程使用。这样就极大的优化了fork的性能。
如果子进程想获得自己的pid可以通过getpid系统调用。
(3)什么时候进程退出
- 正常运行结束程序退出(正常情况):在linux系统中程序运行结束后调用exit退出
- 出现错误程序退出(正常情况):编译一个找不到的文件等
- 严重错误(非正常情况):引用不存在的内存、除数为0等情况
- 被其他用户杀死(非正常情况):在linux系统中用户kill命令杀死进程
(4)进程层次结构
在unix系统中当一个进程创建了另一个进程之后,我们称前者为父进程后者为子进程,一个父进程有0个或多个子进程,这些进程构成了一个进程树。
在Windows系统中进程没有父子进程的层次结构的概念。
(二)线程相关
(1)什么是线程
我们常说线程是cpu调用的最小单位,每个进程中至少有一个线程,当然这只是从cpu调度的角度来看线程;如果从存储资源的角度来看线程的话,
线程解决的最大问题就是它可以很简单地表示共享资源的问题,这里说的资源指的是存储器资源,资源最后都会加载到物理内存,一个进程的所有线程都是共享这个进程的同一个虚拟地址空间的,也就是说从线程的角度来说,它们看到的物理资源都是一样的,这样就可以通过共享变量的方式来表示共享资源,也就是直接共享内存的方式解决了线程通信的问题。
理论上说Linux内核是没有线程这个概念的,只有内核调度实体(Kernal Scheduling Entry, KSE)这个概念。linux的线程本质上是一种轻量级的进程,是通过clone系统调用来创建的。
(2)线程的创建
在文章上面我们提到通过系统调用fork创建进程,如果说父进程是一个多线程运行模式,那么现在面临的一个问题是:是不是需要在新创建的子进程中创建父进程中的所有线程,如果全部创建假设父进程中一个线程正在等待输入而阻塞,那么等输入完成之后数据是传给父进程还是子进程的线程;如果不全创建,如果父进程中一个线程A持有锁,线程B等待锁,那么子进程中如果没有创建线程A,那么线程B在子进程中将永远阻塞。
为了解决这个问题,Linux系统引入了一个强大的系统调用clone
pid=clone(function,stack_ptr,sharing_flags,args)
function:为新建线程的执行入口
args:为新创建线程执行参数
stack_ptr:为新创建线程的私有堆栈指针
sharing_flags:这个是一个位图不同的值可以指定,新创建线程是复制调用clone函数的线程数据结构,还是共享这部分数据结构。
具体参数值可以参考下图:
(3)线程没有层级结构而是对等的关系
进程采用父子结构,init进程是最顶端的父进程,其他进程都是从init进程派生出来的。这样就很容易理解进程是如何共享内核的代码和数据的了。
而线程采用对等结构,即线程没有父子的概念,所有线程都属于同一个线程组,线程组的组号等于第一个线程的线程号。
- 聊聊操作系统-进程与线程
- 从操作系统的角度,聊聊线程与进程
- 操作系统--进程与线程
- 【操作系统】 进程与线程
- 操作系统 *** 进程与线程
- 操作系统--进程与线程
- [操作系统] 进程与线程
- 操作系统进程与线程
- 操作系统--进程与线程
- 【操作系统】进程与线程
- [操作系统] 进程与线程
- 操作系统中的进程与线程
- 操作系统中的进程与线程
- 操作系统中的进程与线程
- 操作系统中的进程与线程
- 操作系统中的进程与线程
- 操作系统中的进程与线程
- 操作系统中的进程与线程
- stm32 通过stm32 cubemx配置中断函数(io中断)
- Docker容器硬盘动态扩容
- LaTeX 之 \label 的运用 -------图表,公式 的引用
- Vuex简单入门
- POJ 3070-Fibonacci (矩阵快速幂)
- 聊聊操作系统-进程与线程
- Python+selenium环境搭建&初步实践
- 揭秘HTTPS的"秘密
- 异常是什么
- SSM框架——使用MyBatis Generator自动创建代码
- 【代码笔记】iOS-二维码
- CSS3新特性之box-reflect倒影
- Windos下如何配置java环境变量
- ssh与ssm部分区别小计