嵌入式面经:一文搞懂线程进程的常见问题
嵌入式面经:一文搞懂线程进程的常见问题
1、什么是进程和线程?
进程:进程是资源分配的基本单位,它是程序执行时的一个实例,在程序运行时创建,每个进程都有独立的。
- 个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。
线程:线程是程序执行的最小单位,是进程的一个执行流,一个进程由多个线程组成的,他们共享资源,同一个进程内的线程共享进程的地址空间,每个线程都有自己独立的运行栈和程序计数器。
- 在一个进程类,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)
- 共享资源需要考虑:多个任务需要防止资源冲突,所以需要通过一些线程同步机制方式来防止多个线程访问共享资源产生冲突,比如互斥锁、信号量、条件变量、读写锁等。
2、进程和线程区别?
先说结论:
特性 | 进程(Process) | 线程(Thread) |
---|---|---|
独立性 | 独立的地址空间和资源 | 共享进程的地址空间和资源 |
创建开销 | 大,因为操作系统需要为进程分配独立的资源和内存 | 小,因为线程共享进程的资源,切换上下文的开销也较小。 |
通信 | 复杂(需IPC机制) | 简单(共享内存) |
稳定性 | 高(进程之间相互独立,一个进程崩溃不会直接影响其他进程崩溃) | 低(线程之间相互依赖,一个线程的崩溃可能导致整个进程崩溃) |
同步 | 简单(进程间无共享数据) | 复杂(需同步机制) |
进程适用于需要独立执行且资源隔离的任务,而线程则适用于需要高并发和资源共享的任务。根据具体应用场景选择合适的并发执行单元可以提高系统的性能和稳定性。
3、何时使用多进程、多线程?
- 对资源的管理和保护要求高,不限制开销和效率时,使用多进程。
- 要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。
4、接1:什么是线程同步?常用的线程同步机制有哪些?
**线程同步:**线程同步是指在多个线程并发执行时,保证它们按照一定的顺序执行以达到正确的结果。
**常用的线程同步机制:**互斥锁、信号量、条件变量、读写锁。
-
互斥锁:是一种用于保护共享资源的同步机制。它确保同一时间只有一个线程可以访问共享资源。
用法:
锁定:当一个线程想要访问共享资源时,它首先需要获取互斥锁。如果锁已经被其他线程持有,那么该线程将会被阻塞,直到锁被释放。
解锁:当线程完成对共享资源的访问后,它必须释放锁,以便其他线程可以继续访问该资源。
信号量:(其实是一种计数器)是一种更通用的同步机制,可以用来控制对共享资源的访问。它可以用来限制同时访问资源的线程数量。
用法:
初始化:设置信号量的初始值。
申请信号量(P操作):本质就是申请获得临界资源中某块资源的使用权限。如果信号量值大于0,程可以访问资源并将计数器减1,否则,线程将被阻塞,如果它的值为0,就挂起该进程的执行。
释放信号量(V操作):本质就是归还临界资源中某块资源的使用权限。当释放成功时临界资源计数器加1,释放被阻塞的线程。
-
条件变量:条件变量(Condition Variable)用于线程之间的通信和协调。当一个条件不满足时,线程可以等待在条件变量上,直到另一个线程发出信号,通知条件已经满足。
用法:
等待:线程在条件变量上等待,直到被其他线程通知。
通知:当条件满足时,其他线程可以通知等待的线程继续执行。
-
读写锁:允许多个线程同时读取共享资源,但在写入资源时,确保只有一个线程可以进行写操作。这提高了读操作的并发性。
用法:
读锁定:允许多个线程同时读取数据。
写锁定:在写入数据时,阻塞所有其他的读和写操作。
5、接2:阻塞和非阻塞有什么特点,适用什么场合?
阻塞和非阻塞是编程中处理I/O操作、进程和线程管理时的重要概念。它们主要用于描述线程或进程在等待某些条件或事件时的行为。下面是对阻塞和非阻塞的详细解释、它们的特点以及适用场合:
特性 | 阻塞(Blocking) | 非阻塞(Non-blocking) |
---|---|---|
定义 | 操作会等待完成,期间线程/进程被挂起 | 操作立即返回,未完成时线程/进程继续执行 |
资源利用 | 线程/进程在等待期间占用系统资源 | 线程/进程在等待期间可以执行其他任务 |
编程复杂度 | 简单,代码执行顺序与逻辑顺序一致 | 复杂,需要处理操作未完成的情况 |
适用场合 | 同步I/O操作、线程同步、简化代码逻辑 | 高性能应用、并发处理、事件驱动编程 |
优点 | 编写和调试更容易 | 更高的系统资源利用率和响应速度 |
缺点 | 在等待期间可能会导致资源浪费 | 编程和调试更复杂,需要处理轮询或回调函数 |
示例场景 | 读取文件、获取锁、等待条件满足 | 网络服务器、多任务处理、用户界面响应 |
6、多线程和多进程:进程间通信方式?
6.1常见的进程间通信方式
由于进程有独立的内存空间,需要通过管道、消息队列、共享内存、信号、套接字(Socket)等机制实现数据交换。适用于需要高隔离性和稳定性的应用。
6.2常见的进程间通信方式
线程间通信(见4):由于线程共享进程的内存空间,可以直接通过共享变量实现通信,但需要使用互斥锁、条件变量、信号量等同步机制来确保数据一致性。适用于需要高效并发处理的应用。
6.3.(6.1拓展)socket:什么情况下用socket?
1、套接字(Socket):是一种广泛用于网络编程的通信机制,适用于不同主机之间以及同一主机上不同进程之间的通信。以下是使用套接字的主要场景及其在同一主机上进程间通信的应用:
2、何时使用套接字:
网络通信
-
客户端-服务器模型
适用场景:如Web服务器、邮件服务器、数据库服务器等。
特点:客户端和服务器可以位于不同的主机上,通过网络进行数据交换。
示例:浏览器通过HTTP协议与Web服务器通信。
-
分布式系统
适用场景:如分布式计算、分布式数据库、分布式文件系统等。
特点:系统的各个节点可以分布在不同的地理位置,通过网络进行协同工作。
示例:Hadoop、Kafka等分布式系统。
-
实时通信
适用场景:如即时通讯工具、视频会议、在线游戏等。
特点:需要低延迟、高实时性的数据传输。
示例:微信、Skype等应用。
同一主机上的进程间通信
-
复杂通信需求
适用场景:需要传输复杂数据结构、大量数据或需要高效通信的场景。
特点:比管道、消息队列等传统IPC方式更灵活,适用于需要双向通信和高带宽的应用。
示例:同一主机上的不同服务进程之间的数据交换。
-
跨语言通信
适用场景:需要在不同编程语言编写的进程之间通信。
特点:套接字支持多种编程语言的接口,适用于异构系统之间的数据交换。
示例:一个Python进程与一个C++进程之间的通信。
-
高性能通信
适用场景:需要高并发、高吞吐量的通信场景。
特点:使用无阻塞I/O、多路复用技术可以实现高效通信。
示例:高性能计算(HPC)应用中的进程通信。
7、什么是僵尸进程与孤儿进程
特性 | 僵尸进程(Zombie Process) | 孤儿进程(Orphan Process) |
---|---|---|
定义 | 一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵死进程。 | 一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并由 init 进程对它们完成状态收集工作。 |
状态 | 保留在进程表中,状态标记为Z | 重新分配给init进程(PID=1)管理 |
资源占用 | 占用很少的系统资源(仅保留进程表项) | 正常占用系统资源 |
消除方式 | 需要父进程调用wait() 或waitpid() 来回收状态信息 | 由init进程接管,正常终止时由init进程回收资源 |
维护 | 系统管理员需监控并杀死父进程以触发init回收僵尸进程 | 由init进程自动维护,不需额外干预 |
危害 | 父进程不调用 wait() / waitpid() 的话,那么保留的信息就不会释放,其进程号就会一直被占用,而系统所能使用的进程号是有限的,如果大量的产生僵死进程会占满进程表,导致系统无法创建新进程 | 通常无害,但如果子进程数量过多,仍会消耗系统资源 |
出现场景 | 常见于父进程未及时处理子进程终止状态的情况 | 常见于父进程异常终止而子进程仍需继续执行的情况 |