笔试难点

61 阅读28分钟

细碎知识点

  • xor异或nor同或

  • strcpy遇到 '\0' 才会停止,要小心他没有结束符导致的终止异常,没有正确终止就可能导致继续复制其他的字符导致的缓冲区溢出

  • C++中值传递的方式有值传递、指针传递和引用传递

  • vim命令,dd删除当前行

  • awk "{print $3}"awk 是一个强大的文本处理工具。这里的 awk 命令使用了默认的输入字段分隔符(通常是空白字符,如空格或制表符),并尝试打印每一行的第三个字段($3)。字段是通过空白字符分隔的。

  • linux里面新添一个用户useradd / adduser都行

  • 中断参数不可以传参,也没有返回值

  • ext2,ext3,ext4都是linux的文件系统,4是最新的

  • C++的STL中的常用的容器,string、vector、list、stack、queue、map

  • int a1:30; 表示 a1 是一个30位的位域,需要4个字节来存储。

  • find是在文件系统中搜索符合条件的文件或目录。grep是搜索文件内容中包含指定字符串的行。

  • 在Windows环境下共享Unix/Linux中的用户目录的一个工具是Samba服务器

  • char型设备驱动需要实现的接口通常包括open、close、read、write、ioctl

  • 缺省函数的默认类型是 int

  • 转义字符最大255

  • main函数带参数的最多个数是两个,就像这样int main(int argc, char *argv[])

HTTP中的GET和POST的区别

好的,让我们用一个生活化的例子来聊聊HTTP中的GET和POST,就像你在图书馆借书一样。

想象一下,你是一个大学生,图书馆是你的服务器,而你想借的书就是服务器上的资源。

  1. GET请求

    • 假设你想借一本特定的书,你会走到图书馆的目录查询机前,输入书名进行搜索。这个过程就像是发送一个GET请求。你告诉图书馆(服务器)你想要什么(书名),图书馆会根据你的请求给你书的索引信息或者直接给你书。这个请求就像是在告诉别人:“嘿,给我看看那本书的信息。”
    • 重要的是,你只是在查询,并没有实际借走书,对图书馆的藏书没有任何影响。这就是GET请求的幂等性,不管你查询多少次,图书馆的书都不会少一本。
  2. POST请求

    • 现在,假设你要参加一个图书馆的问卷调查,你需要填写一些信息,比如你的姓名、学号、你对图书馆的建议等。你填完问卷,然后提交。这个过程就像是发送一个POST请求。你不仅仅是在查询,而是在提交数据给图书馆(服务器)。
    • 提交问卷就像是在告诉图书馆:“这是我的反馈,你们可以记录下来。”你的反馈可能会影响图书馆的服务,比如他们可能会根据你的建议改进。这就是POST请求不是幂等的原因,因为多次提交相同的问卷可能会导致图书馆收到多份相同的反馈。

在这个例子中,GET和POST的主要区别在于:

  • GET就像是在图书馆查询一本书,而POST就像是在图书馆提交一份问卷。
  • GET请求的信息(书名)是公开的,就像URL一样,大家都能看到。POST请求的信息(问卷内容)是私有的,只有图书馆(服务器)能看到。
  • GET请求不会改变图书馆的藏书,而POST请求(提交问卷)可能会影响图书馆的决策。

希望这个例子能帮助你更好地理解HTTP中的GET和POST请求!

大端小端答题可以这样写

image.png

这个函数可以用来判断系统是大端还是小端

#include <stdio.h>

