抛开技术细节,从使用角度来讲:
1. 在单核计算机里,有一个资源是无法被多个程序并行使用的: cpu
Cpu,承担计算任务,单个cpu一次只能运行一个任务。
没有操作系统的情况下,一个程序一直独占着全部cpu
若有2个任务来共享一cpu,需要程序员安排程序运行计划,使得某时刻cpu被A程序独占,下一时刻cpu被程序B独占
这种安排计划就是OS核心组件,被命名为‘scheduler’,调度器,负责把cpu的运行拆分成一段一段的运行片,轮流分给不同的程序去使用,在宏观上,由于分配切换的速度极快,制造出多程序并行在一个cpu上的假象。【真正的并行执行多任务只能在多核cpu上实现】
2. 单核计算机里,有一个资源可以被多个程序共享: 内存
在一个只有调度器,没有内存管理组件的OS里,程序员手动为每个程序安排运行的空间:程序A使用物理地址0x00-0xff,程序B使用物理地址0x100-0x1ff。
这种方式的问题是,每个程序都要协商好怎么使用同一个内存上的不同空间,软件系统与硬件系统千差万别,这种高度定制化的方案没有可行性。因此,引入了“虚拟地址”的概念,从3方面着手来解决这个问题:
1) 硬件上,CPU增加了一个专门的模块叫MMU,负责转换虚拟地址和物理地址。
2) OS上,OS增加一个核心组件,内存管理模块,管理物理内存、虚拟内存相关的一系列事物
3) 应用程序上,发现一个叫做进程的模型,每个进程都用完全一样的虚拟内存地址,然后经由操作系统和硬件MMU协作,映射到不同的物理地址空间。不同的进程,都有各自独立的物理内存空间,不使用特殊手段,是无法访问别的进程的物理内存的。所以说,【进程,是资源分配的基本单位】
3. 现在,不同的应用程序,不关心底层的物理内存分配,也不关心CPU的协调共享,然后有一些程序,想要共享CPU而且还要共享同样的物理内存,这时,【线程】模型出现了,它们被包裹在进程里边,在调度器的管理下共享CPU,用于同样的虚拟地址空间,同样也共享同一个物理地址空间,然后,它们无法跨越包裹自己的进程,去访问别的进程的物理地址空间。【这里,进程和线程共享同一个物理地址空间】
4. 进程之间怎样共享同一个物理地址空间呢?不同的系统方法各异,符合posix规范的操作系统都提供一个接口,叫mmap,可以把一个物理地址空间映射到不同的进程中,由不同的进程来共享。
5. 有的OS里,进程不是调度单位,线程是最基本的调度单位,调度器只调度线程。
对于OS来说,一个任务就是一个进程,比如打开一个浏览器就启动一个浏览器进程,打开一个记事本,就启动一个记事本进程,打开两个记事本,就启动2个记事本进程,打开一个word就启动一个word进程。所以说,【进程,是程序的一次运行】
有些进程,还不止同时干一件事,比如word,可以同时进程打字、拼写检查、打印等待,在一个进程内部,要同时干多件事,就需要同时运行多个‘子任务’,我们把进程内的这些‘子任务’称为线程。
每个进程至少干一件事,所以每个进程中至少有一个线程。多个线程同时执行,类似于多进程的执行方式,也OS在多个线程之间快速切换。每个线程短暂交替运行,看起来像同时执行一样。【所以说,线程,是cpu调度的基本单位,是最小的执行单元】。
如何调度进程和线程,完全由OS决定,程序自己不能决定
执行单任务的进程,就只有一个线程,怎样同时执行多个任务?
两种解决方案:
1) 启动多个进程,每个进程虽然只有一个线程,但是多个进程可以一块执行多个任务
2) 启动一个进程,在一个进程内启动多个线程,这样多个线程可以一块执行多个任务。
3) 启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了,当然这种模型,实际很少采用
总结起来,多任务的的实现3种方式:
1. 多进程模式
2. 多线程模式
3. 多进程+多线程模式
进程是用进程描述符表示的,那么线城是怎么实现和表示的呢?
线程机制,提供了在同一程序内共享内存地址空间运行的一组线程,这些线程可以共享打开的文件和进程的其他资源。如下图所示:
在linux系统中,线程进程的关系更像下图所示。
线程和进程是一样的,只是进程比线程大,它们都指向相同的内存地址空间。
Linux实现线程的机制非常独特。
从内核来讲,并没有线程的概念,linux把线程当做进程来实现。内核并没有特殊的调度算法或者定义特别的数据结构来表征线程。线程仅被看做一个与其它进程共享某些资源的进程。每个线程都拥有唯一属于自己的task_struct,所以在内核中,它看起来像是一个普通的进程(只是它和其他一些进程共享某些资源),只是它没有自己独立的内存地址空间。
在microsoft Windows等OS中,内核提供专门支持线程的机制(这些系统常把线程称作是轻量级进程)。
如果现在需要一个包含4个线程的进程,在提供专门支持线程的系统中,会有一个指向4个不同线程的指针的进程描述符。该描述符负责描述像地址空间,打开的文件这样的共享资源。线程本身再去描述它独占的资源。而,linux仅创建4个进程并分配4个普通的task_struct结构,建立这4个进程时,指定它们共享某些资源。
Linux中,触发任何一个事件时,系统都会把它定义成一个进程,同时根据触发这个进程的用户,给予这个进程一组有效的权限设置。
程序运行起来后,我们看不到摸不着,linux提供一系列命令来查看正在运行的进程。
ps -el 查看当前bash下的相关进程全部信息。、
pstree 显示整棵进程树
Init进程是所有进程的根节点,通过ps可以看到init的PID为1.
当linux启动的时候,init是系统创建的第一个进程,这个进程会一直存在,直到关闭计算机。所有其他进程由init进程衍生出来的。
衍生出来的进程,就是linux父子进程的概念。
当我们登录系统后,会取得一个bashshell,然后利用这个bash提供的接口去执行另一个命令,如bash、ps等,这些另外执行的命令也会触发称为PID,这个后来执行的命令产生的PID就是子进程。而原本的bash环境就是父进程了。
一个进程除了有PID之外,还会有一个PPID(ParentPID),父进程PID。
计算机开机时,内核只建立一个init进程,linux内核不提供直接建立新进程的系统调用。剩下的所有进程都是init进程通过fork机制建立。新的进程通过老的进程复制自身得到,这就是fork。Fork是一个系统调用,进程存活在内存中。每个进程都在内存中分配一块属于自己的空间(内存空间,包括堆、栈、全局静态区、文本常量区、程序代码区)。当一个程序调用fork时,实际上就是将上面的内存空间,又复制出来一个,构成一个新的进程。在内核中为该进程创建新的附加信息(如新的PID,PPID为原进程的PID),此后,两个进程分别继续运行下去。新的进程和原有进程有相同的运行状态(相同的变量值,相同的指令。。。),只能通过进程的附加信息区分两者。
程序调用exec时,进程清空自身的内存空间,根据新的程序文件重建程序代码、文本常量、全局静态、堆和栈,并开始运行。
查看线程数的三种方法:
1. top –H
-H:threadstoggle
加上这个选项启动top,top一行显示一个线程。否则。显示一个进程。
2. ps xH
H shows threadsas if there were processed
这样可以查看所有存在的线程
3. ps -mp
m : shows threads after processes.
查看一个进程起的线程数
1. OS系统级的限制
/proc/sys/kernel/pid_max
/proc/sys/kernel/thread_max
max_user_processes(ulimit-u) #系统限制某用户下最多可以运行多少进程或线程
/proc/sys/vm/max_map_count
以及硬件内存大小(free -g)
2. Java虚拟机本身限制
-Xms #initial java heap size
-Xmx #maximum java heap size
-Xss #the stack size for each thread
进程和线程:
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0013868322563729e03f6905ea94f0195528e3647887415000
进程与线程的一个简单解释:
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
线程在Linux中的实现:
http://blog.csdn.net/dlutbrucezhang/article/details/10047049
Linux基础:进程管理:
http://wuchong.me/blog/2014/07/24/linux-process-manage/
Linux下查看进程和线程:
http://stark-summer.iteye.com/blog/2173704
Linux最大线程数限制及当前线程数查询:
http://blog.csdn.net/wang7dao/article/details/16369381