进程
应用程序的执行实例应用程序, 只有执行时 才叫进程 每一个进程都对应操作系统中应用程序的一个执行实例
特点
1、动态性
启动应用程序 创建进程 给进程分配相应的资源,关闭占用资源会释放
动态产生、动态消亡
2、并发性
可同时运行多个应用程序,让它们各自完成不同的任务,系统中所有进程都可以与其他进程一起并发执行,但是一个CPU在某个给定的时刻 只能处理一个应用程序
操作系统利用时间片轮转 或其他策略,让多个应用程序交替执行的特性
3、独立性
进程独立运行 系统分别为他们分配资源和进行调度
进程是一个独立运行的基本单位
也是系统分配资源和进行调度的独立单位
状态
就绪(Ready)状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。可能会有多个进程处于就绪状态,排成就绪队列。
执行(Running)状态
当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。进程占用处理机,进程的程序正在执行。单处理机系统中只能有一个进程处于执行状态,多处理机系统中可能有多个进程处于执行状态。
阻塞(Blocked)状态
也叫等待或睡眠状态,是进程由于等待某种事件的发生而处于暂停执行的状态。正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
(1) 新状态:进程刚刚建立,还没有送入就绪队列的状态。
(2) 终止状态:一个进程已正常结束或非正常结束,OS已将它从就绪队列中移出,还未将它撤销时的状态。
状态的转换
进程在执行期间可以多次处于就绪状态和执行状态,也可多次处于阻塞状态,但处于新状态只有一次。
(1)新状态->就绪状态:当就绪队列允许接纳新进程时,系统便把处于新状态的进程移入就绪队列。
(2)就绪态->执行状态:进程调度程序为处于就绪状态的进程分配处理机后,该进程进入执行状态。
(3)执行态->阻塞状态:正在执行的进程因需要等待某事件而无法执行。正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
(4)阻塞状态->就绪态:进程所等待的事件发生了,进程就从阻塞状态进入就绪状态。
(5)执行态->就绪状态:正在执行的进程因时间片用完而被暂停执行;或者在可抢占调度方式中,一个优先权高的进程到来后,正在执行的低优先权的进程被强制撤下处理机,转换为就绪状态。
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。
(6)执行态->终止状态:一个进程已完成或发生某种特殊事件,进程将变为终止状态。
就绪挂起: 进程在内存外,但只要被调入内存就可运行
等待挂起: 进程在外存中,并且等待某件事情发生
孤儿进程
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(1号进程)所收养,并有init进程对它们完成状态收集工作。
僵尸进程
一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符(pcb)仍然保存在系统中。这种进程成为僵死进程。
1. 产生原因:
Linux/unix 中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程,子进程的结束和父进程的运行是一个异步过程,即父进程永远不知道子进程什么时候结束,当一个进程完成它的工作终止后,它的父进程需要调用wait()和waitpid()系统调用取得子进程的终止状态。
在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。通过ps命令查看其带有defunct的标志。僵尸进程是一个早已死亡的进程,但在进程表 (processs table)中仍占了一个位置(slot)。
但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个 进程是刚刚结束的这个进程的子进程,如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动 wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。
2. 原理分析:
每个Unix进程在进程表里都有一个进入点(entry),核心进程执 行该进程时使用到的一切信息都存储在进入点。当用 ps 命令察看系统中的进程信息时,看到的就是进程表中的相关数据。当以fork()系统调用建立一个新的进程后,核心进程就会在进程表中给这个新进程分配一个 进入点,然后将相关信息存储在该进入点所对应的进程表内。这些信息中有一项是其父进程的识别码。
子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。那么会不会因为父进程太忙来不及 wait 子进程,或者说不知道子进程什么时候结束,而丢失子进程结束时的状态信息呢?不会。因为UNIX提供了一种机制可以保证,只要父进程想知道子进程结束时的 状态信息,就可以得到。这种机制就是:当子进程走完了自己的生命周期后,它会执行exit()系统调用,内核释放该进程所有的资源,包括打开的文件,占用 的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出码exit code,退出状态the terminationstatus of the process,运行时间the amount of CPU time taken by the process等),这些数据会一直保留到系统将它传递给它的父进程为止,直到父进程通过wait / waitpid来取时才释放。
3.解决方法:
(1) 父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。
(2) 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。
(3) 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号
(4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。
查找进程:
ps -aux | grep flume / netstat -anop | grep 8080(端口号)
自动查杀僵死进程指令:
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -9
守护进程
daemon
在后台运行,不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务.习惯上守护进程的名字通常以d结尾(sshd),但这些不是必须的.
运行方式
①独立运行的守护进程
独立运行的守护进程由 init 脚本负责管理,所有独立运行的守护进程的脚本在/etc/rc.d/init.d/目录下。系统服务都是独立运行的守护进程包括 syslogd 和 cron 等。服务器监听在一个特点的端口上等待客户端的连接。如果客户端产生一个连接请求,守护进程就创建一个子服务器响应这个连接,而主服务器继续监听。以保持多个子服务器池等待下一个客户端请求。
②由 xinetd 管理的守护进程
从守护进程的概念可以看出,系统所运行的每一种服务,都必须运行一个监听某个端口连接所发生的守护进程,这通常意味着资源浪费。为了解决这个问题,Linux引进了“网络守护进程服务程序”的概念。CentOS 6.4使用的网络守护进程是xinted(eXtendedInterNET services daemon)。
xinetd能够同时监听多个指定的端口,在接受用户请求时,它能够根据用户请求的端口不同,启动不同的网络服务进程来处理这些用户请求。可以把xinetd看作一个管理启动服务的管理服务器,它决定把一个客户请求交给那个程序处理,然后启动相应的守护程序。
首先,守护进程最重要的特性是后台运行。
其次,守护进程必须与其运行前的环境隔离开来。
这些环境包括未关闭的文件描述符、控制终端、会话和进程组、工作目录以及文件创建掩码等。这些环境通常是守护进程从执行它的父进程(特别是shell)继承下来的。
最后,守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,也可以由作业控制进程crond启动,还可以由用户终端(通常是shell)执行。
按照服务类型分为如下几个。
系统守护进程:syslogd、login、crond、at等。
网络守护进程:sendmail、httpd、xinetd、等。
独立启动的守护进程:httpd、named、xinetd等。
被动守护进程(由xinetd启动):telnet、finger、ktalk等。[4]
线程
线程是进程的执行单元,进程内部的一个执行单元
它是程序中一个单一的顺序控制流程,进程中同时运行了多个线程 用来完成不同的工作 多线程
特点:
一个线程必须至少有一个父进程
拥有自己的堆栈、程序计数器和局部变量
一个线程可以独立完成某个功能
需要定义一些局部变量 或程序计数器
可以拥有自己的堆栈、程序计数器和局部变量
与父进程的其他线程共享进程所有的全部资源
不会额外占用进程之外的系统资源
独立运行 抢占的方式
一个进程中的所有线程 都是独立运行的
执行的时候采用抢占的方式
Window为了能够让多任务重叠执行
采用类似于时间片轮转的方式 让多个程序或任务交替使用CPU
时间间隔非常短
操作系统如何决定某一个时刻处理哪一个任务?
进程有多个线程 也是类似的道理
多个线程 具有相同的优先级
可运行的线程 就会竞争处理器资源 谁抢到 都有运行的机会
当前的线程有可能被挂起 以便运行另外一个线程
一个线程可以创建和删除另外一个线程
在一个线程中创建另外一个线程 删除另外一个线程
主线程可以启动很多个非主线程,同时也可以停止多个其他线程
同一个进程中的多个线程可以并发执行 交替执行,进程负责调度和管理线程,注意,必须确保线程不会妨碍同一进程的其他线程
分类
1、系统级线程
负责管理调度不同进程之间的多个线程 由操作系统直接管理
把它们按照同样的相对优先调度的方法进行调度
2、操作系统的内核
进程的创建、撤销以及要求由系统设备完成的读写操作,都是利用操作系统的调用而进入操作系统的内核,再由操作系统内核中相应的程序做相应的处理,不同进程之间切换也是在系统内核支持下实现的
3、用户级线程
开发应用程序时由于程序的需要而编写 的线程,创建 执行 消亡 都是在编写应用程序的时候控制的,仅仅存在于用户空间 在应用程序中控制其创建 执行和消亡,开发程序中定好的
用户线程的切换
通常发生在一个应用程序的诸多线程之间,用户级线程的调度算法和调度过程全部由用户自己决定,在运行的时候不需要特殊的系统支持
线程的状态
阻塞,中断和休眠
被阻塞 Waitting/blocking
不可运行状态,这种状态的线程得到一个特定的事件后
可能会返回到可运行状态
导致线程被阻塞的原因
1.调用Thread类的静态方法 sleep()方法
当前正在执行的线程会马上进入阻塞状态,主动放弃所占用的处理器资源,线程调度程序会在可运行状态的线程当中重新选择线程 让它开始执行,没有可运行状态的线程
当前正在运行的线程 也会进入阻塞状态
2 如果一个线程需要用到一个读写操作的结果.
这个读写操作尚未完成 线程将被阻塞
比如一个线程用到一个文件
文件还没有写完 这个时候 当前线程也就被阻塞了
3.如果一个线程的执行需要得到一个对象的锁
这个对象的锁正被别的线程占用 这个线程也将被阻塞
他需要等待这个对象的锁被其它的线程释放以后 才能继续执行
4,当一个线程对象执行了suspend()方法而被挂起
这个线程也会被阻塞
suspend()方法很危险 容易导致死锁 过期的方法 jdk不再使用
在java里面好像都是用sleep()来完成的,在我理解起来其实是一种状态
新生状态 new
没有活动 没有启动
可运行状态
抢占的运行方式 同一个时刻可能会有多个线程都处于可运行状态,调度程序只会选择一个程序让他开始运行,在一个时间点 只能有一个线程正在执行
从其他状态转回可运行状态,调用sleep()方法 当前的线程进入了睡眠状态
线程的睡眠时间已经达到了指定的间隔,这个线程 有可能重新回到可运行状态
一个线程在等待一个对象的锁的时候,处于阻塞的状态,如果另外一个线程释放了这个对象的锁,此线程就从阻塞状态转入可运行状态。
如果一个线程接到一个通知:说不必等待 可以运行,这个线程在等待的锁对象调用了notify()方法 或者notifyAll()方法之后,它也会转入可运行状态
正在运行
死亡 Dead
一个线程的run()方法运行完毕以后,这个线程在运行过程中出现没有被捕获的异常时这个线程会进入死亡状态,处于死亡状态的线程可能不会马上释放占用的内存资源
用等于null这样的方式来判断的时候,这个线程对象它不是null值,此时 它也不再是一个独立的可执行单元,如果我们尝试在一个已经死亡的线程上调用start()方法
就会得到一个异常信息
一个线程对象只能运行一次,运行完毕以后 我们就不能再用这个线程对象调用它的start方法 ,让它重新执行
唤醒
挂起
线程的优先级:
默认的情况下 一个线程会继承其父线程的优先级
父线程:就是启动它的那个线程,一个线程调度程序选择一个线程时,会优先考虑优先级别比较高的线程
java中 优先级是一个整数值,这个值的大小表示了这个线程在与其他的线程竞争处理器资源的时候得到执行的机会多少
切换的原则
1.当一个线程自愿地释放控制时 它会通过显式的放弃、睡眠或者阻塞来完成
所有的线程均接受线程调度器的检查
优先级高的线程优先执行
2.当一个线程可以被一个高优先级的线程抢占资源的时候
抢占式多任务:
cpu会始终处理高优先级线程
低级别优先级的线程将不受到处理
更改线程的优先级 setPriority()方法
线程对象.setPriority(优先级的值)
需要传入一个整形的参数 优先级
取值范围:1-10
默认情况下 线程的优先级是5
线程的优先级的取值范围它和操作系统有时候也有关系
有的操作系统 只支持1-5
如果给一个线程设值优先级10 自动映射到5上面
平时设置一个线程的优先级的时候 优先级的数值一定要在允许的范围之内
线程的调度
需要由操作系统决定运行哪个线程而不运行哪个线程
程序员也要决定运行某一个线程 而挂起某一个线程
这时候需要我们人为地去调度这个线程
主线程
一个进程的入口 ,一个进程至少包含一个主线程
main方法启动时 就会创建当前Java程序的主线程 进入cpu ,程序开始运行的时候创建的public static void main(String[] args) {
主线程的入口
}
参考站点:
http://blog.csdn.net/qq_36532097/article/details/71244691
http://blog.csdn.net/gb4215287/article/details/51855310
http://baike.baidu.com/link?url=os9PUX_h8F_NZi1OsVcBaRw7yAXundq3TN502J7PMAWzJ-gdmNUv6989pHUDlOAJHKA7ly8KEaWTqeXeehK1Gfl4OzgHMTbuqfI_088hdk2cDyMGXKhsQelInaZ4W8JN