int main() {
    unsigned int x = 1; // 声明一个无符号整型变量并赋值为1
    char *c = (char*)&x; // 将整型变量的地址转换为字符指针


   // 如果第一个字节是1,则为小端;如果是0,则为大端
    if (*c) {
        printf("Little-endian\n");
    } else {
        printf("Big-endian\n");
    }

   return 0;

image.png

线程和进程的区别

image.png

关键字volatile有什么含意?并给出三个不同的例子。

  • volatle是指易改变的。用他修饰的变量表明该变量是易发生改变的变量,每当优化器访问该变量时,都会重新读取该变量的值,而不是直接去找寄存器中找该变量的备份。 例子:
  1. 并发的硬件寄存器,如状态寄存器。
  2. 中断服务器的子程序访问的非自动变量。
  3. 多线程中被多个任务共享的变量。

POSIX线程库

POSIX线程库(pthreads)是一个在多核处理器上实现多线程的API,它遵循POSIX(可移植操作系统接口)标准。这个库提供了一套丰富的函数,用于创建和管理线程、同步线程间的操作以及线程间通信。POSIX线程标准被广泛地应用于UNIX、Linux、Mac OS X等操作系统中。

以下是一些使用pthreads时可能会用到的关键概念和函数:

  1. 线程创建

    • pthread_create:创建一个新的线程。
  2. 线程标识符

    • 每个线程都有一个唯一的标识符,通常用pthread_t类型表示。
  3. 线程同步

    • 包括互斥锁(mutexes)和条件变量(condition variables)等机制,用于控制多个线程对共享资源的访问。

    • pthread_mutex_init:初始化一个互斥锁。

    • pthread_mutex_lock:锁定互斥锁。

    • pthread_mutex_unlock:解锁互斥锁。

  4. 条件变量

    • 用于线程间的协调,可以让一个或多个线程在某些条件不满足时挂起,并在条件满足时被唤醒。

    • pthread_cond_init:初始化一个条件变量。

    • pthread_cond_wait:等待条件变量的信号。

    • pthread_cond_signal:向一个等待条件变量的线程发送信号。

  5. 线程取消

    • pthread_cancel:请求取消一个线程。
  6. 线程属性

    • 可以设置线程的属性,如栈大小、调度策略等。
  7. 线程结束和加入

    • pthread_exit:终止调用线程。
    • pthread_join:等待一个线程终止,并回收其资源。
  8. 线程安全函数

    • 一组标准C库函数的线程安全版本,如strtok_rrand_r等。

POSIX线程库是构建并发程序的强大工具,它允许程序员编写能够利用多核处理器优势的程序。使用pthreads时,程序员需要注意线程安全和同步问题,以避免竞态条件和死锁等并发问题。

TCP 拥塞控制方法共有四种:

  1. 慢开始( slow-start )
  2. 拥塞避免( congestion avoidance)
  3. 快重传( fast retransmit )
  4. 快恢复( fast recovery )

数据库系统中的三种基本数据模型是:

  1. 层次模型(Hierarchical Model)

    • 这种模型以树状结构组织数据,每个节点可以有多个子节点,但只能有一个父节点。
    • 数据库中的记录以树形结构表示,其中每个记录可以有多个子记录,但只能有一个父记录。
    • 层次模型适用于具有明确的上下级关系的数据组织,例如组织结构图。
  2. 网状模型(Network Model)

    • 网状模型允许数据以更复杂的网络结构组织,记录可以有多个父记录和多个子记录。
    • 这种模型比层次模型更加灵活,可以表示复杂的数据关系,如多对多关系。
    • 网状模型在某些复杂的应用场景中非常有用,例如复杂的库存管理系统。
  3. 关系模型(Relational Model)

    • 关系模型是目前最广泛使用的数据库模型,它使用表格(称为关系)来组织数据。
    • 在关系模型中,数据被组织成行和列,每行代表一个记录,每列代表一个字段。
    • 表与表之间通过键(如外键)来建立关系,允许数据的规范化存储和查询。
    • 关系数据库管理系统(RDBMS)如MySQL、Oracle、SQL Server等都是基于关系模型。

运算符优先级(!!!)

  1. 括号 ()
  2. 算术运算符 (+-*/%)
  3. 关系运算符 (<><=>===!=)
  4. 逻辑与运算符 (&&)
  5. 逻辑或运算符 (||)
  6. 赋值运算符 (=+=-=*=/=%=<<=>>=&=^=|=) image.png

C语言运算符优先级(超详细)-CSDN博客

各种指针定义的数组

image.png

C语言的用户标识符

  1. 第一个字符:标识符的第一个字符必须是字母(A-Z, a-z)或下划线(_),不能是数字。
  2. 后续字符:在第一个字符之后,标识符可以包含字母、下划线(_)和数字(0-9)。
  3. 大小写敏感:C语言是大小写敏感的,因此Identifieridentifier被视为两个不同的标识符。
  4. 保留字:标识符不能使用C语言的保留字,如intifwhilereturn等。
  5. 避免使用特殊字符:除了字母、数字和下划线之外,其他特殊字符(如空格、标点符号等)不能用于标识符

面向对象和面向过程两种不同思想的理解

  • 面向过程编程的核心是将程序看作一系列的步骤,通过不同的函数或子程序完成不同的任务这种编程思想通常侧重于算法的设计和优化,重视代码的执行效率和速度。面向过程编程的个显著特点是程序结构简单清晰,易于实现和调试。这种编程思想常被用于单纯的数据处理科学计算和简单的控制流程设计。
  • 相反,面向对象编程则是一种更加复杂和抽象的编程思想。它将程序看作一个由对象构成的集合,每个对象都具有特定的属性和行为。面向对象编程的核心是将程序设计为一个由对象互相协作完成任务的系统。这种编程思想侧重于抽象和封装,重视代码的可重用性和可维护性。面句对象编程的一个显著特点是程序结构复杂,但更具有灵活性与可扩展性。这种编程思想常被用于复杂的软件系统设计和开发。
  • 面向过程与面向对象两种编程思想各有优缺点,根据实际需求选择适合的编程思想是非常重要的。一般来说,当程序结构简单,任务单一,需要高效率和速度时,面向过程编程是一个不错的选择。但当程序结构复杂,需要进行多样化、灵活化的设计和扩展时,面向对象则更适合。

linux系统由哪五个子系统组成,各子系统的作用是什么?

Linux系统(内核)主要由进程调度(SCHED)、内存管理(MM)、网络接口(NET)和进程间通信(IPC)5个子系统组成,

  1. 进程调度 进程调度控制系统中的多个进程对CPU的访问,使得多个进程能在CPU中“微观串行,宏观并行地执行。进程调度处于系统的中心位置,内核中其他的子系统都依赖它,因为每个子系统都需要挂起或恢复进程。
  2. 内存管理 内存管理的主要作用是控制多个进程安全地共享主内存区域。当CPU提供内存管理单元(MMU)时,Linux内存管理对于每个进程完成从虚拟内存到物理内存的转换。
  3. 虚拟文件系统 Linux虚拟文件系统对底层隐藏了各种硬件的具体细节,为所有设备提供了顶层统一的接口。而目,它独立于各个具体的文件系统,是对各种文件系统的一个抽象。
  4. 网络接口 网络接口提供了对各种网络标准的存取和各种网络硬件的支持。在Linux中网络接口可分为网络协议和网络驱动程序,网络协议部分负责实现每一种可能的网络传输协议,网络设备驱动程序负责与硬件设备通信,每一种可能的硬件设备都有相应的设备驱动程序
  5. 进程间通信 进程间通信支持进程之间的通信,Linux支持进程间的多种通信机制,包含信号量、共享内存、消息队列、管道、UNIX域套接字等,这些机制可协助多个进程、多资源的互斥访问、进程间的同步和消息传递。

image.png

new/malloc 和 delete/free的区别

image.png

不用第三方交换数值

image.png

楼梯递归问题

image.png

UART、I2C、SPI、USB、IIS通信协议特点/

IIS (Inter-IC Sound)

  • 定义:IIS是一种用于音频数据传输的串行总线,它允许数字音频设备之间进行通信。IIS通常用于连接数字音频编解码器(CODECs)和数字信号处理器(DSPs)。

  • 特点

    • 数据传输:IIS可以传输立体声音频数据,每个通道的数据通常是16位或更高位宽。
    • 时钟信号:IIS使用一个单独的时钟信号(通常称为BITCLK或BCLK)来同步数据传输。
    • 帧同步:IIS使用帧同步信号(通常称为LRCLK或WS)来指示左右声道的切换。
    • 数据有效性:有时还包括一个数据有效信号(通常称为DOUT或BCK),用于指示数据传输的有效性。

参数调用问题

image.png

写一个“标准”宏,这个宏返回一个数组最高维的下标(如数组a[2][4][6],执行宏之后结果是2)。

#define HDINDEX(array)sizeof(array)/sizeof(array[0])//一个小技巧

死锁/死锁产生的四个必要条件/如何解除死锁

死锁是计算机科学中一个重要的概念,指的是两个或多个进程在执行过程中因争夺资源而造成的一种僵局。这些进程在等待其他进程释放它们所需的资源,但因为每个进程都在等待其他进程,所以没有一个进程能够继续执行。死锁发生必须同时满足以下四个条件,这四个条件通常被称为死锁的“必要条件”:

  1. 互斥条件(Mutual Exclusion)

    • 一个资源在一次只能被一个进程使用。如果资源可以被多个进程同时访问,那么这些进程必须以互斥的方式访问资源。
  2. 占有和等待条件(Hold and Wait)

    • 进程至少持有一个资源,并且在等待获取其他进程持有的资源。如果一个进程请求它尚未拥有的资源,而该资源被其他进程持有,那么它必须等待。
  3. 不可剥夺条件(No Preemption)

    • 一个进程在持有资源的同时,不能被其他进程强行剥夺。资源只能由持有它的进程自愿释放。
  4. 循环等待条件(Circular Wait)

    • 存在一种进程资源的循环等待关系。即存在一系列进程 {P1, P2, ..., Pn},其中每个进程 Pi 正在等待下一个进程 Pi+1(按索引加1)持有的资源(Pn 等待 P1持有的资源)。

如果系统中的进程和资源分配不满足这四个条件中的任何一个,死锁就不会发生。例如,如果资源可以被剥夺(即可以被其他进程抢占),那么即使其他条件都满足,死锁也不会发生,因为系统可以剥夺资源来打破循环等待。

避免死锁的策略通常包括破坏这四个条件之一或多个。例如:

  • 破坏占有和等待条件:要求进程在请求任何资源之前一次性请求它所需的所有资源。
  • 破坏不可剥夺条件:允许系统在检测到死锁风险时抢占资源。
  • 破坏循环等待条件:为所有资源类型分配一个线性顺序,并要求每个进程按照这个顺序请求资源。

通过这些策略,可以设计出避免死锁的系统或算法。

银行家算法

银行家算法(Banker's Algorithm)之所以得名,是因为它最初是为银行的事务管理系统设计的。这个算法由艾兹格·迪科斯彻(Edsger Dijkstra)提出,目的是为了确保即使在资源有限的情况下,银行的交易系统也能够安全地运行,避免因资源分配不当而导致的系统崩溃或死锁。

在银行系统中,每个客户可能需要多种类型的资源,例如不同的账户类型或服务。如果一个客户同时请求了多个资源,而这些资源在系统中是有限的,就需要一种机制来确保资源的分配不会导致死锁,即没有任何客户能够完成其事务。

银行家算法通过以下方式与银行业务相关联:

  1. 资源分配:在银行系统中,资源可以是账户、信用额度、服务等。算法确保资源的分配是安全的,即客户在请求资源时,系统能够保证所有客户最终都能完成其事务。

  2. 避免死锁:银行业务中,如果多个客户同时请求资源,并且这些资源的分配导致循环等待,就可能发生死锁。银行家算法通过预先检查资源分配的安全性来避免这种情况。

  3. 最大需求:在银行系统中,每个客户的最大需求可以看作是他们所需的最大信用额度或其他资源的总量。算法使用这个信息来评估资源分配的安全性。

  4. 需求矩阵:银行家算法使用需求矩阵来跟踪每个客户对每种资源的最大需求。这类似于银行跟踪每个客户对不同服务的需求。

  5. 安全性算法:银行家算法的核心是一个安全性算法,它检查在当前资源分配情况下,是否所有客户都能够完成其事务。这在银行系统中非常重要,因为需要确保所有交易都能顺利进行。

因此,银行家算法的名称来源于它最初是为了解决银行系统中的资源分配问题而设计的。尽管这个算法最初是为银行业务设计的,但它的原理也被广泛应用于计算机科学中的资源分配和死锁避免问题。

数模转换器

image.png

pragma pack

#pragma pack 是一种预处理指令,用于在 C 或 C++ 编程中控制数据结构的内存对齐和打包方式。这个指令告诉编译器如何对结构体(struct)或联合体(union)的成员进行对齐,以及在成员之间填充多少字节。

基本语法:

#pragma pack(alignment)
  • alignment:指定对齐的字节数。这是编译器在成员之间插入填充字节的依据。

作用:

  1. 内存对齐:确保数据结构的成员按照特定的边界对齐,这有助于提高数据访问的效率。
  2. 结构体大小调整:通过改变对齐方式,可以调整结构体占用的内存大小。

使用场景:

  • 当你需要确保数据结构的内存布局符合特定的硬件要求时。
  • 当你需要最小化数据结构的大小,以节省内存或满足特定接口的要求时。

示例:

#pragma pack(push, 1) // 保存当前的对齐设置,并设置为1,即无对齐

struct MyStruct {
    char a;   // 1 byte
    int b;    // 4 bytes,但由于#pragma pack(1),实际上只有1 byte的填充
    char c;   // 1 byte
};

#pragma pack(pop) // 恢复到之前的对齐设置

在这个示例中,#pragma pack(push, 1) 指令将结构体的对齐设置为1,这意味着编译器不会在成员之间插入任何额外的填充字节,除非这是为了满足成员自身的对齐要求。#pragma pack(pop) 指令则恢复到之前的对齐设置。

请注意,过度使用 #pragma pack 可能会导致代码的可移植性降低,因为不同的编译器和平台可能有不同的默认对齐规则。此外,它也可能影响代码的性能,因为非自然对齐的数据访问可能较慢。因此,在使用 #pragma pack 时应该谨慎,并考虑到这些因素。

IPv6的地址长度

IPv6(Internet Protocol version 6)的地址长度为128位。与IPv4相比,IPv4地址是32位长,分为4个8位的字节,通常以点分十进制表示(例如:192.168.1.1)。而IPv6地址则提供了极大的地址空间,可以表示为8组4个十六进制数字,每组之间用冒号(:)分隔。

例如,一个典型的IPv6地址可能看起来像这样:

2001:0db8:85a3:0000:0000:8a2e:0370:7334

IPv6地址的这种表示方法允许更简洁的书写方式,例如可以省略连续的零:

  • 连续的零可以用一个双冒号(::)来代替,但在整个地址中只能使用一次以避免歧义。

例如,上面的地址可以简化为:

2001:db8:85a3::8a2e:370:7334

IPv6的这种设计不仅大幅增加了可用的IP地址数量,还提高了网络的可扩展性和效率。

驱动加载 insmod/modprobe区别

image.png

memcpy

在C语言中,当使用memcpy函数进行内存复制时,如果源内存区域和目标内存区域有重叠,即源和目标内存区域有部分或全部相同,就称为“重叠区域”。在这种情况下,memcpy的行为是未定义的(undefined behavior),意味着程序可能会产生不可预测的结果。 比如自己复制自己这种

memcpy(&data[2], data, MAX_DATA_SIZE / 2);

互斥锁怎么用

指针地址在数组中的移动

  • test是一个char类型的数组,每个元素占用1个字节。
  • test_p是一个指向int的指针,它被初始化指向test数组的第一个元素。

test_p作为指针使用时,它的移动量是由它所指向的数据类型决定的。由于test_p是指向int的指针,而int通常是4个字节(在大多数现代系统上),所以每次递增test_p(例如,在test_p++test_p[1]的情况下)都会移动4个字节。

即使test是一个char数组,test_p作为指向int的指针,它仍然会按照int的大小来移动。这意味着:

  • test_p指向test[0],地址为&test[0]
  • test_p + 1将指向test[1]后面的第四个字节,即&test[4],因为指针按照int的大小移动。
  • 同样,test_p[1]将访问从test[0]开始的第四个字节处的值,也就是test[4]

因此,如果test的地址是0x12345678,那么test_p[1]实际上是指向test数组中第五个字节的地址

image.png

中断下半段(bottom half of interrupt)处理

又称为软中断或BH(Bottom Half),是操作系统中处理中断的一种机制。当硬件中断(top half)发生时,操作系统的中断处理程序(top half handler)会立即响应,但为了不阻塞其他中断,它通常会快速完成一些工作,然后将耗时的处理任务推迟到中断下半段来完成。 以下是中断下半段处理机制的一些选项:

  1. tasklet

    • Tasklets 是Linux内核中的一种轻量级线程,用于执行中断下半段处理。
    • 它们在中断处理的上下文中运行,并且可以安全地使用某些内核资源。
  2. 工作队列

    • 工作队列是一种机制,可以将工作项排队,然后在软件中断或内核线程中处理。
    • 工作队列允许中断处理程序快速返回,而将实际的工作推迟到以后执行。
  3. 软中断

    • 软中断是中断下半段的一种实现,它允许中断处理程序将一些工作推迟到内核的其他部分来完成。
    • 软中断处理程序可以在内核的任何安全点执行,通常在进程上下文中运行。

MMU是什么

image.png

const用在指针上

image.png

const和define的对比
  • const的优点

    • 提供类型安全。
    • 作用域限制,减少命名冲突。
    • 可以利用编译器优化。
    • 更好的调试支持。
    • 适用于需要保持值不变且需要类型信息的场景。
  • #define的优点

    • 可以定义复杂的表达式和语句。
    • 可以在编译前进行条件编译。
    • 没有存储开销。
    • 适用于简单的文本替换,如条件编译开关或编译时常量。

总的来说,const更适合定义具有明确类型的常量,而#define更适合用于条件编译和简单的文本替换。在现代C编程实践中,推荐使用const来定义常量,因为它提供了更好的类型安全和作用域控制,从而提高了代码的可维护性和可读性。

Linux下计算磁盘大小

  1. 包含头文件:程序包括了三个头文件,分别是stdio.hstdlib.hsys/vfs.h,这些文件提供了输入输出、标准库函数和文件系统信息的功能。
  2. 定义主函数:main函数是程序的入口点。
  3. 声明变量:声明了一个struct statfs类型的变量diskInfo,用于存储文件系统的状态信息。
  4. 检测磁盘状态:使用statfs函数来获取根目录"/"的文件系统状态信息。如果statfs函数调用失败,程序将使用exit(1)退出。
  5. 计算可用空间:如果statfs调用成功,程序将计算磁盘的可用空间。这里有一个错误,变量名diskInfo在计算时被错误地写成了disklnfo。计算方法是将f_bavail(可用的块数)乘以f_bsize(每个块的大小),然后将结果向右移动20位,相当于除以1024 * 1024,从而将字节转换为MB。
  6. 输出结果:使用printf函数输出计算得到的磁盘可用空间。
  7. 程序结束:返回0表示程序正常结束

image.png

针对没有mmu的arm芯片,其操作系统可以用

uClinux, Uc/os-ll,RTOS

DMA

简述Linux MMU的工作原理,简述linux内核将虚拟地址转换成物理地址的过程

好的,让我们详细分析Linux内存管理单元(MMU)的工作原理,以及Linux内核如何将虚拟地址转换为物理地址。

1. Linux MMU 的工作原理

内存管理单元(MMU)是计算机硬件的一部分,负责将虚拟地址转换为物理地址。在Linux系统中,MMU的工作原理如下:

  1. 地址转换:当程序访问内存时,CPU会生成一个虚拟地址,MMU将这个虚拟地址转换为物理地址。
  2. 页表:Linux使用页表来实现地址转换。每个进程都有自己的页表,存储在进程的内存管理数据结构中。
  3. 页表项:页表由多个页表项(PTE)组成,每个PTE包含虚拟页号和物理页号的映射关系,以及访问权限和状态信息。
2. 虚拟地址到物理地址的转换过程

在32位体系结构中,虚拟地址到物理地址的转换过程如下:

  1. 虚拟地址结构:32位虚拟地址可以分为三个部分:

    • 页目录号(10位)
    • 页表号(10位)
    • 页内偏移(12位)
  2. 查找页目录

    • 使用虚拟地址的高10位作为页目录号,从页目录基址寄存器(CR3)中获取页目录的物理地址。
    • 在页目录中查找对应的页表号。
  3. 查找页表

    • 使用虚拟地址的中间10位作为页表号,从上一步获取的页表物理地址中查找对应的页表。
    • 在页表中查找对应的页号。
  4. 计算物理地址

    • 使用虚拟地址的低12位作为页内偏移。
    • 将页表项中的物理页号与页内偏移组合,得到完整的物理地址。
  5. 访问权限检查

    • 在访问内存时,MMU会检查页表项中的访问权限,确保进程有权访问该内存区域。
    • 如果权限不足,会触发缺页异常。
  6. 缓存

    • 为了提高地址转换的效率,Linux使用TLB(Translation Lookaside Buffer)作为页表项的缓存。
    • TLB存储最近使用的页表项,减少了页表查找的时间。
3. 缺页处理

当虚拟地址对应的物理地址不在内存中时,会触发缺页异常。Linux内核会执行以下步骤处理缺页:

  1. 检查页表项:内核检查页表项,确定是缺页还是其他错误(如权限错误)。
  2. 分配物理内存:如果需要,内核会分配物理内存页。
  3. 读取数据:从磁盘或其他存储设备读取数据到物理内存中。
  4. 更新页表项:内核更新页表项,将虚拟页号映射到新的物理页号。
  5. 重新执行指令:处理完缺页后,CPU会重新执行导致缺页的指令。

通过以上步骤,Linux内核实现了高效的内存管理,确保了进程能够安全、有效地访问内存资源。

简述一下Linux中几种多路复用机制(select/poll/epoll)的适用场景及优缺点对比

不调用库函数编写strcpy,memcpy…………

链表题

交换分区/交换文件/及其作用

  • swap分区通常被称为交换分区,这是一块特殊的硬盘空间,即当实际内存不够用的时候,操作系统会从内存中取出一部分暂时不用的数据,放在交换分区中,从而为当前运行的程序腾出足够的内存空间。
  • 优点是,通过操作系统的调度,应用程序实际可以使用的内存空间将远远超过系统的物理内存。由于硬盘空间的价格远比RAM要低,因此这种方式无疑是经济实惠的。当然,频繁地读写硬盘,会显著降低操作系统的运行速率,这也是使用swap交换分区最大的限制。 在C语言中,函数参数默认是按值传递的,这意味着函数接收的是参数值的副本,而不是参数本身的引用。因此,函数内部对参数值的任何修改都不会影响原始变量。
交换数值的问题

对于 swap_int 函数:

void swap_int(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

在这个函数中,ab 是原始变量 ab 的副本。在函数内部,ab 的值被交换了,但是这个交换只影响函数内部的局部副本,并不会影响调用函数时传递的原始变量。

例如,当 swap_int(a, b); 被调用时,a 的值是10,b 的值是5。在函数内部,ab 的副本被交换了,但是原始变量 ab 的值不会改变。因此,函数调用结束后,原始变量 a 仍然是10,b 仍然是5。

这就是为什么 swap_int 函数不会改变 main 函数中 ab 的值的原因。要实现交换效果,可以使用指针参数来直接修改原始变量的值:

void swap_int(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

然后,你可以这样调用函数:

int a = 10, b = 5;
swap_int(&a, &b);

这样,ab 的值就会被交换。

二叉树遍历方式

  1. 前序遍历:先访问根节点,然后遍历左子树,最后遍历右子树。
  2. 后序遍历:先遍历左子树,然后遍历右子树,最后访问根节点。

转义字符

image.png

COM的线程模型

image.png

用两个栈实现队列的功能

image.png

---rw--s--T 这个权限字符串可以分解为以下部分:

  1. 第一个字符 --- 表示这是一个文件(如果是 d 则表示目录)。
  2. 接下来的三个字符 rw- 表示文件所有者有读(r)和写(w)权限,但没有执行(x)权限。
  3. 接下来的三个字符 s-- 表示与文件所有者同组的用户有特殊权限(s),但没有读(r)、写(w)或执行(x)权限。这里的 s 表示如果文件是可执行的,那么执行该文件的用户将获得文件所有者的用户ID(UID)。
  4. 最后三个字符 T 表示对其他用户设置了粘滞位(sticky bit),但没有读(r)、写(w)或执行(x)权限。粘滞位通常用于公共目录,如 /tmp,以防止用户删除或移动其他用户的文件。

所以,---rw--s--T 表示这是一个文件,文件所有者有读写权限,同组用户有特殊权限但没有其他权限,其他用户没有权限,并且设置了粘滞位。