1、什么是进程
2、什么是进程
1、包含和被包含
: 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。2、系统开销
: 每启动一个进程,系统都会为其分配地址空间
,建立数据表来维护代码段、堆栈段和数据段,线程没有独立的地址空间,它使用相同的地址空间共享数据;
为什么进程切换的开销比线程切换大呢?
切页表
,而且往往伴随着页调度
,因为进程的数据段代码段要换出去,以便把将要执行的进程的内容换进来。本来进程的内容就是线程的超集。而且线程只需要保存线程的上下文(相关寄存器状态和栈的信息)就好了,动作很小3、通信:
由于同一进程中的多个线程具有相同的地址空间
,致使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。在有的系统中,线程的切换、同步和通信都无须操作系统内核的干预。4、安全
:一个进程死掉不会对另一个进程造成影响(源于有独立的地址空间),多线程程序更不易维护,一个线程死掉,整个进程就死掉了(因为共享地址空间)进程之间私有和共享的资源
私有:
地址空间、堆、全局变量、栈、寄存器共享:
代码段,公共数据,进程目录,进程 ID线程之间私有和共享的资源
进程间通信的方式
管道
、系统IPC(包括消息队列、信号量、信号、共享内存等
)、以及套接字 socket
(1)匿名管道
:在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,一般使用fork函数实现父子进程的通信。
特征:
使用:
1、 父进程创建管道,得到两个文件描述符指向管道的两端
2、父进程fork出子进程,子进程也有两个文件描述符指向同一管道。
3、父进程关闭fd[0]
,子进程关闭fd[1]
,即父进程关闭管道读端,子进程关闭管道写端
(因为管道只支持单向通信)。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
命名管道(named pipe)又被称为先进先出队列(FIFO)
:不同于匿名管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是,FIFO(first input first output)总是按照先进先出的原则工作,第一个被写⼊的数据将首先从管道中读出。
特点:
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标记。(消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点)具有写权限
的进程可以按照一定得规则向消息队列中添加新信息,对消息队列有读权限
得进程则可以从消息队列中读取信息
特点
1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
2)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
3)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取
优点:
可以实现任意进程间的通信,并通过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便
缺点:
信息的复制需要额外消耗 CPU 的时间,不适宜于信息量大或操作频繁的场合
信号量:它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。
特点
PV操作
P操作
:负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。操作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;V操作
:负责把一个被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息。操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之。信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
指两个或多个进程共享一个给定的存储区。共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式
特点
1、共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
2、因为多个进程可以同时操作,所以需要进行同步。
3、信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
锁机制:
包括互斥锁/量(mutex)、读写锁(reader-writer lock)、自旋锁(spin lock)
互斥锁/量(mutex):提供了以排他方式防止数据结构被并发修改的方法。
读写锁(reader-writer lock):允许多个线程同时读共享数据,而对写操作是互斥的。
自旋锁(spin
lock)与互斥锁类似,都是为了保护共享资源。互斥锁是当资源被占用,申请者进入睡眠状态;而自旋锁则循环检测保持者是否已经释放锁。
条件变量(condition)
:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
信号量机制(Semaphore)
:为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。
信号机制(Signal)
:类似进程间的信号处理
线程产生的原因:
因此,操作系统引入了比进程粒度更小的线程,作为并发执行的基本单位,从而减少程序在并发执行时所付出的时空开销,提高并发性。和进程相比,线程的优势如下:
1)需要频繁创建销毁的优先用线程
这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的
2)需要进行大量计算的优先使用线程
所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。
3)强相关的处理用线程,弱相关的处理用进程
什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。
一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。
1.运行态,运行态指的就是进程实际占用CPU时间片运行时
2.就绪态,就绪态指的是可运行,但因为其他进程正在运行而处于就绪状态
3.阻塞态,除非某种外部事件发生,否则进程不能运行
孤儿进程
(无害):
init进程
收养,init进程完成对状态收集工作。而且这种过继的方式也是守护进程能够实现的因素。僵尸进程
(有害):
产生背景
wait / waitpid
的话,进程号就会一直被占用
,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程.危害场景
解决方法:
kill发送SIGTERM或者SIGKILL信号杀死该父进程
Ubuntu命令找到系统僵尸进程并杀死:
ps aux | grep 'Z'
来找到僵尸进程pstree -p -s PID
来寻找编号为PID进程也就是僵尸进程的父级进程Linux Daemon(守护进程)是运行在后台的一种特殊进程,并且不被任何终端产生的终端信息所打断。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。
注意:
一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。
作用
Linux 服务器在启动时也需要启动很多系统服务,它们向本地或网络用户提供了 Linux 的系统功能接口,直接面向应用程序和用户,而提供这些服务的程序就是由运行在后台的守护进程来执行的。
Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括:
在Linux成功fork进程后,会在系统中创建一个task_struct
(也称PCB, process control block),用来描述当前进程的状态、进程间关系、优先级和资源等信息。
标识符: 与进程相关的唯一标识符,用来区别正在执行的进程和其他进程。
状态:描述进程的状态,因为进程有挂起,阻塞,运行等好几个状态,所以都有个标识符来记录进程的执行状态。
优先级:如果有好几个进程正在执行,就涉及到进程被执行的先后顺序的问题,这和进程优先级这个标识符有关。
程序计数器:程序中即将被执行的下一条指令的地址。
内存指针: 程序代码和进程相关数据的指针。
上下文数据: 进程执行时处理器的寄存器中的数据。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表等。
记账信息: 包括处理器的时间总和,记账号等等。