《每日一记》系统篇:进程与线程

什么是进程?

进程(Process) 是程序执行时的一个实例,是系统进行资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块(PCB)中。以表示该进程拥有这些资源或正在使用它们。另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。

进程与程序并不是一个概念,进程是动态的(进程是担当分配系统资源(CPU时间,内存)的实体,是具有动态特性的),而程序是一个静态的资源。

进程:通过程序运行,表现在操作系统执行的,被操作系统所管理的(进程管理)
程序:是一个在硬盘上的驱动,属于文件,是一种静态的资源,可执行的资源
文件:是本地磁盘的资源

概括:进程指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程是资源分配的最小单位。

什么是线程?

线程(thread) 是线程,有时也被称为轻量级进程,是程序执行流的最小单元,是进程中的一个实体,是被系统独立调度和分派的基本单位。与进程不同,线程与资源分配无关,线程自己不拥有系统资源,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成。

概括:线程是系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。

进程和线程的关系

通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。但是,一个线程只属于一个进程。进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。而且需要注意的是,线程不是一个可执行的实体。

进程和线程的区别

进程和线程的主要差别在于它们是不同的操作系统资源管理方式

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响。

线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

          (1)进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
          (2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。
          (3)进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束
          (4)线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
          (5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
          (6)线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志进程间的通信方式

进程间的通信

(1)管道(pipe):

管道可用于具有亲缘关系的进程间的通信,是一种半双工的方式,数据只能单向流动,允许一个进程和另一个与它有共同祖先的进程之间进行通信。 


(2)命名管道(named pipe):

命名管道克服了管道没有名字的限制,同时除了具有管道的功能外(也是半双工),它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。

 
(3)信号(signal):

信号是比较复杂的通信方式,用于通知接收进程有某种事件发生了,除了进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。 


(4)消息队列:

消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点


(5)共享内存:

使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。 


(6)内存映射:

内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。

 
(7)信号量(semaphore):

主要作为进程间以及同一进程不同线程之间的同步手段。 

 

(8)套接字(Socket):

更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

 

线程间的通信

1、锁机制:包括互斥锁、条件变量、读写锁
互斥锁:提供了以排他方式防止数据结构被并发修改的方法。
读写锁:允许多个线程同时读共享数据,而对写操作是互斥的。
条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

2、信号量机制(Semaphore):包括无名线程信号量和命名线程信号量

3、信号机制(Signal):类似进程间的信号处理

  • 线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

线程同步

1、互斥锁(对共同资源的互斥控制)

可以通过使用互斥接口保护数据,确保同一时间只有一个线程访问数据。互斥锁(mutex从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。在这种方式下,每次只有一个线程可以向前执行。

在设计时需要规定所有的线程必须遵守相同的数据访问规则,只有这样,互斥机制才能正常工作。操作系统并不会做数据访问的串行化。如果允许其中的的某个线程在没有得到锁的情况下也可以访问共享资源,那么即使其他的线程在使用共享资源前都获取了锁,也还是会出现数据不一致的问题。

你真的懂线程同步么?

 

2、读写锁

读写锁与互斥锁类似,不过读写锁允许更高的并行性。互斥量要么是锁住状态要么是不加锁状态,而且一次只有一个线程可以对其加锁。读写锁可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁

当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是如果线程希望以写模式对此锁进行加锁,它必须阻塞直到所有的线程释放读锁。虽然读写锁的实现各不相同,但当读写锁处于读模式锁住状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求。这样可以避免读模式锁长期占用,而等待的写模式锁请求一直得不到满足

读写锁非常适合于对数据结构读的次数远大于写的情况。当读写锁在写模式下时,它所保护的数据结构就可以被安全地修改,因为当前只有一个线程可以在写模式下拥有这个锁。当读写锁在读模式下时,只要线程获取了读模式下的读写锁,该锁所保护的数据结构可以被多个获得读模式锁的线程读取。

读写锁也叫做共享-独占锁,当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。

与互斥量一样,读写锁在使用之前必须初始化,在释放它们底层的内存前必须销毁。

2、信号量

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。

信号量通过一个计数器控制对共享资源的访问,信号量的值是一个非负整数,所有通过它的线程都会将该整数减一。如果计数器大于0,则访问被允许,计数器减1;如果为0,则访问被禁止,所有试图通过它的线程都将处于等待状态。

计数器计算的结果是允许访问共享资源的通行证。因此,为了访问共享资源,线程必须从信号量得到通行证, 如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果另一个线程等待通行证,则那个线程将在那时获得通行证。

信号量可以分类:

1)二进制信号量(binary semaphore):只允许信号量取0或1值,其同时只能被一个线程获取。

2) 整型信号量(integer semaphore):信号量取值是整数,它可以被多个线程同时获得,直到信号量的值变为0。

3) 记录型信号量(record semaphore):每个信号量s除一个整数值value(计数)外,还有一个等待队列List,其中是阻塞在该信号量的各个线程的标识。当信号量被释放一个,值被加一后,系统自动从等待队列中唤醒一个等待中的线程,让其获得信号量,同时信号量再减一。

