(嵌入式八股)第5章 操作系统(三)

5.21 用户空间与内核通信方式

1. 系统调用(System Call)

系统调用是用户程序通过内核执行特权操作的方式。用户程序通过系统调用向内核请求服务,如读写文件、创建进程等。

代码例程:文件操作系统调用

  • open():系统调用,用于打开文件。如果文件不存在,则会创建它。返回一个文件描述符。
  • write():系统调用,用于向打开的文件中写入数据。它将数据从缓冲区写入文件。
  • close():系统调用,关闭打开的文件并释放资源。
  • 在用户空间程序中,系统调用如 openwriteclose 使得程序能够向内核请求操作文件的服务。通过这些接口,用户程序能访问到硬盘上的文件,而不需要直接操作硬件。

    2. 文件接口(File Interface)

    文件接口是用户程序通过标准文件操作与内核进行通信的常见方式,允许用户程序通过文件进行数据的读写。

    代码例程:文件读写

  • fopen():打开文件用于读取操作。
  • fgets():从文件中读取一行内容到缓冲区。
  • fclose():关闭文件操作。
  • 文件接口通过标准的 C 库函数,如 fopen()fgets()fclose(),允许用户程序与内核中的文件系统进行交互。这是用户程序与内核进行数据交换和管理的一种常见方式。

    3. procfs

    /proc 是一个虚拟文件系统,用户空间程序通过读取和写入 /proc 文件系统的文件与内核通信,获取或修改内核状态和信息。

    代码例程:读取 /proc/cpuinfo

  • /proc/cpuinfo 提供了关于系统 CPU 的信息。
  • 使用 fopen() 打开文件,并通过 fgets() 逐行读取文件内容。
  • 通过读取 /proc 文件系统中的虚拟文件,用户空间程序可以获取系统级的硬件和内核状态信息。

    4. sysfs

    /sys 是另一个虚拟文件系统,用于提供内核、硬件设备的状态信息。通过读取 /sys 中的文件,用户空间程序可以查询或修改设备属性。

    代码例程:读取 /sys/class/net/eth0/address

  • /sys/class/net/eth0/address 提供了网络接口 eth0 的 MAC 地址。
  • 通过 fopen()fgets() 读取文件内容。
  • 通过访问 /sys 文件系统,用户程序可以查询硬件设备的属性和内核参数。这样可以动态地获取或修改设备信息,常用于设备管理和配置。

    5. 共享内存(Shared Memory)

    共享内存是一种进程间通信(IPC)机制,允许多个进程共享一块内存区域。这是最快的进程间通信方式,因为进程之间无需复制数据。

    代码例程:创建和使用共享内存

  • shmget():创建共享内存并返回一个标识符。
  • shmat():将共享内存附加到当前进程的地址空间。
  • shmdt():分离共享内存。
  • shmctl():删除共享内存。
  • 6. 设备文件(Device Files)

    设备文件是用户空间程序与内核之间进行通信的常见方式,设备文件代表了硬件设备,如串口、硬盘、字符设备等。

    代码例程:通过设备文件访问串口设备

  • open():打开设备文件(如串口设备文件 /dev/ttyS0)。
  • write():将数据写入设备文件,设备驱动程序会将数据发送到硬件。
  • close():关闭设备文件。
  • 通过设备文件,用户空间程序可以与内核中的硬件设备进行通信。该例子展示了如何通过打开、写入和关闭设备文件来与串口设备进行交互。

    5.22 进程、线程及之间区别

    1. 进程(Process)

    • 定义:进程是操作系统资源分配的基本单位,表示一个正在运行的程序。每个进程都有自己的地址空间、数据、程序计数器和系统资源。
    • 资源:每个进程拥有独立的地址空间和系统资源(如内存、文件描述符等)。进程之间相互独立,不共享资源。
    • 功能:进程是程序的实体,负责管理程序的运行,包含代码、数据以及程序的执行状态。
    • 创建与销毁:进程的创建和销毁需要较大的开销,因为操作系统需要分配和回收大量的资源。
    • 通信:进程之间不能直接共享数据,通常需要通过进程间通信(IPC)机制,如管道、消息队列、共享内存等进行数据交换。

    2. 线程(Thread)

    • 定义:线程是进程中的实际执行单位,是操作系统调度的最小单位。一个进程可以有多个线程,这些线程共享进程的地址空间和资源。
    • 资源:线程共享同一进程的地址空间和资源(如内存、文件描述符),但每个线程有自己的栈空间、程序计数器和局部变量。
    • 功能:线程负责在进程中执行具体的任务。线程之间的协作比进程之间的协作更加高效。
    • 创建与销毁:线程的创建和销毁比进程轻便,开销较小,因为线程之间共享资源,操作系统只需要为每个线程分配最小的资源。
    • 通信:线程之间通信更加方便,因为它们共享同一进程的内存,可以直接访问共享内存或通过互斥锁等机制同步数据。

    进程与线程的主要区别

    进一步分析:

    1. 内存和资源

    • 进程:每个进程都有自己的内存空间和资源,这意味着一个进程崩溃时不会直接影响到其他进程。但是,进程之间的通信需要通过特定的机制(如消息队列、管道、共享内存等)。
    • 线程:线程共享同一个进程的内存和资源,这使得线程间的通信更加高效,但也带来了同步问题(如数据竞争、死锁等)。如果一个线程崩溃,可能导致整个进程崩溃。

    2. 性能

    • 进程:由于每个进程都有自己的地址空间和资源,进程间的切换开销较大。创建和销毁进程需要更多的系统资源。
    • 线程:线程之间共享资源,创建和销毁线程的开销较小。线程间切换比进程切换更快,因此在需要频繁调度的情况下,使用线程可以提高性能。

    3. 安全性和稳定性

    • 进程:进程相互独立,避免了一个进程的崩溃影响其他进程。操作系统可以为每个进程分配不同的资源和优先级。
    • 线程:线程共享进程的内存,因此它们之间的错误(如访问未初始化的内存)可能导致整个进程崩溃。因此,线程的管理和同步需要特别注意。

    总结:

    • 进程是资源分配的基本单位,它有独立的地址空间和资源。每个进程相互独立,进程间通信相对较慢,但更加稳定。
    • 线程是执行单位,多个线程共享进程的资源,线程间通信更加高效。线程的创建和销毁较为轻量,但一个线程的崩溃可能影响整个进程的稳定性。

    5.23 进程的状态

    操作系统中的进程可以处于多种不同的状态,这些状态反映了进程在不同阶段的活动或等待。进程状态管理是操作系统中至关重要的部分,因为它决定了进程如何被调度和执行。以下是六种主要的进程状态:

    1. 就绪态(Ready)

    • 定义:就绪态是指进程已经准备好执行,只等待 CPU 调度。该进程已经分配了必要的资源,并且可以立即运行,只要操作系统为其分配 CPU 时间。
    • 特征:进程进入就绪态后,操作系统会将其放入就绪队列中,等待 CPU 分配时间。此时,进程已经准备好执行,但还没有被调度执行。
    • 典型操作:当一个进程的时间片用完后,它会从运行态转为就绪态,等待下次 CPU 调度。

    2. 运行态(Running)

    • 定义:运行态是指进程正在 CPU 上执行。进程从就绪态进入运行态后,会被操作系统调度到 CPU 上,并开始执行程序的指令。
    • 特征:在运行态下,进程会执行其代码,并使用 CPU 来进行计算和处理任务。运行中的进程是唯一可以执行的进程,除非它被抢占或执行完成。
    • 典型操作:一旦调度程序选择一个就绪态的进程并分配 CPU 时间片,进程就会进入运行态。

    3. 僵尸态(Zombie)

    • 定义:僵尸态进程是已经终止(退出)的进程,但是其父进程尚未调用 wait()waitpid() 来回收该进程的资源。此时,虽然进程已经完成执行,但它仍然保留在系统中,等待父进程清理。
    • 特征:在僵尸态下,进程的资源(如内存)已经释放,但进程的进程控制块(PCB)还没有被完全销毁。进程仅占用进程表中的一个条目,等待父进程清理。
    • 典型操作:一旦父进程调用 wait() 等待已终止子进程,僵尸进程将被完全清除。

    4. 可中断睡眠状态(浅度睡眠,Interruptible Sleep)

    • 定义:可中断睡眠状态表示进程正在等待某些事件或资源,但它仍然能够接收中断信号。如果有外部事件或信号发生,进程可以被唤醒并转为就绪态。
    • 特征:处于可中断睡眠的进程通常等待某些条件(如 I/O 操作完成)。如果收到信号(如 SIGINT),进程会被唤醒并进入就绪态。
    • 典型操作:一个进程发起 I/O 操作或等待某个条件时进入该状态,通常可以通过外部信号唤醒。

    5. 不可中断睡眠状态(不可中断等待,Uninterruptible Sleep)

    • 定义:不可中断睡眠状态表示进程正在等待某些资源(如 I/O 操作),并且在等待过程中不能被中断。进程必须等待所需资源或事件的完成,不能响应信号(如中断或终止信号)。
    • 特征:进程在此状态下无法被信号中断,通常用于对外部硬件或资源的等待,直到相应的资源准备好。此状态称为深度睡眠。
    • 典型操作:例如,进程等待硬盘 I/O 完成时,通常处于不可中断睡眠状态。

    6. 暂停态(Stopped)

    • 定义:暂停态表示进程被暂停,通常是由于接收到特定的信号(如 SIGSTOP 或 SIGTSTP)。在暂停态下,进程暂停执行,但它并没有完全结束,仍然存在于系统中,等待恢复。
    • 特征:进程处于暂停态时,它不再消耗 CPU 时间,但可以被操作系统恢复。恢复后,进程会转为就绪态,等待下次调度执行。
    • 典型操作:通过发送 SIGSTOP 信号暂停进程,通过 SIGCONT 信号恢复进程执行。

    总结:

    状态转换的过程

    1. 从就绪态到运行态:当 CPU 调度器选择一个进程并分配 CPU 时间片时,进程从就绪态转入运行态。
    2. 从运行态到睡眠态:如果进程在运行过程中需要等待某些资源(如 I/O 完成),它会进入睡眠态。如果是可中断睡眠,可以被信号唤醒;如果是不可中断睡眠,需要等待外部条件。
    3. 从睡眠态到就绪态:当等待的条件满足时,进程会从睡眠态转回就绪态,等待 CPU 调度。
    4. 从运行态到僵尸态:进程执行完毕,但其父进程尚未回收其资源,进程变为僵尸态。
    5. 从暂停态到就绪态:当进程收到恢复信号(如 SIGCONT)时,它会从暂停态恢复,进入就绪态。

    5.24 进程/线程选用的时机

    在操作系统设计中,选择进程还是线程取决于应用程序的需求,特别是在资源管理、任务隔离、并发性和实时性等方面。进程和线程都有其独特的优势和使用场景。以下是使用进程使用线程的具体情况分析。

    使用进程的情况

    1. 需要独立的地址空间和系统资源

    • 应用场景:当任务之间需要完全独立的环境,且需要较强的数据隔离时,应选择使用进程。每个进程有自己的内存空间和资源,不会直接影响其他进程。
    • 例子:运行多个独立的应用程序或服务,如浏览器和数据库服务器,它们需要独立的内存和文件资源。

    2. 需要更高的安全性和稳定性

    • 应用场景:如果一个任务的崩溃不应该影响其他任务的正常运行,使用进程可以提高系统的稳定性。进程是独立的,进程崩溃时不会直接影响其他进程。
    • 例子:在操作系统中,系统进程和用户进程应该分开,避免系统崩溃导致所有用户程序的崩溃。浏览器和操作系统的核心进程应该隔离,防止恶意网页或程序影响操作系统的稳定性。

    3. 并行计算需求

    • 应用场景:在多核处理器的情况下,进程可以充分利用多核的计算能力进行并行计算。每个进程可以在不同的 CPU 核心上独立执行,达到更高的计算效率。
    • 例子:大规模的科学计算或数据处理任务,例如分布式数据库和视频编码任务。

    使用线程的情况

    1. 共享数据和资源

    • 应用场景:如果多个任务需要频繁共享数据和资源,并且需要较高效的通信,使用线程是更好的选择。线程之间可以直接访问同一个进程的地址空间,因此共享数据和资源变得更方便。
    • 例子:在 Web 服务器中处理多个请求,多个线程可以共享数据库连接池,减少资源的占用和数据传输的开销。

    2. 轻量级任务

    • 应用场景:当任务较轻且需要频繁切换时,线程比进程更加轻量级。线程切换的开销远小于进程切换,因此当任务相对简单且需要快速调度时,线程更合适。
    • 例子:一个图形界面程序中的按钮点击事件处理,每个事件可以通过一个线程来处理,线程切换快速且开销小。

    3. 实时性要求

    • 应用场景:如果任务对实时性有较高的要求,线程能够快速响应并处理任务。线程在同一进程内共享资源,因此线程之间的调度和通信速度较快,适合实时任务的处理。
    • 例子:音视频处理、游戏开发等需要快速响应用户输入和数据处理的场景。多个线程可以在后台处理实时数据流,同时保持界面的响应性。

    进程与线程的对比总结

    总结

    • 使用进程:当需要隔离任务、提高系统稳定性或进行计算密集型任务时,使用进程。进程是操作系统中资源分配的基本单位。
    • 使用线程:当任务之间需要共享数据、高效通信并且任务轻量时,使用线程。线程之间的创建和销毁较为轻便,适合处理实时任务和频繁切换的任务。

    5.25 多进程的优缺点

    多进程的优点:

    1. 独立性

    • 每个进程有独立的内存空间和资源。当一个进程发生崩溃时,它不会影响到其他进程的执行。因此,进程之间相互独立,可以有效地避免一个进程的错误影响整个系统的稳定性。
    • 例子:如果浏览器进程崩溃,它只会影响当前打开的标签页,而其他标签页的浏览器进程不受影响。

    2. 安全性

    • 进程之间的内存是隔离的,进程无法直接访问或修改其他进程的内存空间。这种内存隔离为进程之间提供了较高的安全性,尤其是在处理敏感数据时。

    剩余60%内容,订阅专栏后可继续查看/也可单篇购买

    作者简介:仅用大半年时间0基础天坑急转嵌入式开发,逆袭成功拿下华为、vivo、小米等15个offer,面试经验60+,收藏20+面经,分享自己的求职历程与学习心得。 专栏内容:最新求职与学习经验,详细讲解了嵌入式开发的学习路径、项目经验分享、简历优化技巧、面试心得及实习经验,从测评,笔试,技术面,HR面,AI面,主管面,谈薪一站式服务,助你突破技术瓶颈、打破信息差,争取更多大厂offer。

    全部评论
    已老实
    点赞 回复 分享
    发布于 02-27 13:43 浙江

    相关推荐

    金三银四就要来了,很多朋友可能都有求职、跳槽、找工作的打算,免不了要参加各种各样的面试。有些朋友明明很优秀,但因为面试时候太紧张,导致没有发挥好而错过很好的机会。分享一个能够帮助你在面试中减少紧张情绪的方法:反客为主。意思就是,不要把自己当作面试者,而要把自己当作面试官。大多数朋友在找工作的时候一个很常见的心态,就是非常希望应聘的公司能够给自己一个工作机会。这种心态当然没错,但如果心里总是期待着这个结果,很容易患得患失,在面试的时候也自然就会紧张。而如果你反过来想,这场面试不仅仅是公司在考察你,更是你近距离全面考察公司的机会:这家公司的业务到底行不行、未来的老板专业能力怎么样、管理风格又是怎样的、这个职位的工作内容具体是什么、对你的要求又是什么…这些都是你在面试中需要尽可能地去弄清楚的问题。千万不要看轻自己。你确实是在被公司挑选,你确实也希望自己能被面试官看中然后拿到这份工作,但这不是你需要特别关注的,更不是你能够控制的。对你来说,你更应该去关注的,是从自己出发,你是不是愿意和对面的这些人合作、这家公司是不是配得上你、是不是值得你短则一两年、长则三五年每天都在这里上班。这个方法的关键,就是把自己放在和公司平等的位置上。用这个心态去面试,不仅能有效缓解你的紧张,更能帮助你收集到更多对你有用的信息,更好地帮助你做出下一步决策。 #牛客激励计划# #牛客创作赏金赛#
    点赞 评论 收藏
    分享
    评论
    5
    6
    分享

    创作者周榜

    更多
    牛客网
    牛客企业服务