进程和线程
进程
进程即是操作系统对运行程序的一种抽象,操作系统需要识别运行的程序以及分配内存空间等,实现的方式也就是进程,通俗的说法就是操作系统资源分配的基本单位。
[图片上传失败...(image-a51dea-1679213878869)]
进程的组成
组成元素:进程(进程实体)由程序段、数据段、PCB(进程控制块)三部分组成。
同一个程序多个进程的代码段是共享的
进程的特征
独立性:进程是计算机系统中独立运行的基本单位,具有独立的内存空间和系统资源,不会被其他进程干扰。
动态性:进程是动态创建、运行和销毁的,系统可以根据需要创建新的进程,并在不需要时终止进程。
并发性:计算机系统中可以同时运行多个进程,进程之间可以并发执行,提高系统资源的利用率。
阻塞性:进程可能会因为等待某些事件而暂停执行,例如等待输入/输出完成、等待信号量等。
状态可变性:进程的状态会随着执行过程中的不同情况而不断变化,例如进程创建、就绪、运行、阻塞和终止等状态。
合作性:进程可以与其他进程进行通信和协作,例如共享内存、消息传递、管道等方式。
时间片轮转调度:多个进程并发运行时,操作系统会采用时间片轮转调度算法,按照一定的时间片分配给每个进程运行时间,以实现公平调度。
-
系统资源的竞争:不同进程之间需要争夺系统资源,例如 CPU、内存、IO等,需要通过进程调度算法合理分配系统资源,避免出现饥饿和死锁等问题。
程序运行过程:
[图片上传失败...(image-e5cb5a-1679213878869)]
进程运行原理
进程的运行原理主要分为进程的创建、调度和终止三个阶段。
进程创建:当用户需要执行某个程序时,操作系统会为该程序创建一个新的进程。在进程创建的过程中,操作系统会为该进程分配一定的内存空间,并为其初始化各种数据结构。此外,进程的创建还包括打开程序的代码和数据文件,将程序代码装入内存等操作。
进程调度:当进程被创建后,操作系统会按照一定的调度算法,将其放入就绪队列中等待调度。在调度时,操作系统会根据进程的优先级、进程的执行状态等因素来决定调度顺序。当一个进程被调度后,它会被分配一段时间片(time slice)来执行,时间片结束后,操作系统会将该进程暂停,并将其状态置为就绪状态,等待下一次调度。
进程终止:当进程执行完毕或者出现异常情况时,操作系统会将其从系统中清除。在进程终止的过程中,操作系统会关闭进程打开的文件,释放进程占用的内存空间,更新进程控制块等操作。此外,当一个进程终止时,它会向操作系统发送一个终止信号,通知操作系统将其从系统中清除。
总之,进程的运行原理是一个复杂的过程,它涉及到进程的创建、调度和终止等多个方面。操作系统通过精细的调度算法,保证了不同进程之间的资源共享和并发执行,从而提高了系统的性能和效率。
进程的状态及转换
新建状态(New):进程刚被创建,还没有被操作系统调度执行。
就绪状态(Ready):进程已经被操作系统接受,已经分配了所需的资源,只等待系统调度执行。
运行状态(Running):进程正在被 CPU 执行,占用了 CPU 资源。
阻塞状态(Blocked):进程在等待某个事件的发生,比如等待 I/O 操作完成或等待资源释放,此时进程无法执行。
终止状态(Terminated):进程已经完成执行或被操作系统终止。
进程状态之间的转换如下:
新建状态(New) -> 就绪状态(Ready):进程被创建后,等待操作系统调度执行。
就绪状态(Ready) -> 运行状态(Running):进程被操作系统选中,获得 CPU 时间片,开始执行。
运行状态(Running) -> 就绪状态(Ready):进程执行完毕或者时间片用完,进入就绪状态等待下一次调度。
运行状态(Running) -> 阻塞状态(Blocked):进程需要等待某个事件的发生,如 I/O 操作,等待操作系统将其挂起并进入阻塞状态。
阻塞状态(Blocked) -> 就绪状态(Ready):等待的事件已经发生,操作系统将其唤醒并进入就绪状态等待调度。
运行状态(Running) -> 终止状态(Terminated):进程完成了任务或者被操作系统强制终止。
进程控制(原语)
创建原语:create, 阻塞原语:block, 唤醒原语:wakeup, 撤销原语:destroy
线程
线程(Thread)是计算机中的一种基本执行单位,是程序执行的最小单元。线程是进程中的一部分,一个进程可以包含多个线程。多个线程可以共享同一进程的内存空间和其他资源,但每个线程都有自己的程序计数器、栈和局部变量等线程私有的数据。其目的是提高OS的并发性,注意包含用户级线程和内核级线程。
进程和线程的区别
线程相较于进程,大大降级了撤销、创建和切换可执行实体的成本和难度。
资源占用:进程是操作系统资源分配的基本单位,它拥有独立的内存空间、文件描述符等系统资源,不同进程之间彼此独立。而线程是进程内部的执行单元,它共享进程的内存空间和系统资源。
调度:进程的创建、切换、销毁等操作需要操作系统的介入,因此进程调度的开销比线程调度大。线程是由进程创建和管理的,线程的调度是由进程内的线程调度器完成的。
并发性:由于进程之间相互独立,因此进程的并发性高。而线程是共享进程资源的,线程之间的并发性相对较低,因为线程的执行需要协调和同步。
系统开销:由于进程之间的独立性,进程的创建和销毁需要消耗一定的系统资源,而线程的创建和销毁开销相对较小。
通信:由于进程之间彼此独立,因此它们需要通过一些特定的机制(例如管道、消息队列、共享内存等)来进行通信。而线程之间共享进程的内存空间,因此它们可以通过直接访问共享内存来进行通信。
进程的通信方式
- 管道/匿名管道(Pipes) :用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。有名管道(Named Pipes) : 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循先进先出(first in first out)。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
- 信号(Signal) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
- 消息队列(Message Queuing) :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比 FIFO 更有优势。消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点。
- 信号量(Semaphores) :信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
- 共享内存(Shared memory) :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。
- 套接字(Sockets) : 此方法主要用于在客户端和服务器之间通过网络进行通信。套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
进程的上下文切换
在进行上下文切换之前,操作系统需要先保存当前进程的上下文(包括寄存器的值、程序计数器、栈指针等状态信息),以便在将来恢复该进程的执行。
接着,操作系统会将处理器的控制权交给另一个进程,并将其之前保存的上下文信息恢复到寄存器中,使该进程能够从之前暂停的地方继续执行。这个过程称为进程切换,通常需要消耗一定的处理器时间和系统资源。
需要注意上下文切换对于多任务操作系统的性能和稳定性非常重要。如果上下文切换的频率太高,会导致处理器花费过多时间在保存和恢复进程上,从而影响系统的整体性能。因此,操作系统需要对进程进行合理的调度,以最小化上下文切换的次数,并确保所有进程都能够得到公平的处理器时间。
线程间同步方式
- 互斥量(Mutex):采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
- 信号量(Semaphore) :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。事件(Event) :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。