Semaphore可以被抽象为五个操作:

- 创建 Create

- 等待 Wait:线程等待信号量,如果值大于0,则获得,值减一;如果只等于0,则一直线程进入睡眠状态,知道信号量值大于0或者超时。

-释放 Post: 执行释放信号量,则值加一;如果此时有正在等待的线程,则唤醒该线程。

-试图等待 TryWait: 如果调用TryWait,线程并不真正的去获得信号量,还是检查信号量是否能够被获得,如果信号量值大于0,则TryWait返回成功;否则返回失败。

-销毁 Destroy

信号量,是可以用来保护两个或多个关键代码段,这些关键代码段不能并发调用。在进入一个关键代码段之前,线程必须获取一个信号量。如果关键代码段中没有任何线程,那么线程会立即进入该框图中的那个部分。一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。

***互斥量和信号量的区别***

1)互斥量用于线程的互斥,信号量用于线程的同步。

这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

2) 互斥量值只能为0/1,信号量值可以为非负整数。

也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。

3)互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到

3、条件变量

与互斥锁不同,条件变量是用来等待而不是用来上锁的条件变量用来自动阻塞一个线程,直到某特殊情况发生为止通常条件变量和互斥锁同时使用

条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

使用条件变量之前要先进行初始化。条件变量分为两部分: 条件变量. 条件本身是由互斥量保护的.

线程在改变条件状态前先要锁住互斥量. 它利用线程间共享的全局变量进行同步的一种机制。

引伸各种锁🔒

如何创建子进程?根据pid区分子/父进程?

 fork()函数用于从一个已经存在的进程内创建一个新的进程,新的进程称为“子进程”,相应地称创建子进程的进程为“父进程”。使用fork()函数得到的子进程是父进程的复制品,子进程完全复制了父进程的资源,包括进程上下文、代码区、数据区、堆区、栈区、内存信息、打开文件的文件描述符、信号处理函数、进程优先级、进程组号、当前工作目录、根目录、资源限制和控制终端等信息,而子进程与父进程的区别有进程号、资源使用情况和计时器等。

在父进程中fork()函数会将子进程的PID返回给父进程,即父进程的pid变量内存储的是一个大于0的整数

在子进程中,fork()函数会返回0,即子进程的pid变量内存储的是0;如果创建进程出现错误,则会返回-1,不会创建子进程。

进程管理

1、进程管理:一个操作系统管理进程的结构体,将进程的信息保存在结构体中,相当于一个清单(进程表),将进程管理起来,由操作系统进行调度

2、时间片:

现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。所谓的多任务,就是操作系统可以同时运行多个任务。
轮转调度:一种最古老的,最简单,最公平使用最广的算法。任务执行的一小段时间叫做时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态等待下一个属于它的时间片的到来。

3、并发和并行

并发:多个进程在一个CPU下采用时间片轮转的方式,在一段时间之内,让多个进程都得以推进,称之为并发(假同时)。

在这里插入图片描述
并行:多个进程在多个CPU下分别,同时进行运行,这称之为并行(真同时)。

在这里插入图片描述

4、进程上下文

上下文:其实就是一个环境。进程在时间片轮转切换时,每个进程运行环境不同,这样就涉及到前后的上下文环境的切换。从一个进程切换到另一个进程是需要一定的时间进行管理事务处理的——就是一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及堆栈上的内容。切换时需要保存当前进程的所有状态,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。

在这里插入图片描述

5、进程的状态

运行态(该进程实际占用cpu)
就绪态(可运行,但是因为其他进程正在执行而被停止)
阻塞态(除非某种外部时间发生,否则进程不能运行)

 


对于前面两种情况的进程都是可运行的,只是对于就绪态,暂时没有CPU分配给他,对于第三种状态,就算此时有空闲的CPU也不会让该进程运行。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值