操作系统——进程
1、概念
进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。
2、进程间状态转换
就绪、执行、阻塞
- 就绪 —> 执行:对就绪状态的进程,当进程调度程序按一种选定的策略从中选中一个就绪进程,为之分配了处理机后,该进程便由就绪状态变为执行状态;
- 执行 —> 阻塞:正在执行的进程因发生某等待事件而无法执行,则进程由执行状态变为阻塞状态,如进程提出输入/输出请求而变成等待外部设备传输信息的状态,进程申请资源(主存空间或外部设备)得不到满足时变成等待资源状态,进程运行中出现了故障(程序出错或主存储器读写错等)变成等待干预状态等等;
- 阻塞 —> 就绪:处于阻塞状态的进程,在其等待的事件已经发生,如输入/输出完成,资源得到满足或错误处理完毕时,处于等待状态的进程并不马上转入执行状态,而是先转入就绪状态,然后再由系统进程调度程序在适当的时候将该进程转为执行状态;
- 执行 —> 就绪:正在执行的进程,因时间片用完而被暂停执行,或在采用抢先式优先级调度算法的系统中,当有更高优先级的进程要运行而被迫让出处理机时,该进程便由执行状态转变为就绪状态。
3、进程间通信
概念:
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)
方式:
- 无名管道(pipe):无名管道是半双工通信,具有固定的读端和写端,只能用于父子进程或者兄弟进程之间的进程的通信;
- 有名管道(named pipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
- 消息队列(message queue) :消息队列是消息的链接表,存放在内核中,一个消息队列由一个标识符 ID 来标识,消息队列独立于发送与接收进程,进程终止时,消息队列及其内容并不会被删除。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 信号量(semophore) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问,若要在进程间传递数据需要结合共享内存。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。
- 信号 (sinal) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
- 共享内存(shared memory) :指两个或多个进程共享一个给定的存储区。共享内存是最快的 IPC 方式,因为进程是直接对内存进行存取。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
- 套接字(socket) :套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同设备之间的进程通信。
4、进程调度算法
- 先来先服务 调度算法:当每个进程就绪后,它加入就绪队列,选择在就绪队列中存在时间最长的进程运行,进程不会中断;
非抢占式,不会饥饿
- **时间片轮转 **调度算法:系统将所有就绪进程按到达时间的先后次序排成一个队列,选择就绪队列中第一个进程分配时间片,时间片用完后会把进程放入就绪队列尾部;每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
抢占,不会饥饿
- 最短作业优先 调度算法:从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行;
抢占/非抢占,会饥饿
- 最高优先级 调度算法:每次从后备作业队列中选择优先级最高的一个或几个作业,将它们调入内存运行;
抢占/非抢占,会饥饿
- 多级反馈队列 调度算法:设置多个就绪队列,每个队列的进程按照先来先服务排队,然后按照时间片轮转分配时间片,若时间片用完还没有完成,则进入下一级队尾,只有当前队列为空时,才会为下一级队列分配时间片;
抢占式,可能会饥饿
5、守护进程
- 概念:守护进程也叫做精灵进程,是运行在后台的一种特殊进程他独立于控制终端并且可以周期性的执行某种任务或者等待处理某些发生的事件。
- 常见:系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。
- 创建守护进程:
- Fork开始
- 调用fork()产生一个子进程,然后使父进程退出
- 调用setsid()创建一个新对话期,使进程成为一个会话组长
- 禁止进程重新打开控制终端
- 关闭不需要的文件描述符
- 调用chdir命令将当前目录更改为根目录
- 使用unmask(0)将屏蔽字清零
- 处理SIGCHLD信号,设置为SIG_IGN,避免子进程结束时不会产生僵尸进程。
- 使用daemon()函数直接创建守护进程
- Fork开始
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程,孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。作用:维护子进程的信息,以便父进程在以后某个时候获取。