嵌软面试一百问

目录

c语言

1.关键字

static

extern

const

Volatile

2.指针/数组

在C中,传进一个数组之后,自定义一个函数,行参的使用和数组使用的注意事项 数据区放什么,有多大?

在C语言中什么时候用二级指针?

怎么定义一个整形5个元素的数据指针

strcopy strncopy有什么区别

malloc中有部分空间没有被覆掉怎么办?

指针数组和数组指针有什么区别?

如何在C语言中定义一个能够变长的数组空间?

3.预编译

if not define一般有啥用

包头文件“”和<>有啥区别?

文件IO

1.目录

目录操作流程及函数名字

目录的遍历怎么实现?

2.文件

知道哪几种IO模型?

怎么实现文件的拷贝?

复制文本文件怎么操作

fgets和fputs文件的拷贝是怎么实现的?

IO学了什么?

进程线程

1.线程进程区别

线程和进程的概念,区别,以及什么时候用线程什么时候用进程?

线程什么时候互斥,什么时候同步?

进程的目的?

线程有什么作用?

进程间为什么要通信?有几种方式?

进程状态的切换有哪几种?

父与子运行期间的关系是什么?

无名管道和有名管道的创建流程

创建管道-打开-读写管道-关闭管道-卸载管道

编译多任务程序时是首选进程还是线程

2.锁

线程间为什么要加锁?

你知道锁吗?讲下自旋锁和互斥锁的区别,你知道读写锁吗?

为什么多线程需要互斥,多进程不需要?

什么是阻塞?

互斥、同步的概念?

3.多路io

在少事件时,epoll和select那个好?

epoll和poll和select的区别

epoll和线程池的关系和区别?

epoll能否代替线程池?

线程池和多进程的区别?

网络通信

1.http

http的端口号是什么?

http是干什么的,中文名是什么,主要用在什么地方?

端口号是干什么用的?

http请求报文的格式是什么?

http交互报文的格式?

描述一下http的交互过程?

2.tcp/udp

TCP、UDP的区别?

TCP的四层应用是什么?

TCP为啥安全可靠?

TCP/IP分几层,每层的核心任务是什么?

怎样能使UDP稳定?

设计一个点对点通信的软件,选择TCP还是UDP,为什么?

3.其他

应用层的协议是什么?

熟悉那些协议?下载用什么协议?

说一下wireshark,他有哪些功能

应用层的协议有哪些?

数据结构

链表的作用?

数据结构的框架?

链表和数组的区别?

链表头插尾插那种效率高?

简单说一下链表查重,结束的条件是什么?

数组和链表的优缺点?

单链表和双链表的区别?

如果知道某个节点的值,想删除怎么操作?

链表、栈、队列之间的区别?

常用的数据结构有哪些?

栈、队列有什么相同点?

数列A、B、C输出A、C、B用栈的方式可以实现吗?

数据结构对之前的编程有什么帮助?

如何实现二叉树的层序遍历?

广度优先遍历,深度优先遍历的区别?

优先选择哪种链表用到编程中去?

数据库

数据库的流程。

都了解那些数据库?

各个数据库的异同?

用过哪些SQL的编程语言?

数据库的工作原理?

查数据库如何升序打印

编译程序如何选择数据库

基础知识

Linux当中栈默认有多大?

singal作用

什么是同步?什么是异步?

栈区放什么?

内存分布图?有那几个区?

声明和定义的区别

编译程序什么时候会用多任务

什么是内存泄露、内存碎片、内存溢出?

内存片的概念?

什么是MAC地址?

什么是内存大小端?如何测试大端?

什么是原子操作?

算法

冒泡排序的思想?

知道那些排序算法?简单描述一下选择排序的思想。

什么是时间复杂度,常用的排序算法的时间复杂度

冒泡排序和快速排序那种更好?

快速排序稳定吗?

不想遍历元素但是想找到某些特定元素怎么实现?

冒泡排序和快速排序那种更稳定?什么是稳定?

插入法排序的概念

命令

查看网络状态用什么命令?

查进程用什么命令?

用过哪些进程相关命令?

与文件相关的命令有哪些?

GDB常用的命令有哪些?

列举5个操作进程的命令?

库函数用men1和men2查询有什么区别?

操作

是否可以快速定位段错误?

如何调试段错误?

在Linux操作系统中怎么获得内存的大小?

如何测试内存的大小?

你都用过哪些调试手段(方法)?你知道检测内存泄漏的原理

吗?踩内存是什么(关于越界访问)?越界访问你了解多少?当你

的程序发生越界访问,你怎么调试?

如何利用CPU占用率查看内存信息?

硬件

arm的移植、裁剪、中断处理机制

中断和信号的区别?你知道软中断吗?

那你知道中断上下文吗?那你知道信号吗?你如何使用信号,信

号的处理函数要注意哪些?如果我在信号处理函数中malloc空间

会怎么样?


注意:对于面试八股文我们要理性看待,先理解后记忆方为上策。

c语言

1.关键字

  • static

一、静态局部变量

延长局部变量的生命周期,当在函数内部声明一个局部变量为static时,这个变量的生命周期会从定义开始一直持续到程序结束,而不是仅在函数执行期间存在,且只会被初始化一次。

二、静态全局变量

  1. 限制变量的作用域,静态全局变量只能在定义它的源文件中被访问,其他源文件无法直接访问。
  2. 避免命名冲突   - 当多个源文件中可能有相同名称的全局变量时,使用静态全局变量可以避免命名冲突问题。

三、静态函数

1. 限制函数的作用域
   - 静态函数只能在定义它的源文件中被调用,其他源文件无法直接调用。

2. 封装内部实现 - 对于一些不希望被其他源文件访问的辅助函数,可以声明为静态函数,以提高代码的封装性和可维护性。

  • extern

在 C 语言中,extern关键字主要用于声明外部变量和函数,以便在一个文件中使用在其他文件中定义的变量和函数。以下是其具体用法:

一、声明外部变量

二、声明外部函数

  1. 跨文件调用函数
  2. 库函数的使用

  • const

一、修饰变量

 1. 定义常量

- 用`const`修饰的变量在程序运行过程中其值不能被改变,类似于常量。

- 例如: const int MAX_VALUE = 100; 

- 这里`MAX_VALUE`在整个程序中都保持为 100,不能被修改。

2. 增强程序的可读性和可维护性

- 通过使用`const`明确标识出某些不应该被修改的值,使其他程序员在阅读代码时能更容易理解这些变量的特殊性质。

3. 避免意外修改

- 可以防止由于错误的代码操作而导致变量被意外修改,提高程序的稳定性和可靠性。

二、修饰指针

 1. `const`修饰指针指向的内容

- 例如:“`const int *ptr`”表示指针`ptr`指向的内容是常量,不能通过`ptr`来修改其指向的整数的值。

- 但指针本身可以指向其他地址。

2. `const`修饰指针本身

- 例如:“`int * const ptr`”表示指针`ptr`本身是常量,一旦初始化后就不能再指向其他地址,但可以通过`ptr`修改其指向的内容。

3. `const`同时修饰指针和指向的内容

- 例如:“`const int * const ptr`”表示指针`ptr`本身和它指向的内容都是常量,既不能修改指向的内容,也不能让指针指向其他地址。

三、修饰函数参数

 1. 保护传入的参数不被函数内部修改

- 当函数参数是指针或引用类型时,使用`const`修饰可以确保函数内部不会意外修改传入的参数。 - 例如: void printValue(const int *ptr) { printf("%d\n", *ptr); // 这里不能修改 *ptr 的值 } 

2. 提高函数的通用性和可维护性

- 使函数能够接受常量和非常量的参数,同时明确表明函数不会修改传入的参数,增强了代码的可读性和可信赖性。

四、修饰函数返回值

 1. 返回常量值

- 当函数返回值被`const`修饰时,表示返回的是一个常量值,不能通过这个返回值来修改其指向的内容(如果是指针类型的返回值)。

- 例如:  const int *getAddress() { static int value = 10; return &value; } 

- 通过这种方式可以限制对函数返回的指针所指向内容的修改。

2. 增强函数返回值的安全性

- 防止意外修改函数返回的值,尤其是在返回指针或引用类型时,有助于提高程序的稳定性。

  • Volatile

1. 确保变量的可见性:

- 在多线程环境中,每个线程都有自己的工作内存(缓存),线程对变量的修改可能不会立即同步到主内存,其他线程也可能无法及时看到该修改。而使用`volatile`修饰的变量,当一个线程修改了该变量的值后,其他线程能够立即看到这个修改,保证了变量在多线程间的可见性。 - 例如,两个线程同时操作一个`volatile`修饰的共享变量,当一个线程修改了该变量的值,另一个线程能够及时获取到最新的值,而不是使用之前缓存中的旧值。

2. 防止编译器优化:

- 编译器为了提高程序的执行效率,可能会对代码进行优化。在一些情况下,这种优化可能会导致程序出现错误。如果一个变量被`volatile`修饰,编译器会严格按照代码的顺序对该变量进行读写操作,不会对其进行优化。

- 例如,在一个循环中不断读取某个变量的值,如果该变量没有被`volatile`修饰,编译器可能会认为变量的值在循环过程中不会改变,从而只读取一次变量的值并将其缓存起来,后续的循环中直接使用缓存的值。但如果该变量的值可能在循环过程中被其他线程或外部因素修改,那么这种优化就会导致程序出现错误。使用`volatile`修饰该变量后,编译器会在每次循环中都重新读取变量的值,保证程序的正确性。

3. 用于硬件相关的编程:

- 在与硬件交互的程序中,硬件寄存器的值可能会随时发生变化,例如在嵌入式系统中,硬件设备的状态可能会不断改变,对应的寄存器的值也会随之变化。使用`volatile`修饰这些与硬件寄存器相关的变量,可以确保程序能够正确地读取和写入硬件寄存器的值。

- 例如,当读取一个传感器的数据时,传感器的值可能会不断变化,使用`volatile`修饰对应的变量可以保证程序每次读取到的都是最新的值。

总之,`volatile`关键字在 C 语言中是一个非常重要的关键字,它可以保证程序在多线程环境、与硬件交互等情况下的正确性。但是,过度使用`volatile`关键字可能会降低程序的性能,因此应该根据实际情况谨慎使用。

2.指针/数组

  • 在C中,传进一个数组之后,自定义一个函数,行参的使用和数组使用的注意事项 数据区放什么,有多大?

一、行参的使用

 1. 数组作为参数的形式

- 可以直接使用数组名作为参数传递给函数,也可以使用指针来接收数组参数。

- 例如:  void function(int arr[], int size); void function(int *arr, int size);

 - 这两种形式在函数内部的使用方式是相同的。

2. 传递数组大小

- 由于在 C 语言中,数组作为参数传递时会退化为指针,所以函数无法直接知道数组的大小。通常需要额外传递一个参数来表示数组的大小。

- 例如:  void printArray(int arr[], int size) { for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); }  

二、数组使用的注意事项

 1. 不要越界访问

- 在函数内部使用数组时,要确保访问的下标在合法范围内,避免越界访问导致未定义行为。

- 例如:  void processArray(int arr[], int size) { for (int i = 0; i <= size; i++) { // 错误,可能越界访问 // 操作数组元素 } }

2. 注意数组的存储方式

- C 语言中的数组在内存中是连续存储的,这意味着可以通过指针算术来访问数组中的元素。

- 例如:  void accessArrayElements(int arr[], int size) { int *ptr = arr; for (int i = 0; i < size; i++) { printf("%d ", *(ptr + i)); } printf("\n"); }

三、数据区和大小

 1. 数据区的位置

- 局部数组通常存储在栈上,而全局数组和静态数组存储在数据段中。动态分配的数组(使用`malloc`等函数)存储在堆上。

- 例如:  // 全局数组 int globalArray[10]; int main() { // 局部数组 int localArray[5]; // 动态分配的数组 int *dynamicArray = (int *)malloc(8 * sizeof(int)); return 0; }

 2. 大小限制

- 栈的大小通常是有限的,不同的操作系统和编译器可能有不同的限制。如果局部数组过大,可能会导致栈溢出错误。

- 数据段的大小也有限制,但通常比栈的空间大一些。

- 堆的大小通常可以通过操作系统进行调整,但过度分配可能会导致内存耗尽。 总的来说,在 C 语言中使用数组作为函数参数时,要注意正确传递数组大小,避免越界访问,并了解数组在不同数据区的存储方式和大小限制,以确保程序的正确性和稳定性。

  • 在C语言中什么时候用二级指针?

一、作为函数参数传递指针的地址

 1. 修改指针的值

- 当需要在函数内部修改指针本身(而不是指针所指向的值)时,可以使用二级指针作为函数参数。

2. 动态分配多维数组

- 在动态分配二维或更高维数组时,通常需要使用二级指针或更高阶的指针。

二、处理复杂的数据结构

 1. 链表和树等数据结构

- 在链表中,每个节点通常包含一个指向下一个节点的指针。如果要修改链表的头指针,就需要传递头指针的地址,即二级指针。

 - 在二叉树等更复杂的数据结构中,也可能需要使用二级指针来修改树的根节点等关键指针。

2. 函数返回多个值

- 可以使用二级指针作为函数的参数,让函数通过修改二级指针所指向的指针来返回多个值。

总之,二级指针在 C 语言中主要用于需要修改指针本身、处理复杂数据结构以及实现一些特殊的编程需求的场景。使用二级指针时要小心内存管理,确保正确地分配和释放内存,以避免内存泄漏和悬空指针等问题。

  • 怎么定义一个整形5个元素的数据指针

在 C 语言中,可以使用以下方式定义一个指向包含 5 个整数元素的数组的指针: 

int (*ptr)[5];

这里`ptr`是一个指针,它指向一个包含 5 个整数的数组。

例如,可以这样使用这个指针:

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int (*ptr)[5] = &arr;

    for (int i = 0; i < 5; i++) {
        printf("%d ", (*ptr)[i]);
    }

    return 0;
}

在这个例子中,定义了一个包含 5 个整数的数组`arr`,然后定义了指针`ptr`并让它指向这个数组。通过`(*ptr)[i]`的方式可以访问数组中的元素。

  • strcopy strncopy有什么区别

一、函数原型及功能概述

 1. `strcpy`函数

- 原型:`char *strcpy(char *dest, const char *src)`。

- 功能:将源字符串`src`复制到目标字符串`dest`,直到遇到源字符串中的空字符'\0'为止,并且会在目标字符串的末尾自动添加一个空字符'\0'以确保目标字符串是一个以空字符结尾的字符串。

2. `strncpy`函数

- 原型:`char *strncpy(char *dest, const char *src, size_t n)`。

- 功能:将源字符串`src`中的最多`n`个字符复制到目标字符串`dest`。如果源字符串的长度小于`n`,则在目标字符串中填充空字符'\0'直到复制了`n`个字符为止。如果源字符串的长度大于或等于`n`,则目标字符串不会以空字符结尾。

二、使用场景差异

 1. `strcpy`

- 适用于需要完整复制一个以空字符结尾的字符串的情况,当确定源字符串是一个合法的以空字符结尾的字符串且目标字符串有足够的空间容纳源字符串时,可以安全使用。

2. `strncpy`

- 当需要控制复制的字符数量以防止缓冲区溢出时非常有用,特别是在处理可能包含不可预知长度的字符串或者需要截断字符串时。

三、安全性考量

 1. `strcpy`

- 由于不限制复制的字符数量,如果目标字符串的空间不足,可能会导致缓冲区溢出,这是一种非常危险的安全漏洞,可能被恶意利用来执行任意代码。

- 例如:如果目标字符串的长度小于源字符串的长度,`strcpy`会继续复制,超出目标字符串的边界,覆盖相邻的内存区域。

2. `strncpy`

- 虽然可以通过指定复制的字符数量来避免缓冲区溢出,但如果源字符串的长度大于或等于指定的长度`n`,目标字符串可能不会以空字符结尾,这可能会在后续对目标字符串的操作中导致问题,除非手动添加空字符。

- 例如:在使用`strncpy`后,如果没有正确检查目标字符串是否以空字符结尾,可能会导致在使用字符串函数(如`printf`等)时出现不可预期的行为。

总之,在使用这两个函数时,需要根据具体的需求谨慎选择,并注意确保目标字符串有足够的空间和正确的结束标志,以避免潜在的安全问题和错误。

  • malloc中有部分空间没有被覆掉怎么办?

一、确定未覆盖空间的影响

  1. 数据完整性考量
    • 如果未覆盖的空间中可能包含敏感信息,如密码、个人数据等,那么这可能会导致安全风险。例如,在一个处理用户数据的程序中,如果分配的内存区域未被完全覆盖,旧的数据可能会被意外泄露。
    • 如果未覆盖的空间不会影响程序的正常功能和数据完整性,可以暂时忽略。但在一些对内存使用要求严格的场景,如嵌入式系统或安全关键型应用中,即使是少量未覆盖的空间也可能需要被妥善处理。

二、处理未覆盖空间的方法

在使用malloc分配内存后,可以立即使用memset等函数对内存进行初始化,将所有字节设置为特定的值,通常是 0。

  • 指针数组和数组指针有什么区别?

指针数组是数组,数组指针是指针

  • 如何在C语言中定义一个能够变长的数组空间?

1.使用动态内存分配(malloc 和 free)

2.使用可变长度数组(VLA,C99 特性)

3.使用链表等数据结构

3.预编译

  • if not define一般有啥用

1.防止头文件重复包含

2.条件编译

  • 包头文件“”和<>有啥区别?

    1.尖括号(<>):当使用尖括号包含头文件时,编译器会首先在预定义的标准头文件搜索路径中查找头文件。这些搜索路径通常包括编译器安装目录下的包含文件目录等。

    2.引号(“”):当使用引号包含头文件时,编译器会首先在当前源文件所在的目录中查找头文件,如果没有找到,再按照预定义的搜索路径进行查找。

文件IO

1.目录

  • 目录操作流程及函数名字

1.mkdir  :  int mkdir(const char *pathname, mode_t mode);

2.rmdir  :    int rmdir(const char *pathname);

3.getcwd :    char *getcwd(char *buf, size_t size);

4.chdir  :  int chdir(const char *path);

  • 目录的遍历怎么实现?

opendir   :

DIR *opendir(const char *name);

readdir   :

struct dirent *readdir(DIR *dirp);

closedir  :

int closedir(DIR *dirp);

2.文件

  • 知道哪几种IO模型?

一、阻塞 I/O(Blocking I/O)

 1. 工作原理

- 当进程调用一个 I/O 操作时,如果数据尚未准备好,进程会被阻塞,进入等待状态,直到数据准备好并且 I/O 操作完成后,进程才会被唤醒继续执行。

- 例如,在进行文件读取操作时,如果文件中的数据还没有被操作系统加载到内存中,调用`read`函数的进程会被阻塞,直到数据可以被读取为止。

2. 特点

- 简单易懂,编程模型直观。

- 但是在 I/O 操作期间,进程无法进行其他任务,导致资源利用率低,特别是在处理大量并发 I/O 请求时性能较差。

二、非阻塞 I/O(Non-blocking I/O)

 1. 工作原理

- 当进程发起一个 I/O 操作时,如果数据尚未准备好,`read`、`write`等系统调用会立即返回一个错误码,表示数据不可用。进程不会被阻塞,而是可以继续执行其他任务,并不断地轮询检查 I/O 操作是否就绪。

- 例如,在网络编程中,使用非阻塞套接字时,如果没有数据可读,`recvfrom`函数会立即返回`EWOULDBLOCK`或`EAGAIN`错误,表示当前没有数据可读,但进程不会被阻塞,可以继续执行其他代码,并在稍后再次尝试读取数据。

2. 特点

- 进程不会因为 I/O 操作而长时间阻塞,可以在等待 I/O 完成的同时执行其他任务,提高了资源利用率。

- 但是需要不断地轮询检查 I/O 状态,会消耗较多的 CPU 资源,尤其是在 I/O 操作需要较长时间才能完成的情况下,轮询的效率会很低。

三、I/O 多路复用(I/O Multiplexing)

 1. 工作原理

- 使用一个线程或进程同时监视多个文件描述符(如套接字、文件等),当其中任何一个文件描述符就绪(可读、可写或有异常情况)时,就可以进行相应的 I/O 操作。

- 常见的 I/O 多路复用技术有`select`、`poll`和`epoll`(Linux 系统)等。例如,使用`select`函数时,进程将一组文件描述符传递给`select`,`select`会阻塞进程,直到其中至少一个文件描述符就绪,然后进程可以对就绪的文件描述符进行 I/O 操作。

2. 特点

- 可以同时处理多个 I/O 操作,减少了线程或进程的数量,提高了系统的资源利用率。

- 相比非阻塞 I/O 的轮询方式,I/O 多路复用在处理大量并发连接时更加高效,因为它只需要在一个线程或进程中进行阻塞等待,而不是每个连接都使用一个线程进行轮询。

四、信号驱动 I/O(Signal-Driven I/O)

 1. 工作原理

- 进程首先通过系统调用注册一个信号处理函数,然后发起一个 I/O 操作并立即返回。当数据准备好时,操作系统会发送一个信号给进程,进程在信号处理函数中进行 I/O 操作。

- 例如,在网络编程中,使用信号驱动 I/O 的套接字时,进程注册一个信号处理函数,当有数据可读时,操作系统会发送一个信号通知进程,进程在信号处理函数中调用`recvfrom`函数读取数据。 2. 特点

- 进程在等待 I/O 操作完成时不会被阻塞,可以继续执行其他任务。

- 但是信号处理函数的编写相对复杂,并且信号可能会出现丢失或重复的情况,需要额外的处理来确保 I/O 操作的正确性。

五、异步 I/O(Asynchronous I/O)

1. 工作原理

- 进程发起一个异步 I/O 操作后立即返回,操作系统在后台完成 I/O 操作,当 I/O 操作完成后,操作系统会通知进程。进程可以在等待 I/O 完成的同时执行其他任务,无需主动检查 I/O 状态。

- 例如,在支持异步 I/O 的操作系统中,使用异步文件读取操作时,进程发起一个读取请求后继续执行其他任务,当操作系统完成读取操作后,会通过回调函数、信号或其他方式通知进程,进程可以在回调函数中处理读取到的数据。

2. 特点

- 真正实现了 I/O 操作与进程的完全异步,最大限度地提高了系统的并发性能和资源利用率。

- 但是异步 I/O 的实现相对复杂,需要操作系统的支持,并且编程模型相对较难理解和掌握。

  • 怎么实现文件的拷贝?

标准io/文件io  都行,使用open read write close等

  • 复制文本文件怎么操作

标准io/文件io  都行,使用open read write close等

  • fgets和fputs文件的拷贝是怎么实现的?

一、包含头文件

 需要包含``头文件,因为会使用标准输入输出函数。 ```c #include `

二、打开源文件和目标文件

 1. 使用`fopen`函数以适当的模式打开源文件和目标文件。源文件以只读模式("r")打开,目标文件以写模式("w")打开,如果目标文件不存在,会自动创建;如果存在,会被截断为零长度

三、使用`fgets`和`fputs`进行拷贝

 1. `fgets`函数用于从源文件中读取一行文本,它会读取最多`n - 1`个字符(其中`n`是指定的缓冲区大小),直到遇到换行符或文件结束标志。如果成功读取一行文本,`fgets`会返回指向缓冲区的指针;如果到达文件末尾或发生错误,返回`NULL`。

2. `fputs`函数用于将一行文本写入目标文件,它会将字符串写入文件,直到遇到空字符'\0'为止。如果写入成功,`fputs`会返回一个非负值;如果发生错误,返回`EOF`。 

四、关闭文件 完成拷贝后,使用`fclose`函数关闭源文件和目标文件,释放资源。 

 通过以上步骤,就可以使用`fgets`和`fputs`函数实现文件的拷贝。这种方法适用于文本文件的拷贝,对于二进制文件可能不太适用,因为`fgets`和`fputs`在处理二进制文件时可能会出现问题。在处理二进制文件时,可以使用`fread`和`fwrite`函数进行拷贝。

  • IO学了什么?

    一、文件操作

    文件的打开与关闭

    • 学会使用函数如 fopenopen(低级 I/O)等打开文件,并在操作完成后使用 fcloseclose 正确关闭文件,以确保资源的合理释放和数据的完整性。
    • 理解不同的打开模式,如只读、只写、读写等,以及如何处理文件不存在或已存在的情况。

    文件的读写

    • 掌握使用函数如 freadfwritefgetsfputs 等进行文件的读取和写入操作。了解如何读取不同类型的数据,如字符、字符串、结构体等,以及如何逐行读取文本文件。
    • 对于二进制文件和文本文件的读写有不同的处理方式,学会区分并正确使用相应的函数。

    文件的定位与随机访问

    • 了解如何使用 fseekftell 等函数在文件中进行定位,实现随机访问文件的特定位置。这对于处理大型文件或需要快速访问特定数据的情况非常有用。

    二、标准输入输出

  • stdio 库的使用


    熟悉标准输入(stdin)、标准输出(stdout)和标准错误输出(stderr)的概念和使用方法。学会使用 printfscanf 等函数进行格式化的输入输出操作。 理解缓冲区的概念以及如何刷新缓冲区,以确保数据及时输出或输入。

    重定向与管道

    了解如何在命令行中使用重定向(< 和 >)和管道(|)来改变标准输入输出的流向,实现不同程序之间的数据传递和处理。

    三、I/O 模型

    阻塞与非阻塞 I/O

    理解阻塞 I/O 中进程在进行输入输出操作时会被阻塞,直到操作完成;非阻塞 I/O 中进程不会被阻塞,可以继续执行其他任务,但需要不断检查 I/O 状态。 学会在不同的应用场景中选择合适的 I/O 模型,以提高程序的性能和响应性。

    异步 I/O

    了解异步 I/O 的概念和实现方式,即进程发起 I/O 操作后立即返回,操作系统在后台完成操作并通知进程。 认识到异步 I/O 在提高系统并发性能和资源利用率方面的潜力,但也意识到其实现的复杂性。
    • 理解 I/O 多路复用在处理大量并发连接时的优势和应用场景。

    I/O 多路复用

    掌握使用 selectpollepoll 等函数实现 I/O 多路复用,能够同时监视多个文件描述符,当其中任何一个描述符就绪时进行相应的 I/O 操作。

     

    四、错误处理与资源管理

     

    I/O 错误处理

    • 学会处理 I/O 操作中可能出现的错误,如文件打开失败、读取错误、写入错误等。通过检查返回值和使用 perror 等函数输出错误信息,能够及时发现和处理问题。
    • 理解不同的错误码及其含义,以便进行针对性的错误处理。

    资源管理

    • 意识到 I/O 操作涉及系统资源的使用,如文件描述符、内存等。学会在程序中合理地管理这些资源,避免资源泄漏和滥用。
    • 确保在不需要资源时及时释放,如关闭文件、释放内存等,以提高系统的稳定性和性能。
    通过学习 I/O,更好地处理程序中的输入输出操作,实现与外部设备、文件和用户的交互,提高程序的功能和性能。同时,对 I/O 的深入理解也有助于在开发复杂的应用程序时进行有效的资源管理和错误处理。
     

进程线程

1.线程进程区别

  • 线程和进程的概念,区别,以及什么时候用线程什么时候用进程?

一、线程和进程的概念

  1. 进程

    • 进程是操作系统进行资源分配和调度的基本单位。它包含了程序运行所需的资源,如内存空间、文件描述符、寄存器状态等。每个进程都有独立的地址空间,这意味着不同进程之间的数据是隔离的,一个进程中的错误通常不会直接影响到其他进程。
    • 例如,当你打开一个文本编辑器时,操作系统会为这个文本编辑器创建一个进程,分配相应的资源,让它能够独立运行。
  2. 线程

    • 线程是进程中的一个执行单元。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等。线程之间的切换比进程之间的切换开销小,因为线程共享了进程的地址空间,不需要进行像进程切换那样的大量资源切换操作。
    • 比如,在一个多线程的网络服务器中,一个进程可以同时处理多个客户端的连接请求,每个连接可以由一个线程来处理,这些线程共享服务器进程的资源。

二、线程和进程的区别

  1. 资源分配
    • 进程拥有独立的地址空间和资源,包括内存、文件、I/O 设备等。而线程共享进程的地址空间和资源,每个线程有自己的栈空间、寄存器状态等。
    • 例如,两个不同的进程不能直接访问对方的内存空间,而同一个进程中的线程可以直接访问共享的内存区域。
  2. 切换开销
    • 进程切换的开销较大,因为需要切换地址空间、刷新内存缓存等操作。线程切换的开销相对较小,因为线程共享地址空间,只需要切换线程的执行上下文。
    • 比如,在多任务操作系统中,频繁地切换进程会导致系统性能下降,而使用多线程可以在一定程度上减少切换开销,提高系统的响应速度。
  3. 通信方式
    • 进程间通信相对复杂,通常需要使用管道、消息队列、共享内存等机制。线程间通信比较容易,可以直接通过共享内存和全局变量进行通信,但需要注意同步和互斥问题。
    • 例如,在一个分布式系统中,不同的进程可能位于不同的计算机上,需要通过网络进行通信;而在一个多线程的应用程序中,线程之间可以直接访问共享的数据结构。
  4. 并发性
    • 进程和线程都可以实现并发执行,但进程之间的并发性相对较低,因为进程切换开销大。线程之间的并发性较高,可以在一个进程内同时执行多个任务,充分利用多核处理器的优势。
    • 比如,在一个多处理器系统中,多个线程可以同时在不同的处理器核心上执行,提高系统的吞吐量。

三、什么时候用线程,什么时候用进程

  1. 使用线程的情况

    • 当需要提高程序的并发性,同时又希望减少切换开销时,可以使用线程。例如,在一个多线程的服务器程序中,可以同时处理多个客户端的请求,提高服务器的响应速度。
    • 当需要在一个进程内同时执行多个任务,并且这些任务之间需要共享数据和资源时,可以使用线程。例如,在一个图形处理软件中,一个线程可以负责用户界面的交互,另一个线程可以同时进行图像的渲染。
    • 当需要实现高效的任务并行执行,并且任务之间的通信比较频繁时,可以使用线程。例如,在一个科学计算程序中,多个线程可以共同访问和更新一个大型数据结构,提高计算效率。
  2. 使用进程的情况

    • 当需要实现强隔离性,确保一个任务的错误不会影响到其他任务时,可以使用进程。例如,在一个安全关键的系统中,不同的任务可以运行在不同的进程中,以提高系统的安全性和稳定性。
    • 当需要在不同的地址空间中运行不同的程序,并且这些程序之间的通信相对较少时,可以使用进程。例如,在一个操作系统中,不同的应用程序通常运行在不同的进程中,以避免相互干扰。
    • 当需要利用多处理器系统的优势,同时又不希望共享资源时,可以使用进程。例如,在一个分布式计算系统中,不同的任务可以运行在不同的计算机上,每个计算机上的任务以进程的形式运行。
  • 线程什么时候互斥,什么时候同步?

互斥:

访问共享资源,执行不可分割操作(保证原子性)

同步:

1.协调线程间的执行顺序

2.等待特定条件满足

3.确保数据的一致性

  • 进程的目的?

一、资源分配和管理

  1. 合理分配资源
  2. 资源隔离

二、提高系统并发性

  1. 并发执行任务
  2. 响应性和交互性

三、实现任务独立性和模块化

  1. 任务分解
  2. 错误隔离和恢复
  • 线程有什么作用?

提高并发性,资源共享和通信,简化程序设计

  • 进程间为什么要通信?有几种方式?

  1. 数据共享和协作。
  2. 资源分配和管理
  3. 事件通知和同步

进程间通信的方式:

  1. 管道(Pipe)
    • 管道是一种半双工的通信方式,数据只能单向流动。它通常用于具有父子关系的进程之间。管道分为无名管道和有名管道。
    • 无名管道只能在父子进程之间使用,它在内存中创建一个临时的缓冲区,用于父子进程之间的数据传输。
    • 有名管道可以在不相关的进程之间使用,它在文件系统中创建一个特殊的文件,进程可以通过这个文件进行通信。
  2. 消息队列(Message Queue)
    • 消息队列是一种消息的链表,存放在内核中并由消息队列标识符标识。不同的进程可以通过消息队列发送和接收消息。
    • 消息队列克服了管道只能承载无格式字节流以及缓冲区大小受限等缺点。它可以实现不同类型的数据传输,并且可以设置消息的优先级。
    • 例如,在一个多任务处理系统中,不同的任务可以通过消息队列进行通信,高优先级的任务可以优先获取消息进行处理。
  3. 共享内存(Shared Memory)
    • 共享内存是最快的一种进程间通信方式,因为多个进程可以直接访问同一块内存区域,不需要进行数据的复制。
    • 进程在使用共享内存时,需要通过同步机制(如信号量)来确保数据的一致性和正确性。
    • 比如在一个高性能计算应用中,多个进程可以共享一块内存区域来存储计算结果,提高数据的访问速度。
  4. 信号量(Semaphore)
    • 信号量主要用于进程间的同步和互斥。它是一个计数器,用于控制对共享资源的访问。
    • 当一个进程需要访问共享资源时,它需要先获取信号量。如果信号量的值大于零,则进程可以继续执行;如果信号量的值为零,则进程需要等待。当进程完成对共享资源的访问后,它需要释放信号量,以便其他进程可以访问共享资源。
    • 例如,在一个数据库管理系统中,多个进程可能同时访问数据库文件,通过信号量可以确保在同一时间只有一个进程可以对数据库进行写操作。
  5. 套接字(Socket)
    • 套接字是一种更为通用的进程间通信方式,可以在不同主机上的进程之间进行通信。它主要用于网络通信,但也可以在同一台主机上的不同进程之间使用。
    • 套接字分为流式套接字和数据报套接字。流式套接字提供了一种可靠的、面向连接的通信方式,类似于电话系统;数据报套接字提供了一种不可靠的、无连接的通信方式,类似于邮政系统。
    • 比如在一个分布式系统中,不同主机上的进程可以通过套接字进行通信,实现数据的交换和任务的协作。
  • 进程状态的切换有哪几种?

在 Linux 中,进程主要有以下几种状态:

1.  R (TASK_RUNNING) :运行态或就绪态。表示进程要么正在运行,要么准备运行,正在等待被调度到 CPU 上执行。

2.  S (TASK_INTERRUPTIBLE) :可中断的睡眠态。进程正在等待某个事件(如资源、信号等),处于睡眠状态,但可以被信号唤醒。

3.  D (TASK_UNINTERRUPTIBLE) :不可中断的睡眠态。进程也在等待某个事件,处于睡眠状态,但不能被信号唤醒,通常是在等待一些硬件条件。

4.  T (TASK_STOPPED) :暂停态。进程被暂停,例如通过发送  SIGSTOP  等信号。

5.  Z (TASK_DEAD - EXIT_ZOMBIE) :僵尸态。进程已经结束,但其资源还未被父进程回收。

6.  X (TASK_DEAD - EXIT_DEAD) :已死亡态。进程完全终止,资源已被回收。

  • 父与子运行期间的关系是什么?

父子进程之间存在以下关系和特点:
 
1. 资源继承:子进程会继承父进程的部分资源和属性,例如打开的文件描述符、环境变量、当前工作目录等。但子进程有自己独立的地址空间,对内存的修改不会影响到父进程。
2. 执行顺序:父子进程的执行顺序是不确定的,由操作系统的调度器决定。
3. 进程标识:子进程有自己唯一的进程标识符(PID),而父进程可以通过  fork  函数的返回值或  getpid 、 getppid  函数获取子进程和自身的 PID 及父进程的 PID。
4. 通信方式:父子进程可以通过一些进程间通信(IPC)机制进行通信,如管道、消息队列、共享内存等。
5. 终止影响:父进程终止时,子进程不一定会终止,除非父进程是子进程的唯一控制者。但子进程终止时,父进程需要通过某种方式(如  wait  系列函数)来获取子进程的终止状态,否则子进程可能成为僵尸进程。
6. 权限继承:子进程通常继承父进程的权限和访问控制属性。
 
总之,父子进程之间既有联系又相互独立,这种关系为多进程编程提供了灵活和强大的控制方式。

  • 无名管道和有名管道的创建流程

创建管道-打开-读写管道-关闭管道-卸载管道

  • 编译多任务程序时是首选进程还是线程

在编译多任务程序时,没有绝对的首选是进程还是线程,需要根据具体的应用场景和需求来综合考虑。如果资源共享和高效的上下文切换是关键需求,线程可能是更好的选择;如果强隔离性和不同的地址空间需求更为重要,进程可能更合适。在实际应用中,也可以结合使用进程和线程,以充分发挥它们各自的优势。

2.锁

  • 线程间为什么要加锁?

一、保证数据一致性

  1. 避免数据竞争
  2. 确保原子操作

二、实现线程间的同步

  1. 协调线程的执行顺序
  2. 等待特定条件满足

三、防止资源冲突

  1. 独占访问共享资源
  2. 保护临界区
  • 你知道锁吗?讲下自旋锁和互斥锁的区别,你知道读写锁吗?

一、锁的概念

锁是一种用于实现多线程同步的机制,它可以确保在同一时间只有一个线程能够访问特定的资源或执行特定的代码区域。通过加锁,可以避免多个线程同时对共享资源进行并发访问,从而防止数据竞争和不一致性问题。

二、自旋锁和互斥锁的区别

  1. 实现原理

    • 自旋锁:当一个线程试图获取自旋锁时,如果该锁已经被其他线程持有,那么这个线程会在原地循环等待,不断地检查锁是否可用,而不会进入阻塞状态。一旦锁被释放,等待的线程可以立即获得锁并继续执行。
    • 互斥锁:当一个线程试图获取互斥锁时,如果该锁已经被其他线程持有,那么这个线程会进入阻塞状态,被操作系统挂起,直到锁被释放后,操作系统会唤醒该线程,使其重新尝试获取锁。
  2. 适用场景

    • 自旋锁:适用于锁被持有的时间很短的情况。因为在这种情况下,线程在原地循环等待的时间相对较短,不会浪费太多的 CPU 时间。例如,在多处理器系统中,对一些局部变量的保护可以使用自旋锁,因为这些变量的访问通常很快。
    • 互斥锁:适用于锁被持有的时间较长的情况。如果在锁被持有的时间较长时使用自旋锁,那么等待的线程会在原地循环等待很长时间,浪费大量的 CPU 资源。互斥锁可以让等待的线程进入阻塞状态,释放 CPU 资源给其他线程使用。
  3. 性能特点

    • 自旋锁:在短时间内可以提供较高的性能,因为它避免了线程的上下文切换开销。但是,如果锁被持有的时间过长,自旋锁会浪费大量的 CPU 时间,导致性能下降。
    • 互斥锁:在锁被持有的时间较长时性能较好,因为它可以让等待的线程进入阻塞状态,释放 CPU 资源。但是,互斥锁的上下文切换开销较大,在短时间内的性能可能不如自旋锁。

三、读写锁

读写锁是一种特殊的锁,它允许多个线程同时对共享资源进行读操作,但在进行写操作时,必须独占访问共享资源。读写锁通常用于读多写少的场景,可以提高并发性能。

  1. 读锁和写锁的特性

    • 读锁:多个线程可以同时持有读锁,对共享资源进行读操作。读锁的获取和释放通常比较快,因为它不需要独占访问共享资源。
    • 写锁:在任何时候,只有一个线程可以持有写锁,对共享资源进行写操作。当一个线程持有写锁时,其他线程不能持有读锁或写锁,必须等待写锁被释放后才能进行读或写操作。
  2. 适用场景

    • 读写锁适用于读操作远远多于写操作的场景。例如,在一个数据库系统中,对数据的查询操作通常远远多于修改操作,这时可以使用读写锁来提高并发性能。
  3. 性能优势

    • 读写锁可以提高并发性能,因为它允许多个线程同时进行读操作,而不需要互斥。在读多写少的场景下,读写锁的性能通常比互斥锁更好。
  • 为什么多线程需要互斥,多进程不需要?

这种说法并不完全准确。多线程和多进程在某些情况下都需要互斥。

一、多线程需要互斥的原因

资源共享:多线程在同一个进程内运行,它们共享进程的地址空间和资源,如全局变量、堆内存、文件描述符等。当多个线程同时访问和修改共享资源时,如果没有适当的互斥机制,就可能会出现数据竞争和不一致性问题。

  • 例如,一个线程正在读取共享变量的值,而另一个线程同时在修改这个变量的值,那么读取线程可能会得到一个不一致或错误的值。
  1. 原子操作:某些操作需要作为一个原子操作执行,即不能被中断或分割成多个部分。在多线程环境下,需要互斥机制来确保这些操作在一个线程执行期间不会被其他线程干扰。
    • 比如,对一个共享变量进行自增操作(++variable),这个操作在机器级别可能不是原子的,可能会被其他线程中断,导致结果不正确。使用互斥锁可以保证自增操作在一个线程执行期间不会被其他线程打断。

二、多进程在某些情况下也需要互斥

  1. 共享资源:虽然多进程有独立的地址空间,但在某些情况下,它们也可能需要共享资源,如共享内存、文件、数据库连接等。当多个进程同时访问和修改这些共享资源时,也需要互斥机制来确保数据的一致性和正确性。
    • 例如,多个进程同时对一个文件进行写入操作时,如果没有适当的互斥机制,就可能会导致文件内容的混乱。
  2. 协调和同步:在一些复杂的系统中,多个进程可能需要进行协调和同步,以确保它们按照正确的顺序执行。互斥机制可以用于实现这种协调和同步。
    • 比如,在一个分布式系统中,多个进程可能需要对一个共享资源进行访问,需要使用互斥机制来确保在同一时间只有一个进程能够访问该资源。

总之,多线程和多进程在涉及共享资源和需要进行协调和同步的情况下都可能需要互斥机制。不能简单地认为多进程不需要互斥。在实际应用中,需要根据具体的情况来选择合适的同步机制,以确保程序的正确性和性能。

  • 什么是阻塞?

  • 是指系统或组件由于等待某个特定事件的发生而暂停执行,进入等待状态。在阻塞状态下,系统或组件通常会释放一些资源,如 CPU 时间,直到等待的事件发生才会被唤醒继续执行。
  • 比如,一个线程在等待从文件读取数据时,如果数据还未准备好,它就会被阻塞,不再占用 CPU 时间,直到数据可读时被操作系统唤醒。
  • 互斥、同步的概念?

互斥,即互相排斥,在计算机领域中通常是指多个线程或进程在对共享资源进行访问时的一种排他性约束。

同步是指多个线程或进程之间为了协调执行顺序、确保数据的正确性和一致性而进行的一种协作机制。

3.多路io

  • 在少事件时,epoll和select那个好?

不管事件多少epoll都优于select,但是要看可移植性等因数因为epoll是linux特有的机制

  • epoll和poll和select的区别

  • select
    • select 使用线性表来存储文件描述符(fd)集合。当调用 select 函数时,需要将可读、可写和异常这三个 fd 集合从用户空间复制到内核空间。然后,内核会对这些集合中的每个 fd 进行轮询,检查是否有相应的事件发生。例如,对于可读 fd 集合,内核会逐个检查这些 fd 是否有数据可读。由于是轮询机制,当 fd 数量较多时,性能会受到较大影响。
  • poll
    • poll 和 select 的原理比较相似,也是采用轮询的方式来检查文件描述符。它通过一个pollfd结构数组来表示需要监视的文件描述符及其事件类型。和 select 不同的是,poll 在向内核传递文件描述符集合时,不需要像 select 那样划分可读、可写和异常集合,而是将所有信息放在一个pollfd数组中。不过,同样因为是轮询机制,随着文件描述符数量的增加,效率会降低。
  • epoll
    • epoll 是基于事件驱动的机制。它在内核中维护一个红黑树来管理文件描述符,同时还有一个就绪链表用于存放已经触发事件的文件描述符。当一个文件描述符被添加到 epoll 的红黑树中时,会注册相应的事件回调函数。一旦该文件描述符对应的事件发生,内核就会调用回调函数,将其加入到就绪链表中。在调用epoll_wait时,就可以直接从这个就绪链表中获取有事件发生的文件描述符,避免了像 select 和 poll 那样的轮询操作。
  • epoll和线程池的关系和区别?

  1. 概念解释

    • epoll
      • epoll 是一种高效的 I/O 复用技术,主要用于处理大量的并发 I/O 事件。它通过在内核中维护事件表,当文件描述符(如套接字)对应的 I/O 事件发生时(如可读、可写),能够快速地通知应用程序。例如,在一个网络服务器中,epoll 可以监视多个客户端连接的套接字,当有数据可读或者可写时,及时通知服务器程序进行相应的处理。
    • 线程池
      • 线程池是一种线程管理的机制。它预先创建一定数量的线程,并将这些线程放入一个 “池子” 中。当有任务需要执行时,从线程池中取出一个空闲线程来执行任务,任务执行完毕后,线程又回到池中等待下一个任务。比如,在一个 Web 服务器中,对于客户端的请求,可以从线程池中分配线程来处理,避免了频繁地创建和销毁线程所带来的开销。
  2. 关系方面

    • 协同工作提高性能
      • 在高并发的网络应用场景中,epoll 和线程池常常一起使用。epoll 负责监听大量的 I/O 事件,当有事件发生时,将事件对应的任务分配到线程池中。例如,在一个高性能的网络服务器中,epoll 监听众多客户端连接的套接字,当某个套接字有数据可读时,将读取数据并处理的任务交给线程池中的一个线程。这样通过 epoll 的高效 I/O 事件监听和线程池的高效任务处理相结合,可以提高整个系统的性能和并发处理能力。
    • 资源管理层面的关联
      • 它们都涉及到资源的有效利用。epoll 通过减少不必要的 I/O 轮询,节省了 CPU 资源;线程池通过复用线程,减少了线程创建和销毁的资源开销(如内存和 CPU 时间)。在系统资源有限的情况下,这种协同可以更好地平衡 I/O 处理和任务执行,以实现资源的优化配置。
  3. 区别方面

    • 功能重点不同
      • epoll:重点在于 I/O 事件的监听和通知。它的主要作用是在众多的文件描述符中快速地发现有 I/O 事件发生的那些,并将事件信息传递给应用程序。例如,它可以告诉应用程序某个套接字现在可以读取数据了,但它本身并不直接处理数据读取后的业务逻辑。
      • 线程池:重点在于任务的执行。它是一种管理和调度线程的机制,主要用于处理那些需要在单独线程中执行的任务。比如,它可以接收一个函数(任务),并安排线程池中的一个线程去执行这个函数,其关注的是任务的执行效率和线程的复用。
    • 工作层面不同
      • epoll:工作在操作系统内核和应用程序的 I/O 交互层面。它依赖于内核提供的机制,是一种操作系统层面的技术,用于优化 I/O 操作。
      • 线程池:主要工作在应用程序内部的线程管理和任务调度层面。它是一种软件设计模式,用于更好地组织和利用应用程序中的线程资源。
    • 资源开销类型不同
      • epoll:主要的资源开销在于维护事件表和处理 I/O 事件通知的内核数据结构。不过,相比于传统的 I/O 复用方式(如 select),这种开销相对较小。
      • 线程池:其资源开销主要来自于预先创建的线程所占用的内存空间和线程调度的 CPU 时间。线程池中的线程需要占用一定的内存来存储线程的上下文信息等,并且在任务分配和线程调度过程中也会消耗 CPU 资源。
  • epoll能否代替线程池?

不能,Epoll主要用于高效地监测I/O事件,而线程池主要用于执行任务,它们功能互补,从功能、性能和并发处理角度来看,epoll都无法代替线程池。

  • 线程池和多进程的区别?

    其实可以理解成线程与进程的区别

网络通信

1.http

  • http的端口号是什么?

HTTP 的默认端口号是 80。

需要注意的是,HTTP 并不是用 “代码” 来表述端口号,端口号是网络通信中用于区分不同服务的数字标识。在进行网络编程或配置服务器时,可以指定使用特定的端口号来提供 HTTP 服务。例如,在一些服务器软件的配置文件中可以设置监听的端口为 80 以提供 HTTP 服务。

HTTP 的通信是基于一系列的协议规范,其请求和响应通常使用文本格式,包含请求方法、请求头、请求体、响应状态码、响应头和响应体等内容。常见的 HTTP 请求方法有 GET、POST、PUT、DELETE 等。响应状态码如 200 表示成功,404 表示未找到资源,500 表示服务器内部错误等。但这些并不是 “代码” 来指代端口号。

  • http是干什么的,中文名是什么,主要用在什么地方?

HTTP 是超文本传输协议(HyperText Transfer Protocol)的简称。

一、主要作用

HTTP 主要用于在万维网(World Wide Web)上进行信息的传输。它定义了客户端(通常是浏览器)和服务器之间请求和响应的格式与规则。具体来说:

  1. 客户端通过 HTTP 向服务器发送请求,请求可以包括获取特定网页、提交表单数据、上传文件等。
  2. 服务器接收到请求后,根据请求的内容进行处理,并通过 HTTP 向客户端返回响应,响应通常包含请求的资源(如网页内容、图像、视频等)以及有关响应的元数据(如状态码、内容类型等)。

二、应用场景

  1. 网页浏览
    • 当你在浏览器中输入一个网址并按下回车键时,浏览器就会使用 HTTP 向对应的服务器发送请求,以获取该网页的内容。服务器返回包含 HTML、CSS、JavaScript 等资源的响应,浏览器解析这些内容并展示给用户。
  2. 网络应用开发
    • 几乎所有的 Web 应用程序都是基于 HTTP 进行通信的。例如,在线购物网站、社交媒体平台、电子邮件服务等,客户端和服务器之间的数据交换都依赖于 HTTP。
  3. 移动应用开发
    • 许多移动应用也会使用 HTTP 与服务器进行交互。比如,新闻类应用获取最新的新闻资讯、社交类应用更新动态和消息等。
  4. API 调用
    • 各种 Web API 通常也使用 HTTP 作为通信协议。开发人员可以通过发送 HTTP 请求来调用这些 API,获取所需的数据或执行特定的操作。例如,天气预报 API、地图 API 等。
  • 端口号是干什么用的?

  1. 端口号的基本概念。在网络通信中,IP 地址用于标识网络中的一台主机,而端口号则用于标识主机中的一个特定的应用程序或服务。
  2. 端口号的作用
    • 区分应用程序
    • 实现多路复用通信
  3. 端口号的分类
    • 知名端口(Well - Known Ports)
    • 注册端口(Registered Ports)
    • 动态和 / 或私有端口(Dynamic and/or Private Ports)
  • http请求报文的格式是什么?

HTTP 请求报文由以下几个部分组成:

一、请求行

  1. 方法:表示要对指定的资源执行的操作,常见的有 GET(获取资源)、POST(提交数据)、PUT(更新资源)、DELETE(删除资源)等。
  2. 资源路径:指定要请求的资源在服务器上的位置,例如 “/index.html” 表示请求服务器根目录下的 index.html 文件。
  3. HTTP 版本:表明使用的 HTTP 协议版本,如 “HTTP/1.1” 或 “HTTP/2”。

示例:“GET /index.html HTTP/1.1”。

二、请求头(首部字段)

  1. 由一系列键值对组成,用于向服务器传递关于请求的附加信息。
  2. 常见的请求头有:
    • Host:指定请求的服务器主机名和端口号。
    • User-Agent:提供发起请求的客户端信息,如浏览器类型和版本。
    • Accept:告知服务器客户端能够接受的响应内容类型,如 “text/html” 表示接受 HTML 格式的内容。
    • Accept-Language:指定客户端期望的响应语言。
    • Connection:表明客户端与服务器的连接方式,如 “keep-alive” 表示保持连接。

三、空行
用于分隔请求头和请求体。

四、请求体

  1. 并非所有请求都有请求体,如 GET 请求通常没有请求体。
  2. 当使用 POST 或 PUT 等方法提交数据时,请求体会包含要提交的数据内容。
  3. 数据的格式可以是表单数据、JSON 等。
GET /example?param1=value1¶m2=value2 HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Language: en-US,en;q=0.9 Connection: keep-alive

这个请求报文是一个 GET 请求,请求路径为 “/example”,带有两个查询参数。请求头中包含了客户端的各种信息和对响应的期望。由于是 GET 请求,这里没有请求体。

  • http交互报文的格式?

HTTP/1.1 200 OK
Date: Wed, 23 Oct 2024 12:34:56 GMT
Server: Apache/2.4.41 (Unix)
Content-Type: text/html; charset=UTF-8
Content-Length: 1234




    Sample Page


    

Hello, World!

This is a sample web page.

状态行

  • “HTTP/1.1 200 OK” 表示使用 HTTP 1.1 版本,状态码为 200(请求成功),状态描述为 “OK”。

响应头

  • “Date” 表示响应的时间。
  • “Server” 表明服务器软件信息。
  • “Content-Type” 指定响应内容为 HTML 格式且字符编码为 UTF-8。
  • “Content-Length” 表示响应体的长度为 1234 字节。

响应体

  • 是一个简单的 HTML 页面内容,包含一个标题和一段文本。
  • 描述一下http的交互过程?

  1. 建立 TCP 连接
  2. 客户端发送 HTTP 请求报文
  3. 服务器接收并处理请求报文
  4. 服务器发送 HTTP 响应报文
  5. 客户端接收并处理响应报文
  6. 关闭 TCP 连接(可选)

2.tcp/udp

  • TCP、UDP的区别?

  1. 连接方式
    • TCP(传输控制协议):是一种面向连接的协议。在数据传输之前,通信双方必须先建立连接,这个过程就像打电话,需要先拨通对方号码建立通话链路。通过 “三次握手” 的过程来建立连接。具体来说,客户端发送一个带有 SYN(同步序列号)标志的数据包给服务器,服务器收到后返回一个带有 SYN 和 ACK(确认)标志的数据包,客户端再发送一个带有 ACK 标志的数据包,这样连接就建立起来了。这种连接方式保证了数据传输的可靠性,但也增加了建立连接的开销。
    • UDP(用户数据报协议):是一种无连接协议。数据发送方可以随时向接收方发送数据,就像寄信,不需要事先与对方建立联系。它没有像 TCP 那样复杂的连接建立过程,所以传输数据的速度可能更快,但相对而言可靠性较低。
  2. 可靠性
    • TCP:提供可靠的数据传输服务。它通过序列号、确认应答、重传机制等来确保数据能够准确无误地从发送方传输到接收方。发送方发送的数据段都带有序列号,接收方收到数据后会发送确认应答(ACK),如果发送方在一定时间内没有收到 ACK,就会认为数据丢失,从而重新发送数据。例如,在文件传输场景中,TCP 可以保证文件的每一个字节都能正确地传输到目的地。
    • UDP:不提供可靠的数据传输服务。它只是尽力将数据发送出去,不保证数据是否会丢失、重复或者乱序。接收方收到的数据可能是不完整的或者有错误的,但 UDP 不会像 TCP 那样去处理这些问题。比如在实时视频流传输中,偶尔丢失几个视频帧可能不会对整体观看体验产生太大影响,这种情况下 UDP 就可以接受。
  3. 数据传输顺序
    • TCP:保证数据按照发送的顺序到达接收方。因为它有严格的序列号和确认应答机制,接收方会按照序列号的顺序来重组数据,确保数据的顺序性。例如,在传输一个包含多个部分的文档时,TCP 会保证这些部分按照发送时的顺序被接收和处理。
    • UDP:不保证数据的传输顺序。由于没有序列号和严格的顺序控制机制,数据可能会以任何顺序到达接收方。例如,在网络拥塞的情况下,后发送的 UDP 数据包可能会先到达目的地。
  4. 数据传输效率和开销
    • TCP:由于要进行连接建立、维护连接状态、数据确认和重传等操作,会产生较多的协议开销。这使得 TCP 的数据传输效率相对较低,但它的可靠性高。例如,在传输大量小数据块且对可靠性要求很高的场景下,TCP 是比较合适的,但它可能会因为频繁的确认和重传操作而消耗较多的网络资源。
    • UDP:没有连接建立和维护的过程,也没有复杂的确认和重传机制,所以协议开销小,数据传输效率高。它可以快速地将数据发送出去,适用于对实时性要求高但对数据完整性要求相对较低的场景。例如,在网络游戏中,玩家的操作指令可以使用 UDP 快速发送,即使偶尔丢失个别指令,也不会对游戏体验产生毁灭性的影响。
  5. 应用场景
    • TCP:适用于对数据准确性和完整性要求极高的场景,如文件传输(FTP)、电子邮件传输(SMTP、POP3、IMAP)、网页浏览(HTTP、HTTPS)等。这些场景中,数据的准确性和完整性是至关重要的,用户无法接受数据丢失或错误。
    • UDP:常用于对实时性要求较高、对数据丢失有一定容忍度的场景,如实时视频会议、在线游戏、流媒体直播(如一些简单的网络电台)等。在这些场景中,及时获取数据比数据的绝对完整性更重要。
  • TCP的四层应用是什么?

  1. 应用层
    • 功能概述:这是 TCP/IP 模型的最顶层,直接面向用户和应用程序。应用层协议定义了应用程序如何使用网络服务来实现特定的功能,如文件传输、电子邮件、网页浏览等。它提供了用户与网络之间的接口,使得用户能够通过应用程序发起网络请求并接收响应。
    • 常见协议
      • HTTP(超文本传输协议):用于在万维网上传输超文本内容,是网页浏览的基础。当用户在浏览器中输入网址并访问网页时,浏览器通过 HTTP 协议向服务器发送请求,获取网页内容。例如,当访问 “www.example.com” 网站时,浏览器发送 HTTP 请求,服务器返回包含 HTML、CSS、JavaScript 等文件的响应,浏览器再将这些内容呈现给用户。
      • HTTPS(安全的超文本传输协议):是 HTTP 协议的安全版本,通过在 HTTP 和 TCP 之间添加 SSL/TLS 加密层,确保数据传输的安全性。在涉及用户隐私信息(如网上银行、电商购物支付环节)的网页浏览场景中广泛应用,防止信息被窃取或篡改。
      • SMTP(简单邮件传输协议):用于发送电子邮件。它规定了邮件服务器之间以及邮件客户端与服务器之间发送邮件的格式和规则。例如,当用户在邮件客户端(如 Outlook、Gmail 等)编写邮件并点击发送按钮时,邮件客户端通过 SMTP 协议将邮件发送到指定的邮件服务器。
      • POP3(邮局协议第 3 版)和 IMAP(互联网消息访问协议):用于接收电子邮件。POP3 协议简单地将邮件从服务器下载到本地客户端,下载后服务器上的邮件通常会被删除或标记为已读。IMAP 协议则允许用户在服务器上管理邮件,如查看邮件、移动邮件、删除邮件等操作,客户端和服务器上的邮件状态可以保持同步。
  2. 传输层
    • 功能概述:传输层主要负责在不同主机上的应用程序之间提供端到端的通信服务。它的核心任务是将应用层的数据分割成合适的数据段,并通过网络层将这些数据段发送到目标主机,同时保证数据的可靠传输和正确排序。传输层在网络通信中起到承上启下的作用,对应用层屏蔽了网络底层的细节,使应用程序能够专注于数据的发送和接收。
    • 主要协议
      • TCP(传输控制协议):是一种面向连接的、可靠的传输协议。通过 “三次握手” 建立连接,在数据传输过程中使用序列号、确认应答、重传机制等来确保数据准确无误地从发送方传输到接收方。例如,在文件传输场景中,TCP 可以保证文件的每一个字节都能正确地传输到目的地。同时,TCP 还通过流量控制和拥塞控制机制,合理地利用网络资源,避免网络拥塞。
      • UDP(用户数据报协议):是一种无连接的、不可靠的传输协议。它不保证数据的完整性和顺序性,只是尽力将数据发送出去。UDP 适用于对实时性要求较高、对数据丢失有一定容忍度的场景,如实时视频会议、在线游戏等。因为它没有像 TCP 那样复杂的连接建立和数据确认机制,所以传输效率较高。
  3. 网络层
    • 功能概述:网络层主要负责将传输层的数据段封装成数据包,并在不同的网络之间进行路由选择,将数据包从源主机发送到目标主机。它需要处理网络地址(如 IP 地址)、路由转发、网络拥塞等问题,是实现网络互联的关键一层。
    • 主要协议
      • IP(互联网协议):是网络层的核心协议,用于给网络中的每一个节点(主机或路由器)分配一个唯一的 IP 地址,并通过 IP 地址来标识数据包的源地址和目标地址。IP 协议定义了数据包的格式和基本的传输规则,如数据包的分片和重组。例如,当一个主机要发送数据到另一个主机时,首先要将数据封装成 IP 数据包,在数据包头部填写源 IP 地址和目标 IP 地址,然后通过路由器等网络设备进行转发,最终到达目标主机。
      • ICMP(互联网控制消息协议):用于在 IP 主机、路由器之间传递控制消息。最常见的应用是 “Ping” 命令,通过发送 ICMP Echo 请求和接收 ICMP Echo 应答来测试网络连接是否通畅。当主机收到一个无法送达的 IP 数据包时,也会通过 ICMP 协议向源主机发送一个 “目标不可达” 的消息。
      • ARP(地址解析协议):用于将 IP 地址转换为物理地址(如以太网 MAC 地址)。在局域网中,当主机知道目标主机的 IP 地址,但需要将数据发送到目标主机的物理网卡时,就需要通过 ARP 协议来查询目标主机的 MAC 地址。例如,在以太网环境中,主机 A 要发送数据给主机 B,首先通过 ARP 协议获取主机 B 的 MAC 地址,然后将数据封装成以太网帧,帧的目的 MAC 地址填写为主机 B 的 MAC 地址,这样数据才能正确地发送到目标主机。
  4. 网络接口层(链路层)
    • 功能概述:网络接口层是 TCP/IP 模型的最底层,主要负责将网络层的数据包转换为物理网络能够传输的信号,并将物理网络接收到的信号转换为数据包。它涉及到物理介质(如双绞线、光纤、无线信号等)和网络接口设备(如网卡、交换机、路由器等)的操作,直接与物理网络相连。
    • 主要功能
      • 物理地址处理:每个网络接口都有一个唯一的物理地址(如以太网 MAC 地址),网络接口层负责识别和处理这些物理地址。在数据发送过程中,根据目标物理地址将数据发送到正确的网络接口;在数据接收过程中,检查接收到的数据帧的目的物理地址是否是自己的物理地址,如果是,则接收并向上层传递数据。
      • 数据帧封装和传输:将网络层的数据包封装成数据帧,添加帧头和帧尾,帧头包含目标物理地址、源物理地址等信息,帧尾包含校验和等信息。通过物理介质(如电缆、光纤或无线信道)将数据帧传输给相邻的网络节点。例如,在以太网中,数据帧通过网线传输到交换机,交换机根据帧头中的目的 MAC 地址将数据帧转发到正确的端口。
  • TCP为啥安全可靠?

  1. 连接管理机制
    • 三次握手建立连接:在数据传输之前,TCP 需要通过三次握手来建立连接。首先,客户端向服务器发送一个带有 SYN 标志的数据包,请求建立连接;服务器收到后,会返回一个带有 SYN 和 ACK 标志的数据包,表示确认收到请求并同意建立连接;最后,客户端再发送一个带有 ACK 标志的数据包,确认服务器的回应。通过三次握手,能够确保通信双方都已经准备好进行数据传输,避免了因一方未准备好而导致的数据丢失或传输错误。而且这种机制可以防止产生错误的连接,比如防止已经失效的连接请求报文突然又传送到服务器端而产生错误1。
    • 四次挥手断开连接:当数据传输完成后,TCP 使用四次挥手来安全地断开连接。一方发送 FIN 报文表示想要关闭连接,另一方收到后回复 ACK 报文确认,然后在自己也没有数据要发送时再发送 FIN 报文,对方再回复 ACK 报文。这样的过程确保了双方都能正确地结束通信,并且可以在连接关闭过程中处理一些未完成的数据传输或清理资源等操作。
  2. 序列号与确认应答机制1:
    • 序列号:TCP 对每个发送的数据字节都进行编号,即序列号。发送方在发送数据时,会将每个数据段的序列号包含在数据包中。接收方收到数据后,可以根据序列号来判断数据的顺序是否正确,并且可以确定是否有数据丢失或重复。如果接收方发现数据段的序列号不连续,就可以知道数据在传输过程中出现了乱序,从而可以根据序列号对数据进行重新排序,确保数据的正确顺序。
    • 确认应答:接收方在成功接收到数据后,会向发送方发送一个确认应答报文,其中包含了确认号,该确认号表示接收方期望下一次收到的数据的序列号。发送方根据接收方的确认应答来判断数据是否已经被正确接收,如果在一定时间内没有收到确认应答,就会认为数据丢失或传输出现问题,并进行重传。这种确认应答机制保证了数据的可靠传输,确保发送方能够知道数据是否已经被接收方成功接收。
  3. 超时重传机制1:
    • TCP 会为每个发送的数据段设置一个超时定时器。如果在定时器超时之前,发送方没有收到接收方的确认应答,就会认为该数据段丢失,并重新发送该数据段。超时时间的计算是动态的,会根据网络的状况和以往的传输经验进行调整。例如,在网络状况较差时,超时时间会相应延长,以避免频繁的重传;而在网络状况较好时,超时时间会适当缩短,提高传输效率。通过超时重传机制,TCP 能够在网络出现丢包等问题时,自动重新发送数据,保证数据的完整性。
  4. 流量控制机制1:
    • TCP 通过滑动窗口协议来实现流量控制。接收方会在确认应答报文中告知发送方自己的接收缓冲区大小,即窗口大小。发送方会根据接收方的窗口大小来控制发送数据的速度和数量,确保发送的数据不会超出接收方的处理能力。这样可以避免接收方的缓冲区溢出,保证数据的正确接收和处理。如果接收方的处理速度较慢,接收方可以通过减小窗口大小来通知发送方降低发送速度;反之,如果接收方的处理能力提高,接收方可以增大窗口大小,让发送方加快发送速度。
  5. 拥塞控制机制1:
    • TCP 采用了一系列的拥塞控制算法来避免网络拥塞。例如,在连接建立初期,TCP 会采用慢启动算法,逐渐增加发送数据的速度,以避免一开始就发送大量数据导致网络拥塞。当数据传输速度达到一定阈值时,进入拥塞避免阶段,此时发送方会线性地增加发送数据的速度。如果发现网络出现拥塞,比如发生了丢包现象,TCP 会立即降低发送数据的速度,进入拥塞恢复阶段。通过这种拥塞控制机制,TCP 能够根据网络的状况自动调整数据传输的速度,既保证了数据的可靠传输,又避免了网络拥塞的发生。
  6. 数据校验1:
    • TCP 在发送和接收数据时都会计算校验和。校验和是根据数据内容计算出的一个数值,用于检测数据在传输过程中是否出现了错误。如果接收方计算出的校验和与发送方发送的校验和不一致,就说明数据在传输过程中出现了错误,接收方会丢弃该数据段,并通知发送方重新发送。这种数据校验机制保证了接收方收到的数据是正确的,提高了数据的可靠性。
  • TCP/IP分几层,每层的核心任务是什么?

  1. 应用层(Application Layer)
    • 这一层是最靠近用户的一层,包括了许多应用程序协议。例如:
      • HTTP(Hyper - Text Transfer Protocol):用于在万维网上传输超文本数据,也就是我们日常浏览网页时浏览器和服务器之间使用的协议。当用户在浏览器中输入网址并访问网页时,浏览器通过 HTTP 协议向服务器发送请求,服务器再通过 HTTP 协议将网页内容发送回浏览器。
      • FTP(File Transfer Protocol):用于在网络上进行文件的上传和下载。比如在公司内部网络或者互联网上,从服务器下载文件或者将本地文件上传到服务器,就可以使用 FTP 协议。它提供了登录、文件目录操作、文件传输等功能。
      • SMTP(Simple Mail Transfer Protocol):用于发送电子邮件。当用户在邮件客户端(如 Outlook、Thunderbird 等)编写邮件并点击发送时,邮件客户端会使用 SMTP 协议将邮件发送到邮件服务器,然后由邮件服务器进行后续的投递。
  2. 传输层(Transport Layer)
    • 主要有两个重要的协议,TCP 和 UDP。
    • TCP(Transmission Control Protocol):是一种面向连接的、可靠的传输协议。例如,在进行大文件传输时,如从云存储服务器下载一个大型的软件安装包,TCP 会先建立一个连接,这个连接就像是在发送方和接收方之间建立了一条可靠的通信管道。在传输过程中,TCP 会对每一个数据包进行编号,接收方收到数据包后会向发送方发送确认信息。如果发送方没有收到确认信息,就会重新发送数据包,从而确保数据能够准确无误地从一端传输到另一端。
    • UDP(User Datagram Protocol):是一种无连接的、不可靠的传输协议。在一些对实时性要求很高的场景中发挥作用,比如在线游戏。在网络游戏中,玩家的操作信息需要快速发送到服务器,即使偶尔丢失一两个数据包,也不会对游戏体验造成太大影响。UDP 不会像 TCP 那样进行复杂的连接建立和确认机制,而是直接将数据包发送出去,所以它的传输速度更快。
  3. 网络层(Internet Layer)
    • 关键协议是 IP(Internet Protocol)。IP 协议负责将数据包从源主机发送到目的主机。例如,当用户通过家里的电脑访问国外的网站时,IP 协议会根据目的主机的 IP 地址,在复杂的互联网网络中寻找合适的路由,就像快递包裹根据收件地址在不同的物流中心和运输路线之间进行选择一样,将数据包从本地网络转发到目标网络。
  4. 网络接口层(Link Layer)
    • 这一层负责将 IP 数据包转换为物理网络能够传输的帧格式,并且处理物理网络的硬件细节。例如,在以太网环境中,网络接口层会将 IP 数据包封装成以太网帧,添加源 MAC 地址(网卡的物理地址)和目的 MAC 地址等信息,然后通过网线或者无线信号将数据发送出去。它是 TCP/IP 协议和物理网络之间的桥梁,使得数据能够在不同的物理介质(如双绞线、光纤、Wi - Fi 等)上进行传输。

所以,TCP/IP 协议与 TCP/IP 模型紧密结合,各层协议协同工作,使得数据能够在不同的网络和设备之间进行高效、可靠的传输。

  • 怎样能使UDP稳定?

  • 添加校验和机制
    • UDP 本身只提供了基本的校验和来检查数据是否损坏。可以在应用层添加更复杂的校验和算法。例如,使用 CRC(循环冗余校验)算法,在发送端对数据进行计算得到校验码,与数据一起发送。接收端收到数据后,用相同的算法重新计算校验码,对比发送端的校验码,如果不一致,就要求发送端重新发送数据。这样可以在一定程度上保证数据的完整性。
  • 实现简单的重传机制
    • 在应用层建立一个简单的反馈机制。当接收端发现数据丢失或者损坏时,可以向发送端发送一个重传请求。发送端维护一个发送缓冲区,保存已经发送的数据副本。当收到重传请求时,发送端从缓冲区中取出数据进行重新发送。不过这种重传机制不像 TCP 那样复杂和自动,需要应用程序自己来控制和管理。
  • 采用前向纠错技术(FEC)
    • FEC 是一种在发送数据时添加冗余信息的技术。例如,使用里德 - 所罗门码(Reed - Solomon code),发送端在发送数据时会根据一定的规则生成额外的纠错码。接收端在收到数据后,即使部分数据丢失或者错误,也可以利用纠错码来恢复原始数据。这种方法可以减少因为数据丢失而需要重传的次数,提高 UDP 传输的稳定性。
  • 使用流量控制和拥塞控制策略(在一定程度上)
    • 虽然 UDP 不像 TCP 那样有完善的流量控制和拥塞控制机制,但在应用层可以模拟一些简单的策略。例如,发送端可以通过监测网络状况(如发送数据的往返时间)来调整发送数据的速率。如果往返时间变长,说明网络可能出现拥塞,发送端可以适当降低发送速率,以避免数据丢失。
  • 设计一个点对点通信的软件,选择TCP还是UDP,为什么?

               TCP因为更稳定

3.其他

  • 应用层的协议是什么?

应用层协议是建立在传输层协议(如 TCP 或 UDP)之上,用于规定应用程序之间如何进行通信的协议。它主要负责处理应用程序的数据格式、请求 - 响应模式、语义和对话规则等诸多细节,使得不同的应用程序能够在网络环境中有效地交换信息。

  • 熟悉那些协议?下载用什么协议?

  • TCP/IP 协议
    • 这是互联网的基础协议套件。其中 TCP(传输控制协议)提供面向连接的、可靠的字节流服务。在数据传输前要建立连接,通过三次握手确保双方都准备好通信,并且在传输过程中有确认应答、重传机制保证数据的完整性和顺序性。IP(互联网协议)主要负责将数据包从源地址发送到目的地址,进行路由选择。
  • HTTP/HTTPS 协议
    • HTTP 是超文本传输协议,用于在万维网上传输网页等超文本内容。比如我们日常使用浏览器访问网页,浏览器和服务器之间就是通过 HTTP 协议进行通信。HTTPS 是 HTTP 的安全版本,在 HTTP 的基础上加入了 SSL/TLS 加密层,用于安全地传输敏感信息,像网上银行交易、登录信息等场景。
  • FTP 协议
    • 文件传输协议,用于在网络上进行文件的上传和下载。它有主动模式和被动模式两种工作方式。在主动模式下,客户端先发起控制连接请求,服务器收到后主动与客户端建立数据连接;在被动模式下,客户端同样发起控制连接请求,服务器返回用于数据连接的端口号,客户端再与之建立数据连接,从而实现文件的传输。

        下载一般使用的是HTTP协议

  • 说一下wireshark,他有哪些功能

Wireshark 是一款免费且开源的网络封包分析软件1。其主要功能包括:

  1. 数据包捕获
    • 实时捕获:能够实时抓取通过指定网络接口的所有数据包,无论是以太网、Wi-Fi 还是其他类型的网络连接。例如,在监测网络故障时,可以实时获取网络中传输的数据包,以便分析问题发生时的网络通信情况。
    • 离线分析:支持导入之前保存的数据包文件(.pcapng 或.pcap 格式)进行离线分析,这对于事后排查问题或者分析之前的网络通信记录非常有用。
  2. 协议解析
    • 广泛的协议支持:可以解析几百种网络协议,包括常见的 TCP/IP、HTTP、FTP、SMTP 等,以及一些不太常见的专用协议。它能够将数据包按照不同的协议层次进行拆分和展示,让用户清晰地看到每个协议字段的具体内容和含义。例如,对于 HTTP 协议的数据包,可以查看请求方法、URL、状态码、头部信息等详细内容。
    • 协议细节分析:深入分析协议的具体实现和交互过程,帮助用户理解协议的工作原理和通信细节。比如,对于 TCP 协议的三次握手、四次挥手过程,Wireshark 可以清晰地展示每个数据包的序号、确认号、窗口大小等信息,以便用户分析连接的建立和关闭过程。
  3. 数据过滤1:
    • 捕获过滤器:在开始捕获数据包之前设置过滤条件,只捕获符合条件的数据包,避免产生过大的捕获文件,同时也可以减少后续分析的工作量。例如,可以设置只捕获特定 IP 地址、特定端口或特定协议类型的数据包。
    • 显示过滤器:在捕获完成后,对已经捕获到的数据包进行进一步过滤,以便快速找到感兴趣的数据包。用户可以使用强大的过滤表达式,根据数据包的各种属性(如源地址、目的地址、协议类型、数据包长度等)进行筛选。
  4. 流量统计与分析
    • 会话统计:能够统计网络中的会话信息,包括源 IP 地址、目的 IP 地址、使用的协议、数据包数量、字节数等。通过会话统计,用户可以快速了解网络中不同主机之间的通信情况,发现异常的通信行为。
    • 流量图表:可以生成各种流量图表,如数据包数量随时间的变化、字节数的分布、协议的使用比例等。这些图表可以帮助用户直观地了解网络流量的趋势和分布情况,便于进行网络性能分析和优化1。
  5. 数据包追踪与重组1:
    • 追踪流:可以追踪特定的网络流,如 TCP 流或 UDP 流。用户可以查看一个连接中数据包的交互顺序和内容,方便分析通信过程中的数据传输情况。例如,对于一个 HTTP 会话,用户可以追踪从客户端发起请求到服务器返回响应的整个过程。
    • 数据重组:能够将属于同一个应用层数据的多个数据包进行重组,还原出完整的应用层数据,如 HTTP 请求的内容、FTP 传输的文件等。这对于分析文件传输、网页浏览等应用层的通信行为非常有帮助。
  6. 安全分析
    • 入侵检测:通过分析数据包,检测潜在的入侵行为或恶意活动。例如,检测端口扫描、网络侦察等常见的攻击行为,帮助网络安全人员及时发现安全威胁。
    • 恶意软件分析:协助分析与恶意软件相关的网络通信,通过捕获和分析与恶意软件活动相关的数据包,了解恶意软件的通信模式和行为特征,为恶意软件的检测和防范提供依据。
    • 网络取证:在网络安全事件发生后,作为网络取证的重要工具,帮助调查人员重建网络会话、查看数据包的详细信息,追溯安全事件的发生过程和相关的网络活动。
  7. 自定义功能
    • 插件支持:允许用户安装和使用插件,扩展其功能。用户可以根据自己的需求编写或下载插件,实现特定的分析功能或与其他工具进行集成。
    • 配置选项:提供了丰富的配置选项,用户可以根据自己的使用习惯和需求进行个性化设置,如调整界面布局、设置数据包的显示格式、配置协议解析选项等。
  • 应用层的协议有哪些?

HTTP(超文本传输协议)/HTTPS(安全超文本传输协议),FTP(文件传输协议),SMTP(简单邮件传输协议),POP3(邮局协议第3版),IMAP(互联网消息访问协议),DNS(域名系统)协议,SNMP(简单网络管理协议),SSH(安全外壳协议),mqtt(消息队列遥测传输协议)

数据结构

  • 链表的作用?

动态存储数据:链表是一种动态的数据结构,它可以在程序运行过程中根据需要灵活地分配内存空间来存储数据元素。与数组不同,数组在创建时需要预先指定大小,而链表不需要。

  • 数据结构的框架?

逻辑结构:线性结构和非线性结构

物理结构:顺序,链式,索引,散列

  • 链表和数组的区别?

  1. 存储方式
    • 数组
      • 数组是一种连续存储的数据结构。它在内存中占用一段连续的空间,所有元素按照顺序依次存放。
    • 链表
      • 链表是通过节点来存储数据的,每个节点包含数据域和指针域(在单链表中,指针域指向下一个节点)。这些节点在内存中的存储位置可以是任意的,不一定是连续的。
  2. 访问元素方式
    • 数组
      • 数组具有随机访问的特性。因为数组元素的存储位置是连续的,且可以通过下标直接计算出元素的存储地址,所以可以在时间复杂度内访问任意位置的元素。
    • 链表
      • 链表访问元素通常需要从头节点开始顺序遍历。如果要访问链表中的某个节点,需要从链表的头节点出发,沿着指针域逐个节点访问,直到找到目标节点。平均访问一个节点的时间复杂度为,其中n是链表的长度。
  3. 插入和删除操作
    • 数组
      • 在数组中插入和删除元素可能比较复杂。当需要在数组中间插入一个元素时,为了保持数组元素的连续性,需要将插入位置之后的元素依次向后(插入元素)或向前(删除元素)移动。
    • 链表
      • 链表的插入和删除操作相对灵活。对于插入操作,只需要修改相关节点的指针即可。
  4. 空间利用效率
    • 数组
      • 数组在创建时需要预先指定大小。如果预先分配的空间过大,可能会造成内存浪费;如果分配的空间过小,可能需要重新分配更大的空间来存储更多元素,这个过程比较复杂且可能会产生额外的开销。例如,定义一个长度为100的数组来存储用户输入的数据,但实际只用到了10个元素,就会浪费90个元素的空间。
    • 链表
      • 链表是一种动态的数据结构,不需要预先指定大小。它根据实际需要动态地分配内存空间来存储节点。每个节点只占用其自身数据和指针所需的空间,不会造成像数组那样因预分配空间过大而浪费的情况。但是,由于链表节点中包含指针,会占用额外的内存空间,相比数组(只存储数据元素),在存储相同数量的数据元素时,链表可能需要更多的内存空间。
  5. 适用场景
    • 数组
      • 适合存储和处理数据元素数量固定,且需要频繁进行随机访问的情况。例如,存储矩阵(二维数组)用于数学计算,如矩阵乘法等,因为在矩阵运算过程中经常需要快速访问矩阵中的某个元素。
    • 链表
      • 适合需要频繁进行插入和删除操作的数据存储场景。例如,实现一个简单的文本编辑器,用于存储文本内容,当用户在文本中间插入或删除字符时,链表可以很方便地实现这些操作。另外,在一些数据长度不确定,且动态增长的数据存储场景中也很适用,如建立一个动态的任务队列,不断有新任务加入或已有任务完成(从队列中删除)。
  • 链表头插尾插那种效率高?

在单链表中,头插法的效率更高。因为头插法的时间复杂度为,而尾插法的时间复杂度为。头插法不需要遍历链表,而尾插法在插入新节点时需要遍历链表找到尾节点。

在双链表中,头插法和尾插法的效率一样高,时间复杂度都为。这是因为双链表有尾节点指针,可以直接访问尾节点,避免了单链表尾插法中遍历链表寻找尾节点的过程。

  • 简单说一下链表查重,结束的条件是什么?

外循环到底或找到

  • 数组和链表的优缺点?

  1. 数组的优点

    • 高效的随机访问
    • 良好的空间局部性
    • 简单易懂的结构和操作
  2. 数组的缺点

    • 固定的大小限制
    • 插入和删除操作的复杂性和低效性
    • 初始化开销
  3. 链表的优点

    • 动态的大小调整
    • 高效的插入和删除操作(不考虑查找节点位置的时间)
    • 灵活的内存分配
  4. 链表的缺点

    • 低效的随机访问
    • 额外的存储开销
    • 复杂的遍历操作和结构维护
  • 单链表和双链表的区别?

没什么区别,就是一个多了一个前指针,所以删除,插入等操作有所不同

  • 如果知道某个节点的值,想删除怎么操作?

遍历对比查找然后删除

  • 链表、栈、队列之间的区别?

  1. 逻辑结构与操作规则
    • 链表
      • 链表是一种较为灵活的线性数据结构。它由节点组成,每个节点包含数据域和指针域(在单向链表中,指针域指向下一个节点)。节点之间通过指针相连,其逻辑结构就像一条链,元素之间的顺序由指针来确定。
      • 链表的操作比较多样。例如,可以在链表的任意位置进行插入和删除操作。插入节点时,只需调整相关节点的指针即可。比如,在一个单向链表中,要在节点 A 和节点 B 之间插入节点 C,只需将节点 C 的指针指向节点 B,再将节点 A 的指针指向节点 C。
      • 栈是一种特殊的线性表,具有后进先出(LIFO)的操作特性。想象一个只有一个开口的容器,元素只能从这个开口进出。最后放入栈中的元素最先被取出。
      • 栈主要有两个基本操作:入栈(push)和出栈(pop)。当进行入栈操作时,新元素被放置在栈顶;而出栈操作则是将栈顶元素移除。例如,将元素 1、2、3 依次入栈,那么出栈的顺序就是 3、2、1。
    • 队列
      • 队列也是线性表,但遵循先进先出(FIFO)的原则。可以把队列类比为排队,先进入队列的元素先离开。
      • 队列的基本操作是入队(enqueue)和出队(dequeue)。入队操作是在队尾添加元素,出队操作是从队头移除元素。例如,有元素 a、b、c 依次入队,那么出队顺序就是 a、b、c。
  2. 存储方式及实现细节
    • 链表
      • 链表可以采用链式存储。节点在内存中的位置不必是连续的,这使得链表在动态存储分配方面具有优势。例如,在插入或删除节点时,不需要像数组那样移动大量的元素来腾出空间或填补空缺。
      • 链表有多种类型,如单向链表、双向链表和循环链表。单向链表每个节点只有一个指向下一个节点的指针;双向链表的节点有两个指针,分别指向前一个和下一个节点;循环链表的最后一个节点的指针指向第一个节点,形成一个环。
      • 栈可以用数组(顺序栈)或者链表(链栈)来实现。顺序栈是利用一段连续的内存空间来存储元素,通过一个栈顶指针来指示栈顶元素的位置。当入栈时,栈顶指针向上移动(通常是增加索引值);出栈时,栈顶指针向下移动。
      • 链栈则是基于链表实现的栈,栈顶元素是链表的头部。入栈操作相当于在链表头部插入一个节点,出栈操作相当于从链表头部删除一个节点。
    • 队列
      • 队列同样可以用数组(顺序队列)或链表(链队列)实现。顺序队列需要考虑队头和队尾的位置,并且可能会出现假溢出的情况(数组前端有空间,但队尾指针已经到达数组末尾),通常可以采用循环队列来解决这个问题。
      • 链队列由一个头指针和一个尾指针分别指向队列的头和尾。入队操作是在队尾添加一个节点,出队操作是从队头删除一个节点
  • 常用的数据结构有哪些?

数组(顺序表),链表,栈,队列

  • 栈、队列有什么相同点?

都属于顺序表,当然也都有链式结构的栈和队列

  • 数列A、B、C输出A、C、B用栈的方式可以实现吗?

可以实现

  • 数据结构对之前的编程有什么帮助?

一、提高程序的运行效率

  1. 优化存储和访问
  2. 降低算法时间复杂度

二、增强程序的可维护性和可读性

  1. 模块化编程
  2. 代码逻辑清晰化

三、拓展程序的功能和应用范围

  1. 支持复杂的数据处理
  2. 实现数据的高效组织和管理
  • 如何实现二叉树的层序遍历?

  1. 首先将根节点入队。
  2. 当队列不为空时,重复以下操作:
    • 弹出队首元素,访问该节点。
    • 如果该节点有左子树,将左子树入队。
    • 如果该节点有右子树,将右子树入队。
  • 广度优先遍历,深度优先遍历的区别?

  • 广度优先遍历(Breadth - First Search,BFS)
    • 广度优先遍历是一种图形数据结构的遍历策略。对于二叉树而言,它是从根节点开始,按照层次顺序,一层一层地遍历节点。先访问距离根节点最近的节点,然后逐步向外扩展,访问距离根节点更远的节点。就像在一个水波纹扩散的过程中,先处理离中心(根节点)较近的点。
  • 深度优先遍历(Depth - First Search,DFS)
    • 深度优先遍历则是从根节点开始,沿着一条路径尽可能深地访问节点,直到不能再继续深入(到达叶子节点或满足某种终止条件),然后回溯到前一个节点,再尝试其他未访问的分支路径。可以想象成一个人在走迷宫,一直沿着一条路走,走到头了再回头走其他没走过的路。
  • 优先选择哪种链表用到编程中去?

  1. 单链表(Singly - Linked List)
  • 适用场景
    • 数据的顺序访问和动态插入 / 删除操作
    • 内存分配灵活的情况
  1. 双链表(Doubly - Linked List)
  • 双链表在插入和删除操作时,可以更快地定位到相邻的节点,因为它可以从两个方向进行查找。
  • 适用场景
    • 需要频繁的双向遍历操作
    • 需要高效的节点删除操作
  1. 循环链表(Circular - Linked List)
    • 特点
      • 循环链表可以是单循环链表(最后一个节点的指针指向第一个节点)或者双循环链表(每个节点的前后指针都形成一个闭环)。它的主要特点是没有明显的头和尾,形成一个循环的结构。
    • 适用场景
      • 循环调度和资源分配场景
      • 需要循环处理数据的场景
  • 数据库

  • 数据库的流程。

  • 都了解那些数据库?

以下是一些常见的数据库:

  1. 关系型数据库(RDBMS)

    • MySQL
    • Oracle Database
    • Microsoft SQL Server
    • sqlite3
  2. 非关系型数据库(NoSQL)

    • MongoDB
    • Redis
    • Cassandra
  3. 对象 - 关系型数据库(ORDBMS)

    • PostgreSQL
  • 各个数据库的异同?

  1. 存储模式2:
    • 关系型数据库(如 MySQL、Oracle、SQL Server)
      • 采用表格结构存储数据,数据在表格中按照行和列的方式排列,每行数据表示一条记录,每列表示一个字段。这种结构非常适合存储结构化数据,数据之间的关系明确,便于理解和管理。例如,在一个学生信息管理系统中,学生的基本信息可以存储在一个表格中,包括学号、姓名、年龄、性别等字段。
    • 非关系型数据库(如 MongoDB、Redis)
      • 存储模式更加灵活多样,如键值对存储、文档存储、列存储、图形存储等。以 MongoDB 为例,它使用 BSON 格式存储数据,将相关数据组织成文档,类似于关系型数据库中的行,但每个文档可以包含不同类型和数量的字段,并且字段的结构可以是动态的,不需要像关系型数据库那样预先定义表结构。
  2. 查询语言2:
    • 关系型数据库:通常使用结构化查询语言(SQL)进行查询,这是一种功能强大、标准化程度高的查询语言,可以执行复杂的查询和数据操作,如多表关联查询、子查询、聚合函数等。例如,在 SQL 中可以使用 “SELECT * FROM table1 JOIN table2 ON table1.id = table2.id” 这样的语句来实现两个表的关联查询。
    • 非关系型数据库:使用各自特定的查询语言,这些语言通常更适合处理非结构化或半结构化数据。例如,Redis 使用命令行方式进行数据操作,如 “SET key value” 用于设置键值对,“GET key” 用于获取对应的值;MongoDB 使用类似 JSON 的查询语言进行查询,支持丰富的查询操作,如范围查询、正则表达式查询、地理位置查询等。
  3. 性能特点
    • 关系型数据库:在处理复杂查询和保证数据一致性方面性能较好,但在大规模数据处理和高并发请求处理方面可能性能较差。因为关系型数据库在执行查询时需要进行较多的表连接操作和数据一致性检查,这会消耗一定的系统资源和时间2。
    • 非关系型数据库:在处理大规模数据和高并发请求方面性能较好,因为它们的存储结构和查询方式更适合快速读写操作。例如,Redis 将数据存储在内存中,读写速度非常快,适用于缓存场景;MongoDB 支持自动分片和副本集,可以提高读写性能、容错性和可用性2。
  4. 数据一致性
    • 关系型数据库:强调数据的强一致性,即数据在多个表之间保持严格的关联关系,事务的原子性、一致性、隔离性和持久性(ACID)得到保障。例如,在银行系统中,账户的转账操作必须保证数据的一致性,即转账前后账户的余额变化是准确无误的。
    • 非关系型数据库:更注重数据的最终一致性,即数据的最终状态是一致的,但在数据更新过程中可能会存在短暂的数据不一致情况。例如,在社交媒体平台上,用户发布的一条动态可能不会立即在所有的副本节点上同步,但是最终会达到一致的状态。
  5. 扩展性2:
    • 关系型数据库:由于其数据结构的特点,通常难以进行横向扩展(即增加服务器节点来提高性能),而只能通过垂直扩展(即增加硬件资源,如升级 CPU、内存、硬盘等)来提高性能,但垂直扩展存在一定的瓶颈。
    • 非关系型数据库:通常支持横向扩展,可以通过增加节点来提高系统的性能和容量,具有较好的可扩展性,能够适应大规模数据存储和高并发访问的需求。
  6. 安全性
    • 关系型数据库:通常提供强大的数据完整性和安全性保证,如用户身份验证、访问控制、数据加密和审计功能等。例如,Oracle 数据库拥有一套完整的安全性方案,包括访问控制、数据加密和审计等功能,以确保数据库系统的安全性和合规性。
    • 非关系型数据库:安全性方面可能相对较弱,但一些非关系型数据库(如 MongoDB)也在不断加强安全性特性,提供了一些基本的安全功能2。
  7. 适用场景
    • 关系型数据库:适合需要复杂查询、严格数据一致性和高数据安全性的场景,如企业的财务系统、人力资源系统、电子商务平台的订单管理系统等。
    • 非关系型数据库:适合需要高性能、大规模数据处理和灵活数据模型的场景,如社交媒体平台、物联网应用、日志分析系统等。
  8. 成本2:
    • 关系型数据库:商业关系型数据库(如 Oracle、SQL Server)的成本可能较高,包括软件许可费用、硬件资源需求和维护成本等。但也有一些开源的关系型数据库(如 MySQL、PostgreSQL),其使用成本相对较低。
    • 非关系型数据库:开源的非关系型数据库较多,因此在软件成本方面可能较低,但在大规模应用时可能需要更多的硬件资源和较高的运维成本。
  9. 开发和管理的复杂性2:
    • 关系型数据库:使用和管理可能较复杂,需要专门的数据库管理员进行维护,对开发人员的技术要求也较高,需要掌握 SQL 语言和数据库设计等知识。
    • 非关系型数据库:通常更易于使用和管理,但某些高级功能可能需要特定的知识和技能。例如,在使用 MongoDB 时,需要了解其文档结构和查询语言的特点,以便更好地发挥其性能优势。
  • 用过哪些SQL的编程语言?

sqlite3

  • 数据库的工作原理?

  1. 数据存储原理
    • 关系型数据库
      • 关系型数据库以表格的形式存储数据。例如,一个学生信息表可能包含学号、姓名、年龄、专业等列。这些表格在数据库中被存储为文件,数据按照行(记录)和列(字段)的方式进行组织。
      • 在底层,数据库管理系统会为每个表分配一定的存储空间,通常是在磁盘上。数据以二进制的形式存储,并且数据库会维护一些索引结构来提高数据的访问速度。例如,B - 树索引是一种常见的索引结构,它可以快速定位到符合条件的记录。假设我们要查询某个专业的所有学生,数据库可以通过专业字段上的索引,快速地在磁盘上找到对应的学生记录,而不需要遍历整个表。
    • 非关系型数据库
      • 非关系型数据库有多种存储方式。以键值对数据库为例,它将数据存储为键和值的形式,就像字典一样。例如,在一个缓存数据库中,键可能是网页的 URL,值是网页的内容。这种存储方式非常简单直接,数据的存储和检索主要基于键。
      • 文档型数据库(如 MongoDB)存储的是类似 JSON 格式的文档。每个文档可以有不同的结构,并且可以包含嵌套的字段。这些文档在存储时,数据库会对其进行序列化处理,将其转换为适合存储的格式(如 BSON),然后存储在磁盘或内存中。对于图形数据库,它存储的是节点和边的信息,用于表示各种实体之间的关系,例如社交网络中的用户和朋友关系。
  2. 数据操作原理
    • 数据插入
      • 在关系型数据库中,当插入一条新记录时,数据库会先检查插入的数据是否符合表的结构和约束条件。例如,一个表中的某个字段可能被定义为非空,那么插入的数据必须在这个字段上有值。然后,数据库会找到合适的存储空间,将新记录插入到表中对应的位置。如果有索引,还需要更新索引结构,以反映新插入的数据。
      • 非关系型数据库的数据插入过程相对简单。对于键值对数据库,只要提供键和值,就可以将数据插入到存储中。对于文档型数据库,将文档发送给数据库,数据库会将其存储在相应的集合(类似于关系型数据库中的表)中。
    • 数据查询
      • 关系型数据库的查询操作主要使用 SQL 语言。当执行一个查询语句时,数据库首先解析查询语句,确定要查询的表、字段、条件等信息。然后,它会根据是否有索引以及查询的复杂程度,选择合适的查询执行计划。例如,如果查询中有连接操作(如通过外键关联两个表),数据库需要确定连接的顺序和方式,以高效地获取数据。最后,将查询结果返回给用户或应用程序。
      • 非关系型数据库的查询方式因类型而异。键值对数据库通过键来查询值。文档型数据库可以使用类似 SQL 的查询语言(如 MongoDB 的查询语言)或者基于文档内容的查询方式,通过指定文档中的字段和条件来查找匹配的文档。图形数据库则使用专门的图形查询语言,如 Cypher(用于 Neo4j)来查询节点之间的关系。
    • 数据更新和删除
      • 在关系型数据库中,更新和删除操作都需要先定位到要操作的记录。更新操作会根据指定的条件,修改记录中的一个或多个字段的值。删除操作则会根据条件从表中删除符合条件的记录。在执行这些操作时,同样需要考虑对索引的影响,可能需要更新或重建索引。
      • 非关系型数据库的更新和删除操作也类似。键值对数据库通过键来定位数据进行更新或删除。文档型数据库可以根据文档的内容和条件来更新或删除文档,图形数据库则根据节点和边的属性来执行相应的操作。
  3. 事务管理原理
    • 关系型数据库
      • 事务是一组数据库操作的集合,这些操作要么全部成功,要么全部失败。关系型数据库通过 ACID(原子性、一致性、隔离性、持久性)特性来管理事务。原子性确保事务中的所有操作作为一个整体执行,要么全部完成,要么全部回滚。例如,在银行转账事务中,从一个账户扣款和向另一个账户收款这两个操作必须同时成功或同时失败。
      • 一致性保证事务执行前后数据库的状态是一致的。数据库通过各种约束条件(如主键约束、外键约束、检查约束等)来维护一致性。隔离性是指多个事务并发执行时,每个事务感觉不到其他事务的存在。数据库通过锁机制和隔离级别来实现隔离性。持久性则确保事务一旦提交,其结果就会永久保存,即使系统出现故障。
    • 非关系型数据库
      • 一些非关系型数据库也支持事务,但实现方式可能与关系型数据库不同。例如,一些非关系型数据库可能更强调最终一致性,而不是像关系型数据库那样严格的强一致性。在分布式的非关系型数据库中,事务可能通过分布式事务协议(如两阶段提交协议或基于向量时钟的协议)来实现,以协调多个节点之间的操作,确保数据的一致性。
  4. 数据库引擎的工作原理
    • 查询引擎
      • 查询引擎是数据库管理系统的核心组件之一。它负责接收用户的查询请求,将其转换为内部可以理解的操作序列。对于关系型数据库,查询引擎会解析 SQL 查询语句,构建查询树(也称为语法树),通过对查询树的优化,选择最佳的查询执行计划。这个过程涉及到很多复杂的算法和策略,如索引选择、连接顺序优化等。
      • 非关系型数据库的查询引擎也类似,只是它处理的是不同类型的查询语言和数据结构。例如,文档型数据库的查询引擎会根据文档的结构和查询条件,快速定位到符合要求的文档。
    • 存储引擎
      • 存储引擎负责数据的实际存储和读取。在关系型数据库中,不同的存储引擎有不同的特点。例如,InnoDB 存储引擎通过聚簇索引来存储数据,数据和索引存储在一起,这种方式可以提高数据的访问速度。MyISAM 存储引擎则将数据和索引分开存储,它的读取速度较快,但不支持事务和行级锁。
      • 非关系型数据库的存储引擎也因类型而异。键值对数据库的存储引擎主要关注键值的快速存储和检索。文档型数据库的存储引擎需要考虑文档的存储格式、索引构建等问题,以方便文档的查询和更新。
  • 查数据库如何升序打印

SELECT * FROM students ORDER BY grade;
  • 编译程序如何选择数据库

  1. 考虑数据的性质和结构
    • 结构化数据
      • 如果数据具有明确的结构,例如包含多个相互关联的表,且表之间存在严格的关系(如主键 - 外键关系),像企业资源规划(ERP)系统中的客户信息、订单信息、产品库存信息等,关系型数据库是首选。例如,在一个电商 ERP 系统中,产品表可能通过外键与库存表、订单详情表相连,使用关系型数据库(如 MySQL、Oracle 或 SQL Server)可以很好地维护这种复杂的关系,通过 SQL 的连接操作可以方便地查询订单中的产品信息以及对应的库存情况。
    • 半结构化或非结构化数据
      • 当数据的结构比较灵活,如文档、日志、图像、音频等,或者数据的格式不固定,非关系型数据库就更合适。例如,一个内容管理系统,文章内容可能包含不同的字段,如标题、作者、正文、标签等,并且不同文章的标签数量和类型可能不同。此时,使用文档型数据库(如 MongoDB)可以方便地存储和查询这些半结构化的数据,因为它允许每个文档有不同的结构,不需要像关系型数据库那样预先定义严格的表结构。对于存储大量的日志数据,键值对数据库(如 Redis)或者专门的日志存储数据库(如 Elasticsearch)可以高效地处理。
  2. 性能需求方面
    • 读写性能要求
      • 高并发读操作:如果应用主要是对数据进行大量的读取操作,例如一个新闻网站,用户频繁地浏览新闻文章,需要快速地从数据库中获取数据。对于这种情况,缓存数据库(如 Redis)可以作为前端缓存,将经常访问的数据存储在内存中,以极快的速度提供数据访问。同时,一些非关系型数据库(如 MongoDB)也可以通过适当的索引策略和数据分片来提供高效的读性能。
      • 高并发写操作:在一些实时数据处理系统,如物联网设备数据采集系统,大量的设备会同时向数据库写入数据。对于这种高并发写的场景,一些非关系型数据库(如 Cassandra)具有良好的分布式写入性能,它通过无主架构和最终一致性模型,能够处理大量的并发写入请求,避免了像关系型数据库那样在高并发写入时可能出现的锁竞争和性能瓶颈。
    • 数据量和扩展性需求
      • 大规模数据存储和处理:当面对海量的数据(如大数据分析系统、社交媒体平台),需要考虑数据库的扩展性。非关系型数据库(如 HBase)通常在处理大规模数据方面具有优势,它可以通过增加节点实现横向扩展,能够轻松应对数据量的增长。关系型数据库在处理大规模数据时可能会遇到性能瓶颈,不过一些关系型数据库(如 PostgreSQL)也在不断改进其分布式和扩展性能力,例如通过使用分片技术来扩展存储和处理能力。
  3. 数据一致性要求
    • 强一致性需求
      • 在金融系统(如银行的核心账务系统)、票务系统等对数据准确性和一致性要求极高的应用中,关系型数据库是更好的选择。因为关系型数据库严格遵循 ACID 原则(原子性、一致性、隔离性、持久性),能够保证在复杂的事务处理过程中数据的完整性和一致性。例如,在银行转账业务中,从一个账户扣款和向另一个账户收款这两个操作必须同时成功或同时失败,关系型数据库可以通过事务机制来确保这一点。
    • 最终一致性可接受的情况
      • 对于一些对实时一致性要求不高的应用,如社交媒体的用户动态发布和传播系统,非关系型数据库的最终一致性模型是可以接受的。例如,当用户发布一条动态后,系统可能不会立即将这条动态同步到所有关注者的信息流中,而是在一段时间内逐步更新,最终达到所有数据的一致状态。这种方式可以提高系统的可用性和性能,适应高并发和大规模数据的场景。
  4. 开发和维护成本
    • 开发团队的技术能力和熟悉程度
      • 如果开发团队对 SQL 和关系型数据库技术非常熟悉,那么在技术选型时可以优先考虑关系型数据库。因为关系型数据库的开发工具和技术(如 SQL 开发、数据库设计工具)比较成熟,开发人员可以更高效地进行数据库相关的开发工作。相反,如果团队对新兴的非关系型数据库技术(如 NoSQL 数据库)有更多的经验,或者项目的性质更适合使用非关系型数据库,那么可以选择相应的非关系型数据库。
    • 维护成本和资源需求
      • 商业关系型数据库(如 Oracle)通常需要购买许可证,并且对硬件资源(如服务器性能、存储容量)的要求较高,维护成本(包括数据库管理员的人力成本、软件升级成本等)也相对较高。而开源的关系型数据库(如 MySQL)和非关系型数据库(如 MongoDB、Redis)在软件成本方面较低,但在大规模应用时可能需要更多的硬件资源和运维投入来确保性能和可靠性。例如,非关系型数据库在分布式环境下的集群管理和数据一致性维护可能需要更多的技术知识和资源投入。
  5. 安全性和合规性需求
    • 数据安全要求
      • 对于涉及敏感数据(如医疗记录、个人隐私数据、企业机密信息)的应用,数据库的安全性至关重要。关系型数据库通常提供成熟的安全机制,如用户认证、授权、数据加密等。例如,Oracle 数据库有一套完善的安全体系,包括用户角色管理、数据加密技术(如透明数据加密)和审计功能,可以有效地保护数据安全。一些非关系型数据库也在不断加强安全功能,如 MongoDB 提供了身份验证、授权和网络加密等安全措施。
    • 行业合规性要求
      • 在某些受监管的行业(如金融、医疗、政府部门),数据库的选型需要满足特定的行业标准和法规。例如,金融行业的数据存储和处理可能需要符合巴塞尔协议等相关法规,关系型数据库在满足这些合规性要求方面可能有更多的实践经验和成熟的解决方案,因为它们长期应用于金融系统的核心业务。但随着非关系型数据库的发展,一些也在逐渐满足行业合规性需求,通过提供符合标准的安全和审计功能来适应监管要求。

基础知识

  • Linux当中栈默认有多大?

X86下面32位是8Mb,64位是10mb,可修改,ulimit -s查看

arm下面是4kb-8kb

  • singal作用

进程间通信:

事件通知,进程同步与协调

进程内:

异常处理,定时器

  • 什么是同步?什么是异步?

一个是线性的,应该是并发的

  • 栈区放什么?

局部变量,函数参数,函数返回地址

  • 内存分布图?有那几个区?

嵌软面试一百问_第1张图片

  • 声明和定义的区别

声明不会分配存储空间而定义会

  • 编译程序什么时候会用多任务

当编译大型软件项目时,编译程序可以利用多任务来进行并行编译。一个大型项目通常包含许多源文件,例如在一个 C++ 大型项目中,可能有成百上千个.cpp.h文件。如果使用单任务编译,编译器需要一个一个地处理这些源文件,效率较低。

  • 什么是内存泄露、内存碎片、内存溢出?

  1. 内存泄漏(Memory Leak)内存泄漏是指程序在动态分配内存后,无法释放已经不再使用的内存空间。这就好比你租了一间房子,但是用完之后忘记退租,导致房子一直被占用,而其他人却无法使用。在计算机程序中,随着时间的推移,这些未释放的内存会不断积累,最终可能导致系统可用内存越来越少。
  2. 内存碎片(Memory Fragmentation)内存碎片是指在内存分配和释放过程中,由于分配和释放的大小和顺序不规则,导致内存空间被分割成许多小的、不连续的空闲块。这就好比一个大的储物仓库,物品的进出没有规律,导致仓库里出现很多小的、难以利用的空间。

  3. 内存溢出(Memory Overflow)

    内存溢出是指程序试图使用的内存超出了系统为其分配的范围。这就好比你试图往一个容量有限的杯子里倒过多的水,水会溢出来。在程序中,当写入数据的大小超过了预先分配的内存空间时,就会发生内存溢出。
  • 内存片的概念?

  1. 定义

    • 内存片(Memory Chip)通常是指计算机内存系统中的物理存储单元,它是存储数据的基本硬件组件。这些芯片安装在计算机的主板或者其他存储设备(如内存条)上,通过电路与计算机的其他部件相连,从而实现数据的存储和读取。
  2. 类型

    • 随机存取存储器(Random - Access Memory,RAM)芯片
      • 这是最常见的内存片类型,用于计算机的主存储器。它允许计算机快速地访问存储在其中的任何数据,无论数据存储位置如何。
      • SRAM(静态随机存取存储器)芯片:速度非常快,常用于高速缓存(Cache)。例如,在 CPU 内部的一级缓存(L1 Cache)和二级缓存(L2 Cache)通常使用 SRAM 芯片。因为它的速度快,能够在极短的时间内(通常在几个纳秒内)提供数据,有助于提高 CPU 的运行效率。不过,SRAM 芯片成本高、集成度低,相同存储容量下体积较大。
      • DRAM(动态随机存取存储器)芯片:这是计算机主内存(如我们常见的 DDR、DDR2、DDR3、DDR4 等内存条)中广泛使用的内存芯片。它的存储密度高,成本相对较低,能够提供较大的存储容量。但是它的速度比 SRAM 慢,需要定期刷新来保持数据(因为 DRAM 是利用电容存储电荷来表示数据,电荷会随着时间泄漏,所以需要不断刷新来维持数据的正确性)。
    • 只读存储器(Read - Only Memory,ROM)芯片
      • 这种内存芯片中的数据通常是在制造过程中写入的,用户在正常使用情况下无法修改其中的数据。它主要用于存储计算机启动时需要的基本指令和固定的数据,如计算机的 BIOS(基本输入输出系统)。
      • PROM(可编程只读存储器):用户可以通过特殊的编程设备将数据写入 PROM 芯片,但写入后数据就固定下来,不能再修改。
      • EPROM(可擦除可编程只读存储器):它允许用户通过紫外线照射等方式擦除芯片中的数据,然后重新写入新的数据,这在一定程度上增加了灵活性。
      • EEPROM(电可擦除可编程只读存储器):这种芯片可以通过电信号来擦除和写入数据,比 EPROM 更加方便,在一些需要频繁更新少量固定存储数据的设备(如电子设备中的配置存储)中广泛使用。
  3. 内存片的工作原理

    • 以 DRAM 芯片为例,它是基于电容存储原理工作的。在芯片内部,数据以二进制的形式存储在大量的电容单元中。每个电容单元可以存储一个二进制位(0 或 1),当电容充电时表示 1,放电时表示 0。为了读取数据,内存控制器会通过地址线发送要读取数据的内存单元地址,芯片内部的电路根据这个地址找到对应的电容单元,然后检测电容的电荷状态来确定数据是 0 还是 1,并通过数据线将数据发送出去。而在写入数据时,内存控制器通过地址线和数据线将数据和要写入的地址发送给芯片,芯片内部电路根据这些信息对相应的电容单元进行充电或放电操作来存储数据。
  4. 内存片的重要性和应用场景

    • 内存片是计算机系统能够正常运行的关键部件之一。在计算机启动时,ROM 芯片中的 BIOS 程序首先运行,它负责初始化计算机硬件设备,如检测内存、硬盘、显卡等设备是否正常工作,然后加载操作系统。而 RAM 芯片则在计算机运行过程中起到临时存储数据和程序代码的作用。例如,当我们打开一个应用程序时,操作系统会将应用程序的代码和相关数据从硬盘加载到 RAM 中,这样 CPU 就可以快速地访问和处理这些数据,提高程序的运行速度。在服务器、个人电脑、移动设备(如智能手机、平板电脑)等各种电子设备中,内存片都发挥着不可或缺的作用。
  • 什么是MAC地址?

  • MAC 地址(Media Access Control Address),也称为物理地址或硬件地址,是用于标识网络设备(如网卡、网络接口卡等)的唯一标识符。它在数据链路层(第二层)工作,是网络通信中一个非常重要的地址。这个地址由 48 位二进制数组成,通常表示为 12 位的十六进制数,例如:00 - 1B - 44 - 11 - 3A - B7。
  • 什么是内存大小端?如何测试大端?

  • 大端(Big - Endian)
    • 大端存储模式是指数据的高位字节存于低地址,低位字节存于高地址。就好像按照从左到右(高位在前)的正常阅读顺序存储数据。例如,对于 32 位整数0x12345678,如果按照大端存储,在内存中的存储顺序(假设从低地址到高地址)是0x12(高位字节)在最开始的内存地址,接着是0x340x56,最后是0x78(低位字节)在最高地址。这种存储方式符合人类的正常思维习惯,在网络协议、文件格式等很多场景下比较常用,因为它使得数据的高位部分先出现,便于理解和处理。
  • 小端(Little - Endian)
    • 小端存储模式与大端相反,数据的低位字节存于低地址,高位字节存于高地址。对于0x12345678这个 32 位整数,在小端存储下,0x78(低位字节)存于最开始的内存地址,接着是0x560x34,最后是0x12(高位字节)存于最高地址。这种方式在硬件实现上有时候会比较方便,因为 CPU 读取内存数据时从低地址开始读取是比较自然的顺序,对于一些按字节读取和处理数据的操作比较有利。
  • 什么是原子操作?

  • 原子操作(Atomic Operation)是指在计算机系统中,一个操作在执行过程中不会被中断,它要么全部执行完成,要么完全不执行,不会出现执行到一半被其他操作干扰的情况。就好像一个不可分割的 “原子”,是一个独立的、完整的操作单元。
  • 硬件实现
    • 许多处理器都提供了原子操作指令。例如,在 x86 架构中,有 “LOCK” 前缀指令,它可以将一个指令变成原子操作。当一个指令加上 “LOCK” 前缀后,处理器会在执行这个指令时锁定系统总线,阻止其他处理器或硬件设备访问相关的内存区域,直到这个原子操作完成。比如,“LOCK XCHG”(交换操作)指令可以原子地交换一个寄存器和一个内存单元的值。
  • 软件实现(基于互斥锁等机制)
    • 在不直接支持原子操作的系统或者对于一些复杂的操作组合,也可以通过软件方式来模拟原子操作。一种常见的方法是使用互斥锁(Mutex)。例如,在多线程编程中,如果要对一个共享变量进行操作,可以先获取互斥锁,然后进行操作,操作完成后再释放互斥锁。这样,在同一时刻只有一个线程能够获取互斥锁并对共享变量进行操作,从而保证操作的原子性。不过,使用互斥锁会带来一定的性能开销,因为获取和释放锁的过程涉及到系统调用和线程间的上下文切换

算法

  • 冒泡排序的思想?

一次沉底一个数

  • 知道那些排序算法?简单描述一下选择排序的思想。

选择,冒泡,插入,快排,选择排序就是打擂台

  • 什么是时间复杂度,常用的排序算法的时间复杂度

  1. 时间复杂度的概念

    • 时间复杂度(Time Complexity)是用来衡量算法运行时间随着输入规模增长而增长的量级。它主要关注的是算法执行操作的次数与输入规模之间的关系,而不是具体的运行时间(因为运行时间会受到硬件、编程语言等多种因素的影响)。通常用大 O 符号(O)来表示时间复杂度,它描述了算法在最坏情况下的时间增长趋势。

    • 例如,一个算法的时间复杂度为 O (n),这意味着随着输入规模 n 的增加,算法的运行时间大致与 n 成线性关系。如果时间复杂度是 O (n²),则运行时间会随着输入规模 n 的平方增长。

  2. 常用排序算法的时间复杂度

    • 冒泡排序(Bubble Sort)
      • 概念:冒泡排序是一种简单的排序算法,它通过反复比较相邻的元素并交换它们的位置,将最大(或最小)的元素逐步 “冒泡” 到数组的一端。
      • 时间复杂度
        • 最坏情况:O (n²)。当数组是倒序排列时,每次比较都需要交换元素,总共需要进行 n - 1 次比较,每次比较的次数从 n - 1 逐渐减少到 1,总的比较次数大约是 n (n - 1)/2,所以时间复杂度是 O (n²)。
        • 最好情况:O (n)。当数组已经是有序排列时,只需要进行一次遍历,比较次数为 n - 1,所以时间复杂度是 O (n)。
        • 平均情况:O (n²)。在大多数情况下,冒泡排序的性能与最坏情况接近。
    • 插入排序(Insertion Sort)
      • 概念:插入排序的基本思想是将一个元素插入到已经排好序的数组中合适的位置。它从第二个元素开始,将每个元素与前面已排序的元素逐个比较,然后插入到合适的位置。
      • 时间复杂度
        • 最坏情况:O (n²)。当输入数组是倒序排列时,每次插入一个元素都需要移动前面所有的元素,插入第 i 个元素需要移动 i - 1 次,总的移动次数大约是 n (n - 1)/2,所以时间复杂度是 O (n²)。
        • 最好情况:O (n)。当数组已经是有序排列时,每次插入元素只需要比较一次,总共需要比较 n - 1 次,所以时间复杂度是 O (n)。
        • 平均情况:O (n²)。在一般情况下,插入排序的性能也比较接近最坏情况。
    • 选择排序(Selection Sort)
      • 概念:选择排序的基本操作是在未排序的元素中找到最小(或最大)的元素,然后将其与未排序部分的第一个元素交换位置。这个过程不断重复,直到所有元素都排序完成。
      • 时间复杂度
        • 最坏情况:O (n²)。每次都需要在剩余的 n - i 个元素中找到最小(或最大)的元素,总共需要进行 n - 1 次选择操作,每次选择操作的比较次数从 n - 1 逐渐减少到 1,总的比较次数大约是 n (n - 1)/2,所以时间复杂度是 O (n²)。
        • 最好情况:O (n²)。选择排序的时间复杂度不受输入数据初始顺序的影响,即使数组已经是有序的,也需要进行完整的 n - 1 次选择操作,所以最好情况也是 O (n²)。
        • 平均情况:O (n²)。
    • 快速排序(Quick Sort)
      • 概念:快速排序是一种分治算法。它首先选择一个基准元素(pivot),然后将数组分为两部分,左边的元素都小于等于基准元素,右边的元素都大于等于基准元素。接着对这两部分分别进行快速排序,直到整个数组排序完成。
      • 时间复杂度
        • 最坏情况:O (n²)。当每次选择的基准元素都是数组中的最大值或最小值时,会导致划分出的子数组非常不平衡,例如数组已经是有序或逆序排列时,就可能出现这种情况。
        • 最好情况:O (n log n)。当每次划分都能将数组均匀地分成两部分时,快速排序的递归树深度为 log n,每层需要进行 n 次比较操作,所以时间复杂度为 O (n log n)。
        • 平均情况:O (n log n)。在实际应用中,快速排序的性能通常较好,平均时间复杂度接近最好情况。
    • 归并排序(Merge Sort)
      • 概念:归并排序也是一种分治算法。它将数组分成两半,对每一半进行排序,然后将排序好的两半合并成一个有序的数组。
      • 时间复杂度
        • 最坏情况:O (n log n)。归并排序的时间复杂度比较稳定,无论输入数据的初始顺序如何,每次划分和合并的操作时间复杂度都是 O (n log n)。
        • 最好情况:O (n log n)。
        • 平均情况:O (n log n)。
    • 堆排序(Heap Sort)
      • 概念:堆排序是利用堆这种数据结构进行排序的方法。它首先将数组构建成一个堆(最大堆或最小堆),然后不断地将堆顶元素与最后一个元素交换,并重新调整堆,直到整个数组排序完成。
      • 时间复杂度
        • 最坏情况:O (n log n)。堆排序在构建堆和每次调整堆的过程中,时间复杂度都是 O (n log n),不受输入数据顺序的影响。
        • 最好情况:O (n log n)。
        • 平均情况:O (n log n)。
  • 冒泡排序和快速排序那种更好?

  1. 性能方面
    • 时间复杂度
      • 冒泡排序
        • 最好情况时间复杂度为,当输入数据已经是有序的时候,只需要进行一次遍历比较操作,比较次数为。
        • 最坏情况时间复杂度为,当输入数据是倒序排列时,每次比较都需要交换元素,总共需要进行次比较和交换操作。
        • 平均情况时间复杂度为,在大多数实际情况中,冒泡排序的性能接近最坏情况。
      • 快速排序
        • 最好情况时间复杂度为,当每次划分都能将数组均匀地分成两部分时,快速排序的递归树深度为,每层需要进行次比较操作。
        • 最坏情况时间复杂度为,当每次选择的基准元素都是数组中的最大值或最小值时,会导致划分出的子数组非常不平衡,例如数组已经是有序或逆序排列时就可能出现这种情况。
        • 平均情况时间复杂度为,在实际应用中,快速排序的性能通常较好,平均时间复杂度接近最好情况。
    • 从时间复杂度角度看:在处理大规模数据时,快速排序的平均性能更好,因为其平均时间复杂度为,而冒泡排序平均时间复杂度为。随着数据量的增大,的增长速度远慢于,例如当时,,而约为(这里以以 2 为底计算)。
  2. 稳定性方面
    • 冒泡排序:是一种稳定的排序算法。稳定排序是指在排序过程中,相等元素的相对顺序保持不变。在冒泡排序中,如果两个元素相等,它们不会交换位置,所以相对顺序得以保持。例如,对于数组(这里和表示两个相等的元素),在冒泡排序过程中,和的顺序不会改变。
    • 快速排序:是一种不稳定的排序算法。在快速排序的划分过程中,可能会改变相等元素的相对顺序。例如,在划分过程中,以某个元素为基准进行划分,可能会导致相等元素被划分到不同的子数组中,从而改变它们的相对顺序。
  3. 空间复杂度方面
    • 冒泡排序:空间复杂度为,因为它只需要几个额外的变量用于交换元素,是一种原地排序算法,不需要额外的大量存储空间来进行排序。
    • 快速排序:空间复杂度为,这是因为快速排序是递归算法,在递归调用过程中需要栈空间来保存函数调用的信息。在最好和平均情况下,递归树的深度为,所以空间复杂度为。不过,在最坏情况下,空间复杂度会退化为。
  4. 应用场景方面
    • 冒泡排序:由于其简单易懂、实现容易且稳定的特点,适用于小规模数据的排序或者对稳定性有要求且数据规模较小的场景。例如,在对一个班级学生成绩进行排序(数据规模较小,且可能需要保持相同成绩学生的原始顺序),或者对一些简单的配置选项按照既定顺序排序等场景下可以使用。
    • 快速排序:因为其平均性能较好,适用于大规模数据的排序。在很多实际的排序应用中,如数据库中的数据排序、大型文件中的数据排序等场景,快速排序是比较常用的排序算法之一,能够在较短的时间内完成排序任务。不过,由于其不稳定的特性,如果对排序后相等元素的顺序有严格要求,就需要谨慎使用。
  • 快速排序稳定吗?

不稳定

  • 不想遍历元素但是想找到某些特定元素怎么实现?

二分查找

  • 冒泡排序和快速排序那种更稳定?什么是稳定?

冒泡更稳定,最坏情况和最好情况的差别小就是稳定

  • 插入法排序的概念

    拿,比,挪,插

命令

  • 查看网络状态用什么命令?

ping:检测链接

ifconfig:显示网络接口的 IP 地址、子网掩码和广播地址等基本信息外,还能显示网络接口的状态(如是否开启)、MAC 地址等信息。

netstat:用于显示网络连接、路由表和网络接口统计信息等。它可以显示当前系统的所有 TCP(Transmission Control Protocol)和 UDP(User Datagram Protocol)连接状态,包括本地地址、外部地址、连接状态(如 ESTABLISHED、LISTEN 等)等信息。还可以查看系统正在监听的端口信息,以及各个网络接口的收发数据包数量等统计信息。在 Linux 系统中,命令格式类似,如 “netstat -tulnp”,“-t” 用于显示 TCP 连接,“-u” 用于显示 UDP 连接,“-l” 用于显示监听端口,“-n” 以数字形式显示,“-p” 显示相关的进程。

traceroute:用于跟踪数据包从本地主机到目标主机所经过的路由路径。它通过向目标主机发送一系列具有不同生存时间(TTL)值的 UDP 数据包(在 Windows 系统中也可以使用 ICMP 数据包)来实现。当数据包经过每个路由器时,路由器会将 TTL 值减 1,当 TTL 值减为 0 时,路由器会返回一个 ICMP 超时消息,这样就可以获取到数据包经过的每个路由器的 IP 地址和往返时间等信息

  • 查进程用什么命令?

top:这是一个动态查看进程的命令,它会实时更新进程的信息。在终端中输入 “top” 后,会显示一个类似于表格的界面,包括进程 ID、用户、优先级、CPU 使用率、内存使用率、进程状态等各种信息。默认情况下,进程是按照 CPU 使用率排序的,可以通过按下不同的按键来改变排序方式、终止进程或者调整更新频率等操作。例如,按下 “k” 键可以终止一个选定的进程,按下 “M” 键可以按照内存使用率排序进程。

ps:“ps” 是 “process status” 的缩写,用于查看当前系统中的进程状态。基本语法是 “ps”,但这种方式只会显示与当前终端相关的进程。常用的语法是 “ps -ef” 或 “ps -aux”,“-ef” 和 “-aux” 都是用于显示所有进程的详细信息。其中 “-e” 表示显示所有进程,“-f” 表示全格式显示,“-a” 表示显示所有终端中的进程,“-u” 表示显示进程的用户信息(如用户 ID、用户所属组等),“-x” 表示显示没有控制终端的进程。

  • 用过哪些进程相关命令?

    • kill 命令
      • 功能:用于向指定的进程发送信号,最常见的用途是终止进程。它通过进程 ID(PID)来指定要操作的进程。
      • 语法和示例:基本语法是 “kill [PID]”,例如,如果通过 “ps -ef | grep [进程名]” 查到某个进程的 PID 为 1234,要终止这个进程,可以输入 “kill 1234”。除了终止进程(默认发送 SIGTERM 信号),还可以发送其他信号,如 “kill -9 [PID]”,“-9” 表示发送 SIGKILL 信号,强制立即终止进程,这种方式比较 “强硬”,可能会导致进程没有机会进行清理工作。
    • pkill 命令
      • 功能:和 kill 命令类似,但是它通过进程名而不是 PID 来终止进程,使用起来更加方便。
      • 语法和示例:基本语法是 “pkill [进程名]”。例如,要终止所有正在运行的 Firefox 浏览器进程,可以输入 “pkill firefox”。
    • nice 命令
      • 功能:用于设置进程的优先级。在 Linux 系统中,进程优先级的取值范围是 - 20(最高优先级)到 19(最低优先级),普通用户只能设置正的优先级(即降低自己进程的优先级),只有超级用户(root)可以设置负的优先级(提高进程优先级)。
      • 语法和示例:基本语法是 “nice -n [优先级值] [命令]”。例如,要以较低优先级(优先级为 10)运行一个名为 test.sh 的脚本,可以输入 “nice -n 10./test.sh”。
    • renice 命令
      • 功能:用于改变正在运行的进程的优先级。和 nice 命令不同的是,它是对已经在运行的进程进行优先级调整。
      • 语法和示例:基本语法是 “renice [新优先级值] -p [PID]”,其中 “-p” 用于指定进程的 PID。例如,发现一个进程(PID 为 5678)占用过多资源,可以降低它的优先级,假设要将其优先级调整为 5,可以输入 “renice 5 -p 5678”。
  • 与文件相关的命令有哪些?

  1. 文件查看类命令

    • cat(concatenate 的缩写)命令(Linux、Unix)
      • 功能:用于查看文件内容、将多个文件内容合并输出。它会把文件内容按照原样输出到终端屏幕上。
      • 语法和示例:基本语法是 “cat [文件名]”。例如,“cat README.txt” 可以查看当前目录下名为 “README.txt” 的文件内容。如果要合并多个文件内容并输出,可以使用 “cat file1.txt file2.txt > combined_file.txt”,这会把 “file1.txt” 和 “file2.txt” 的内容合并后输出到 “combined_file.txt” 文件中。
    • more 命令(Linux、Unix)和 less 命令(Linux、Unix)
      • 功能:当文件内容较多时,cat 命令会快速滚动屏幕内容,不方便查看。more 命令和 less 命令用于分页查看文件内容。more 命令是早期的分页查看工具,功能相对简单,只能向前翻页;less 命令功能更强大,支持向前和向后翻页,还支持搜索等功能。
      • 语法和示例:对于 more 命令,基本语法是 “more [文件名]”,如 “more large_file.txt”,按空格键可以向下翻页,按 “q” 键退出查看。对于 less 命令,基本语法是 “less [文件名]”,如 “less large_file.txt”,使用 “Page Down” 键或 “j” 键向下翻页,“Page Up” 键或 “k” 键向上翻页,按 “/” 键可以进行搜索,同样按 “q” 键退出查看。
    • head 命令(Linux、Unix)和 tail 命令(Linux、Unix)
      • 功能:head 命令用于查看文件的开头部分内容,tail 命令用于查看文件的结尾部分内容。它们对于快速查看文件的起始或结束信息很有用,比如查看日志文件的最新记录或者文件头部的注释信息。
      • 语法和示例:head 命令的基本语法是 “head -n [行数] [文件名]”,例如 “head -n 10 data.txt” 可以查看 “data.txt” 文件的前 10 行内容。tail 命令的基本语法是 “tail -n [行数] [文件名]”,例如 “tail -n 5 log.txt” 可以查看 “log.txt” 文件的最后 5 行内容。在查看日志文件时,还可以使用 “tail -f [文件名]”,这个命令会动态跟踪文件的末尾内容,当文件有新的内容添加时,会实时显示在屏幕上,常用于查看实时更新的日志,如服务器日志。
  2. 文件操作类命令

    • cp(copy 的缩写)命令(Linux、Unix)和 copy 命令(Windows)
      • 功能:用于复制文件或目录。在 Linux 和 Unix 中,cp 命令可以复制单个文件、多个文件或者整个目录。在 Windows 中,copy 命令主要用于文件复制。
      • 语法和示例:在 Linux 中,cp 命令的基本语法是 “cp [源文件或目录] [目标文件或目录]”。例如,“cp file1.txt new_file1.txt” 可以将 “file1.txt” 复制为 “new_file1.txt”;“cp -r dir1 dir2”(“-r” 表示递归复制)可以将 “dir1” 目录及其内容复制到 “dir2” 目录。在 Windows 中,“copy file1.txt new_file1.txt” 与 Linux 类似,不过 Windows 的 copy 命令没有像 Linux 中 cp 命令那样方便的递归复制目录功能。
    • mv(move 的缩写)命令(Linux、Unix)和 move 命令(Windows)
      • 功能:用于移动文件或目录位置,在 Linux 和 Unix 中还可以用于文件或目录的重命名。在 Windows 中,move 命令主要用于移动文件或文件夹。
      • 语法和示例:在 Linux 中,mv 命令的基本语法是 “mv [源文件或目录] [目标文件或目录]”。例如,“mv file1.txt new_dir/” 可以将 “file1.txt” 移动到 “new_dir” 目录下;“mv old_name.txt new_name.txt” 可以将文件 “old_name.txt” 重命名为 “new_name.txt”。在 Windows 中,“move file1.txt new_dir” 的功能类似,用于将文件移动到指定目录。
    • rm(remove 的缩写)命令(Linux、Unix)和 del 命令(Windows)
      • 功能:用于删除文件或目录。在 Linux 和 Unix 中,rm 命令可以删除文件和目录,使用时需要谨慎,特别是在删除目录时,因为文件删除后通常很难恢复。在 Windows 中,del 命令主要用于删除文件,rd 命令用于删除目录。
      • 语法和示例:在 Linux 中,rm 命令的基本语法是 “rm [文件名或目录名]”。例如,“rm file1.txt” 可以删除 “file1.txt” 文件;“rm -r dir1”(“-r” 表示递归删除)可以删除 “dir1” 目录及其内容。在 Windows 中,“del file1.txt” 可以删除指定文件,“rd dir1”(如果目录不为空,需要添加 “/s” 参数来递归删除)可以删除指定目录。
  3. 文件属性查看和修改类命令

    • ls(list 的缩写)命令(Linux、Unix)和 dir 命令(Windows)
      • 功能:用于查看目录下的文件和子目录信息。在 Linux 和 Unix 中,ls 命令可以显示文件和目录的名称、权限、所有者、大小、修改时间等多种信息。在 Windows 中,dir 命令主要显示文件和目录的名称、大小、修改日期等基本信息。
      • 语法和示例:在 Linux 中,ls 命令的基本语法有多种形式。例如,“ls -l” 可以以长格式显示文件和目录的详细信息,包括权限、所有者等;“ls -a” 可以显示所有文件和目录,包括隐藏文件(在 Linux 中,以 “.” 开头的文件是隐藏文件)。在 Windows 中,“dir” 命令直接显示当前目录下的文件和目录信息,“dir /a” 可以显示包括隐藏文件在内的所有文件和目录。
    • chmod 命令(Linux、Unix)
      • 功能:用于修改文件或目录的权限。权限包括读(r)、写(w)、执行(x)权限,分别针对文件所有者、所属组和其他用户这三个类别。
      • 语法和示例:基本语法是 “chmod [权限模式] [文件名或目录名]”。例如,“chmod 755 file1.txt”,其中 “755” 是权限模式,它表示文件所有者有读、写、执行权限(7 = 4 + 2+ 1,4 代表读权限,2 代表写权限,1 代表执行权限),所属组和其他用户有读和执行权限(5 = 4 + 1)。通过 chmod 命令可以灵活地控制文件和目录的访问权限。
  • GDB常用的命令有哪些?

gdb [可执行文件名]:这是启动GDB调试器的基本命令。

quit或q:用于退出GDB调试器。当调试完成后,输入“quit”或“q”即可结束调试会话。

start:开始执行

break或b:设置断点

info break或i b:用于查看已经设置的断点信息。它会列出所有断点的编号、类型(函数断点或行号断点等)、断点位置以及是否启用等详细信息。

delete或d [断点编号]:用于删除指定的断点。如果不指定断点编号,将删除所有断点。例如,“delete 2”会删除编号为2的断点。

enable [断点编号]disable [断点编号]:分别用于启用和禁用已设置的断点。例如,“disable 3”会禁用编号为3的断点,“enable 3”则重新启用它。

run或r:用于开始运行程序。如果程序需要命令行参数,可以在“run”命令后添加参数,如“run arg1 arg2”。

continue或c:在程序遇到断点暂停后,使用“continue”命令可以让程序继续执行,直到下一个断点或者程序结束。

next或n:单步执行程序,执行下一行代码。如果下一行是函数调用,“next”命令会将整个函数当作一个步骤执行,不会进入函数内部。

step或s:也是单步执行,但与“next”不同的是,如果下一行是函数调用,“step”命令会进入函数内部,逐行执行函数中的代码。

until或u [行号]:让程序继续执行,直到到达指定的行号或者下一个断点。这在跳过一些循环或者不想逐行调试的代码段时很有用。

print或p [变量名]:用于打印变量的值。例如,“print i”可以打印变量“i”的值。还可以对变量进行表达式求值,如“print i + 10”。

display [变量名]:与“print”类似,但“display”会在每次程序暂停(如遇到断点)时自动打印变量的值,不需要每次手动使用“print”命令。

info locals或i locals:用于查看当前函数中所有局部变量的值。这在调试过程中快速查看函数内部的变量状态很方便。

backtrace或bt:用于查看当前程序的栈帧信息,即函数调用栈。它会显示当前函数以及调用它的上层函数的信息,包括函数名、参数和返回地址等,这有助于了解程序的执行流程和函数调用关系。

frame或f [栈帧编号]:用于选择并切换到指定的栈帧。通过切换栈帧,可以查看不同函数调用层次中的变量和执行状态。例如,“frame 2”会切换到编号为2的栈帧。

  • 列举5个操作进程的命令?

ps,top,kill,pkill,nice

  • 库函数用men1和men2查询有什么区别?

  1. man 1

    • 含义:用于查看用户命令的手册页。这些是普通用户在终端中经常使用的命令,如文件操作命令(lscpmv等)、文本处理命令(catgrep等)、系统管理命令(useraddpasswd等)和网络工具命令(pingifconfig等)。
    • 内容特点:主要介绍命令的语法、选项、参数以及示例用法。它以方便用户使用这些命令完成各种任务为重点。例如,man 1 ls会详细说明ls命令的各种选项,像-l用于以长格式显示文件信息,包括文件权限、所有者、大小等;-a用于显示所有文件,包括隐藏文件。还会给出不同选项组合的使用示例,帮助用户快速理解和掌握命令的用法。
  2. man 2

    • 含义:用于查看系统调用(system call)的手册页。系统调用是操作系统内核提供给应用程序的接口,允许程序请求内核提供服务,如文件操作(openreadwrite等)、进程管理(forkexec等)、内存管理(mmap等)和网络通信(socketbind等)。
    • 内容特点:侧重于介绍系统调用的功能、参数、返回值以及错误处理。以man 2 open为例,它会详细解释open系统调用的函数原型(如int open(const char *pathname, int flags, mode_t mode);),每个参数的具体含义(pathname是要打开文件的路径名,flags是打开文件的标志,mode是文件权限模式),返回值的意义(成功返回文件描述符,失败返回 - 1),以及可能出现的错误码(如ENOENT表示文件不存在)。这对于程序员编写需要与操作系统底层交互的程序非常重要。
  3. man 3

    • 含义:用于查看 C 语言库函数的手册页。这些函数是在 C 语言编程中常用的函数,如字符串处理函数(strcpystrcat等)、数学函数(sincos等)、输入输出函数(printfscanf等)和时间函数(timelocaltime等)。
    • 内容特点:主要包括函数的原型、参数说明、返回值、函数功能描述和示例代码。例如,man 3 printf会给出printf函数的原型(如int printf(const char *format,...);),详细解释format参数的格式化规则(如%d用于输出整数,%s用于输出字符串等),返回值表示成功输出的字符数,还会提供一些使用printf函数的示例代码,帮助程序员正确使用该函数进行格式化输出。
  4. man 4

    • 含义:用于查看特殊文件(如设备文件)的手册页。这些文件通常与硬件设备相关,如硬盘设备文件(/dev/sda等)、终端设备文件(/dev/tty等)和网络设备文件(/dev/net/tun等)。
    • 内容特点:介绍特殊文件的性质、用途、相关的驱动程序以及如何与这些文件进行交互。例如,man 4 sda(假设存在这个手册页)可能会介绍硬盘设备文件/dev/sda的块大小、读写方式、与磁盘分区的关系以及如何使用底层的磁盘 I/O 操作来读取或写入该设备文件中的数据。
  5. man 5

    • 含义:用于查看文件格式和配置文件的手册页。这包括各种系统配置文件(如/etc/passwd/etc/fstab等)和数据文件格式(如/var/log/messages日志文件格式)。
    • 内容特点:主要描述文件的格式结构、各个字段的含义、文件的用途以及配置选项的说明。例如,man 5 passwd会详细解释/etc/passwd文件中每个字段(如用户名、密码占位符、用户 ID、组 ID、用户描述信息、家目录和登录 shell)的含义,以及如何正确地修改这个文件来添加或删除用户等操作。
  6. man 7

    • 含义:用于查看系统相关的各种协议、惯例和其他杂项信息的手册页。这包括文件系统层次结构(如 Linux 的/目录下各个子目录的用途)、网络协议(如TCP协议、UDP协议等的详细说明)和系统启动过程等内容。
    • 内容特点:内容比较广泛,可能包括概念性的解释、协议的工作原理、系统惯例的遵循方式等。例如,man 7 tcp会深入讲解TCP协议的工作原理,包括三次握手、数据传输、流量控制、拥塞控制等机制,以及在 Linux 系统中如何通过网络编程接口(如socket系统调用)来利用TCP协议进行网络通信。
  7. man 8

    • 含义:用于查看系统管理命令的手册页。这些命令主要用于系统维护和管理,通常需要管理员权限才能执行,如iptables(防火墙配置命令)、httpd(Web 服务器管理命令)和systemctl(系统服务管理命令)。
    • 内容特点:重点介绍命令的管理功能、配置选项、操作示例以及安全注意事项。例如,man 8 iptables会详细说明如何使用iptables命令来设置防火墙规则,包括如何允许或禁止特定的网络流量、如何设置端口转发等操作,同时也会提及相关的安全风险和注意事项。

操作

  • 是否可以快速定位段错误?

GBD:RUN

  • 如何调试段错误?首先,使用gdb <可执行文件名>命令启动 GDB 并加载程序。然后,运行程序(在 GDB 中使用run命令),当程序出现段错误时,GDB 会停止并显示相关信息。例如,它会给出类似Program received signal SIGSEGV, Segmentation fault.的提示,接着可以使用backtrace(或bt)命令查看栈帧信息,了解函数调用的顺序,从而确定段错误发生在哪个函数的调用过程中。还可以使用print命令查看变量的值,帮助分析段错误的原因。

  • 在Linux操作系统中怎么获得内存的大小?

使用free命令

  • 原理free命令通过读取/proc/meminfo文件中的信息来展示内存使用情况。/proc/meminfo是一个虚拟文件,它由内核维护,包含了各种内存相关统计信息,如总内存、已用内存、空闲内存等。free命令对这些信息进行解析和格式化输出。
  • 你都用过哪些调试手段(方法)?

printf大法,gdb,

  • 你知道检测内存泄漏的原理吗?

    • 基本原理
      • 引用计数是一种简单的内存管理技术。在这种方法中,每个对象都有一个引用计数。当对象被创建时,引用计数初始化为 1。每当有一个新的引用指向这个对象时,引用计数就增加 1;当引用离开作用域或者被显式地设置为不再引用该对象时,引用计数就减少 1。当引用计数变为 0 时,说明这个对象不再被引用,可以安全地释放其占用的内存。
      • 内存泄漏情况分析:如果一个对象的引用计数始终不为 0,即使它已经不再被程序逻辑所需要,那么就可能发生了内存泄漏。例如,在一个函数中创建了一个对象,并且将这个对象的引用赋值给一个全局变量,但是在函数结束后没有正确地处理这个全局变量对该对象的引用,导致对象的引用计数无法归零,从而造成内存泄漏。
  1. 标记 - 清扫(Mark - Sweep)算法原理
    • 基本原理
      • 标记 - 清扫算法是一种垃圾回收(Garbage Collection,GC)算法,用于检测和回收不再使用的内存。它主要分为两个阶段:标记阶段和清扫阶段。
      • 在标记阶段,从根对象(如全局变量、栈上的活动变量等)开始,通过遍历对象引用图,将所有可以从根对象访问到的对象标记为 “存活”。这个过程类似于深度优先搜索(DFS)或者广度优先搜索(BFS),沿着对象之间的引用关系进行遍历。
      • 在清扫阶段,遍历整个堆内存,将未被标记为 “存活” 的对象所占的内存空间回收。这些对象就是被认为是不可达的,即不再被程序使用的对象。
    • 内存泄漏情况分析:如果在标记阶段,某些对象因为错误的引用关系(如循环引用,或者对象被错误地保存在一个长期存在的数据结构中)而被错误地标记为 “存活”,但实际上它们已经不再被程序逻辑所需要,那么这些对象占用的内存就会被保留,从而导致内存泄漏。
  2. 工具辅助检测原理(以 Valgrind 为例)
    • 基本原理
      • Valgrind 是一个用于检测内存错误和泄漏的工具。它通过在一个虚拟的 CPU 环境中运行程序来工作。当程序运行时,Valgrind 会拦截所有的内存分配和释放操作(如mallocfree等函数),并记录下这些操作的相关信息。
      • 对于每一块分配的内存,Valgrind 会跟踪其使用情况,包括哪些指针指向这块内存,是否有合适的释放操作与之匹配等。它通过模拟程序的内存访问模式,检查是否存在没有释放的内存块。
    • 内存泄漏情况分析
      • 如果程序分配了一块内存(例如使用malloc函数),但是在程序结束或者应该释放这块内存的时候没有执行相应的释放操作(free函数),Valgrind 就会检测到这种内存泄漏情况。它会报告泄漏的内存块大小、分配该内存块的位置(函数和代码行)等信息,帮助开发者定位和修复内存泄漏问题。

Valgrind 会检测到ptr指向的内存块没有被释放,从而报告内存泄漏。

  • 踩内存是什么(关于越界访问)?

踩内存” 是一种形象的说法,主要是指程序在访问内存时,超出了所分配内存块的边界,访问了不该访问的内存区域。这是一种内存越界访问的错误行为。例如,对于一个已分配了 10 个字节内存空间的数组,如果程序试图访问第 11 个字节的位置,就属于踩内存的情况。

  • 越界访问你了解多少?

  1. 定义和基本概念

    • 越界访问是指程序在访问内存中的数据结构(如数组、缓冲区或对象)时,超出了该数据结构预先分配的内存边界。这是一种常见的程序错误,可能导致程序崩溃、数据损坏或安全漏洞。例如,对于一个定义为int arr[5];的数组,合法的访问范围是arr[0]arr[4],如果访问arr[5]或其他超出这个范围的元素,就属于数组越界访问。
  2. 产生原因

数组操作不当

指针运算错误

缓冲区溢出漏洞

  1. 可能导致的后果

    • 程序崩溃
      • 操作系统通常会保护内存,当程序进行越界访问时,可能会触发操作系统的保护机制。例如,在许多操作系统中,访问未分配给自己的内存区域(如内核空间)会导致段错误(SIGSEGV信号),从而使程序异常终止。这种情况在调试程序时很容易发现,因为程序会突然停止并返回错误信息。
    • 数据损坏
      • 越界访问可能会修改相邻内存区域的数据。如果这些数据是程序中其他重要变量或数据结构的一部分,就会导致程序的逻辑错误。例如,在一个包含多个变量的内存区域中,一个数组的越界访问可能会覆盖其他变量的值,使得后续的程序计算或操作基于错误的数据,从而产生错误的结果。
    • 安全漏洞
      • 恶意攻击者可以利用缓冲区溢出类型的越界访问来执行恶意代码。攻击者可以精心构造输入数据,使得程序的缓冲区溢出,并将恶意代码注入到溢出后的内存区域。如果程序的权限设置不当,这些恶意代码可能会被执行,从而导致系统被入侵。例如,一些网络服务程序如果存在缓冲区溢出漏洞,攻击者可以通过网络发送恶意数据来控制这些程序。
  2. 检测和预防方法

    • 静态分析工具
      • 编译器可以提供一些警告选项来帮助检测可能的越界访问。例如,在 C/C++ 语言中,GCC 编译器的-Wall选项可以开启许多有用的警告,包括一些可能导致越界访问的数组下标问题。此外,还有专门的静态分析工具,如 Clang - Static Analyzer,它可以在编译时对代码进行更深入的分析,检查是否存在潜在的越界访问风险。
    • 动态检测工具
      • 工具如 Valgrind(主要用于 C/C++)可以在程序运行时检测内存错误,包括越界访问。Valgrind 通过在一个虚拟的 CPU 环境中运行程序,监控内存的访问操作,当发现越界访问时,它会报告错误的位置和类型。在其他语言中,也有类似的内存调试工具,如 Python 的memory_profiler可以帮助检测内存相关的问题,包括可能的越界访问导致的内存异常增长。
    • 编写安全的代码
      • 在代码编写过程中,养成良好的编程习惯可以有效预防越界访问。例如,在使用数组时,始终确保索引值在合法范围内。对于指针操作,要清楚地了解指针所指向的内存区域的边界。在处理缓冲区时,使用更安全的函数(如fgets代替gets在 C 语言中),并且对输入的长度进行检查和限制。
  • 当你的程序发生越界访问,你怎么调试?

使用调试工具

  • 如何利用CPU占用率查看内存信息?

top,vmstat命令查看系统的虚拟内存统计信息

硬件

  • arm的移植、裁剪、中断处理机制

  1. ARM 移植

    • 基本概念
      • ARM 移植是指将软件(通常是操作系统、驱动程序或应用程序)从一个平台(如 x86 架构)或者一种 ARM 配置(如 ARMv7)迁移到另一种 ARM 架构(如 ARMv8)或特定的 ARM 硬件设备上。
    • 关键步骤和注意事项
      • 交叉编译环境搭建:ARM 架构与常见的 x86 架构不同,需要建立交叉编译工具链。例如,使用 GCC 交叉编译器,通过配置合适的工具链(如 arm - none - linux - gnueabi - gcc)来生成可以在 ARM 设备上运行的二进制文件。这个工具链会针对 ARM 架构的指令集、字节序、寄存器等特点进行编译。
      • 硬件差异适配:不同的 ARM 芯片有不同的硬件特性,如不同的内存映射、中断控制器、外围设备等。在移植过程中,需要根据目标 ARM 设备的硬件手册,调整软件中的硬件相关部分。例如,对于内存访问,要确保软件对内存地址的操作符合目标设备的内存布局。如果软件中有直接操作硬件寄存器的代码(如设置 GPIO 引脚),需要将寄存器地址和操作方式适配到目标 ARM 设备的寄存器映射和功能特性。
      • 库文件和依赖关系处理:软件可能依赖于一些库文件。在移植时,需要确保这些库文件也能在 ARM 环境下正常工作。有些库可能需要重新编译以适配 ARM 架构,并且要注意库文件的版本兼容性和依赖关系。例如,一个基于 C 语言的应用程序可能依赖于数学库(如 libm.a),需要确保这个库在 ARM 设备上有正确的版本并且能够被正确链接。
  2. ARM 裁剪

    • 基本概念
      • ARM 裁剪主要是针对操作系统或者大型软件套件而言,目的是减少软件的体积和资源占用,使其更适合 ARM 设备的有限资源(如内存、存储容量)。
    • 关键步骤和注意事项
      • 功能模块分析:首先需要对软件进行功能模块分析,确定哪些模块是目标 ARM 设备必需的,哪些是可以舍弃的。例如,对于一个嵌入式 Linux 系统,在一个资源有限的 ARM 设备上,可能不需要完整的图形界面系统(如 X Window System),可以将这部分功能模块裁剪掉。
      • 配置选项调整:许多软件通过配置选项来控制功能的启用和禁用。在裁剪过程中,利用这些配置选项来关闭不必要的功能。以 Linux 内核为例,可以通过修改内核配置文件(通常是.config 文件),将不需要的设备驱动、文件系统等模块设置为不编译进内核。比如,如果目标 ARM 设备没有 USB 接口,就可以在配置文件中禁用 USB 相关的驱动模块。
      • 代码优化和精简:除了通过配置选项裁剪功能外,还可以对代码本身进行优化和精简。这可能包括去除冗余代码、优化算法以减少代码量和资源消耗。例如,在一些嵌入式应用中,对一些数学计算函数进行简化,以降低计算复杂度和代码大小。
  3. ARM 中断处理机制

    • 基本概念
      • 中断是指在 ARM 处理器正常执行程序的过程中,由于内部或外部事件的发生,使得处理器暂停当前任务,转而去处理该事件,处理完成后再返回原来被中断的程序继续执行。ARM 中断处理机制是确保系统能够及时响应各种事件并高效处理的关键。
    • 中断处理流程和相关组件
      • 中断源:ARM 系统中的中断源可以是内部的(如定时器中断、软件触发中断)或外部的(如 GPIO 引脚的电平变化、外部设备的数据接收完成)。每个中断源都有一个对应的中断请求(IRQ)线连接到中断控制器。
      • 中断控制器:它是管理和分发中断请求的核心组件。在 ARM 架构中,中断控制器负责接收来自各个中断源的请求,根据优先级对它们进行排序,并将最高优先级的中断请求发送给 ARM 处理器。例如,Cortex - A 系列处理器中的通用中断控制器(GIC)可以处理多个中断源,并支持中断优先级、中断屏蔽等功能。
      • 中断向量表和中断服务程序(ISR):中断向量表是一个存储中断处理程序入口地址的表格。当处理器接收到中断请求并确认可以响应后,它会根据中断号从中断向量表中获取对应的中断服务程序的入口地址,然后跳转到该地址开始执行中断处理程序。中断服务程序是专门用于处理特定中断事件的一段代码,例如,对于一个外部 UART 接收中断,其 ISR 可能会读取接收到的数据,并将其存储到缓冲区中。
    • 中断优先级和嵌套处理
      • ARM 中断系统支持中断优先级设置。高优先级的中断可以打断正在执行的低优先级中断服务程序,这称为中断嵌套。例如,一个实时性要求很高的定时器中断(高优先级)可以中断正在处理的外部设备中断(低优先级)。在实现中断嵌套时,需要注意在中断服务程序中保存和恢复现场(如寄存器的值),以确保在嵌套中断处理完成后,低优先级中断能够正确地继续执行。同时,中断控制器会根据优先级设置来确保高优先级中断能够及时得到响应。
  • 中断和信号的区别?你知道软中断吗?

  1. 中断和信号的区别
    • 概念定义
      • 中断:是指计算机在执行程序的过程中,遇到某些紧急事件需要处理,而暂时中止当前程序的执行,转去执行相应的处理程序,处理完毕后再返回原来被中断的程序继续执行。中断通常是由硬件设备产生的,比如外部设备(如键盘、鼠标、硬盘等)完成数据传输或者发生错误时,会向 CPU 发送中断请求。例如,当你按下键盘上的一个按键时,键盘控制器会向 CPU 发送一个中断信号,通知 CPU 有按键事件发生,CPU 会暂停当前正在执行的程序,转而去执行键盘中断处理程序,读取按键的值并进行相应的处理,之后再回到原来的程序继续执行。
      • 信号:是 Unix、Linux 等操作系统中用于进程间通信的一种机制。它是软件层面的概念,用于通知进程发生了某个事件。信号可以由操作系统内核发送给进程,也可以由一个进程发送给另一个进程。例如,当一个进程执行了非法操作(如访问非法内存地址)时,操作系统内核会向这个进程发送一个SIGSEGV(段错误)信号,进程收到这个信号后,可以根据预先定义的信号处理方式进行处理,比如终止进程或者进行一些错误恢复操作。
    • 产生来源
      • 中断:主要来源于硬件设备,如磁盘 I/O 完成中断、网卡接收数据中断等。不过,现代操作系统也支持软件中断(也叫异常),这是由 CPU 执行指令过程中出现的特殊情况引发的,像除数为零、溢出等情况会产生软件中断。
      • 信号:产生于软件层面,是由操作系统内核或其他进程发送的。例如,使用kill命令可以向指定进程发送一个信号。
    • 处理方式
      • 中断:CPU 收到中断请求后,会根据中断向量表(存储中断处理程序入口地址的表)找到对应的中断处理程序并执行。在硬件中断处理过程中,通常需要保存当前程序的上下文(如程序计数器、寄存器的值等),以便在中断处理完成后能够正确地恢复执行。而且中断处理程序的执行优先级一般较高,需要尽快完成以减少对正在运行程序的影响。
      • 信号:进程可以通过注册信号处理函数来指定收到某个信号时要执行的操作。如果进程没有注册相应的信号处理函数,那么操作系统会采用默认的处理方式,比如对于SIGTERM信号默认是终止进程。信号处理相对比较灵活,进程可以选择忽略某些信号,也可以在信号处理函数中进行复杂的操作,如清理资源、保存状态等。
    • 对程序执行的影响
      • 中断:硬件中断会打断当前正在执行的指令序列,使 CPU 立即跳转到中断处理程序。这种打断是强制性的,当前程序无法阻止硬件中断的发生。中断处理完成后,程序会从被打断的地方继续执行,就好像中断没有发生过一样(前提是中断处理过程正确保存和恢复了程序的上下文)。
      • 信号:信号发送给进程时,进程在合适的时候(通常是在从内核态返回用户态时)检查是否有信号到达。如果有信号,并且进程没有忽略该信号,那么就会执行相应的信号处理函数。信号处理函数执行完毕后,进程会继续执行原来的代码路径,除非信号处理函数改变了进程的执行状态(如调用exit函数终止进程)。
  2. 软中断
    • 定义:软中断是一种软件触发的中断机制。它是相对于由硬件设备触发的硬中断而言的。在操作系统中,软中断通常用于实现一些需要异步处理的任务,特别是在多任务处理环境下,用于在合适的时候延迟执行某些操作。
    • 工作原理:当系统产生软中断时,会标记一个软中断状态位,表示有软中断事件发生。这个标记操作通常是非常快速的,然后在合适的时机(例如,在系统的中断上下文或者进程调度的某个时刻),内核会检查软中断状态位,如果有软中断事件被标记,就会执行相应的软中断处理程序。软中断处理程序执行的时机和硬中断有所不同,硬中断是立即响应硬件请求而执行,而软中断是在软件机制安排下的适当时间执行,以避免过度干扰当前正在执行的关键代码路径。
    • 应用场景:在 Linux 内核中,网络子系统大量使用软中断来处理网络数据包的接收和发送。例如,当网卡接收到数据包并通过硬中断通知 CPU 后,硬中断处理程序会进行一些必要的紧急处理(如将数据包从网卡缓冲区复制到内存中的一个临时位置),然后触发一个软中断,将后续更复杂的网络协议处理(如 IP 层、TCP 层的处理)放到软中断处理程序中进行,这样可以尽快释放硬中断处理程序占用的资源,让系统能够更快地响应其他硬件中断。同时,软中断也用于实现一些定时任务、异步 I/O 完成后的回调等功能。
  • 那你知道中断上下文吗?

    定义

    • 中断上下文是指在处理中断时,内核所处于的一种特殊的执行环境。当硬件产生中断请求,CPU 暂停当前正在执行的任务,转而去执行中断处理程序,此时 CPU 所运行的环境就是中断上下文。它和进程上下文是相对的概念。进程上下文包括了进程运行所需的各种资源,如进程的代码段、数据段、栈空间、寄存器状态等,是进程正常运行时的环境;而中断上下文主要是为了快速处理中断事件而设置的环境。
    特点
    • 资源限制
      • 中断上下文不能进行进程调度。因为中断处理程序是为了快速响应和处理中断事件,它应该尽快完成任务,将 CPU 控制权交回给被中断的程序或者其他就绪的任务。如果在中断上下文中进行进程调度,可能会导致系统的复杂性增加,甚至出现死锁等问题。例如,假设中断处理程序在执行过程中进行了进程调度,新调度的进程可能会依赖于当前中断尚未完成的操作,而当前中断处理程序又被挂起,就会产生混乱。
      • 中断上下文的栈空间是有限的。由于中断可能随时发生,为了避免栈空间的过度占用,中断上下文通常使用一个较小的、专用的栈空间。这个栈空间的大小是在系统启动时就确定好的,如果在中断处理过程中栈空间溢出,会导致系统崩溃。
    • 执行时间限制
      • 中断上下文的执行时间应该尽可能短。因为它会打断正在执行的程序,如果中断处理程序执行时间过长,会影响系统的响应性能。例如,在一个实时系统中,如果一个硬件中断处理程序占用 CPU 时间过长,可能会导致其他重要的硬件设备(如传感器数据采集设备)无法及时得到处理,从而影响整个系统的功能。
    • 不允许睡眠
      • 中断上下文不能调用可能导致睡眠的函数。睡眠是指进程让出 CPU,进入等待状态,等待某个事件发生后再被唤醒。在中断上下文中,由于不能进行进程调度,所以不能让中断处理程序睡眠。如果中断处理程序调用了可能导致睡眠的函数,如wait_event(用于等待一个事件发生),会导致系统无法正确恢复中断上下文,进而出现错误。
    用途
    • 中断上下文主要用于处理硬件设备的紧急事件,如读取硬件设备的数据、清除硬件设备的中断标志位等。例如,当硬盘完成一次数据读取操作后,会产生一个中断,CPU 进入中断上下文,在这个环境中,中断处理程序会从硬盘控制器的数据寄存器中读取数据,并将数据传递给相应的进程(可能是通过唤醒等待该数据的进程或者将数据放入缓冲区等方式),同时清除硬盘控制器的中断标志位,告知硬件设备中断已经得到处理,以便硬件设备可以进行下一次操作。

    那你知道信号吗?你如何使用信号,信号的处理函数要注意哪些?如果我在信号处理函数中malloc空间会怎么样?

    信号的基本概念

    • 信号是一种软件层面的中断机制,用于通知进程发生了某个事件。在 Unix、Linux 等操作系统中,信号是进程间通信的一种方式。信号可以由操作系统内核发送给进程,也可以由一个进程发送给另一个进程。例如,当用户在终端中按下Ctrl + C时,内核会向正在运行的前台进程发送一个SIGINT(中断信号),告诉进程用户想要终止它。

    信号的使用方法

    • 发送信号
      • 在 Linux 中,可以使用kill命令发送信号。例如,kill -SIGTERM pid可以向进程号为pid的进程发送SIGTERM(终止信号)。在程序中,也可以使用kill函数(在sys/types.hsignal.h头文件中有声明)来发送信号。
    接收信号和信号处理函数注册
    • 进程可以通过signal函数(在signal.h头文件中有声明)来注册一个信号处理函数。

    信号处理函数的注意事项

    • 可重入性问题
      • 信号处理函数应该是可重入的。因为信号可能在程序执行的任何时候到达,包括在函数执行的过程中。如果信号处理函数不可重入,可能会导致数据不一致或者程序崩溃。例如,如果一个函数正在对一个全局变量进行操作,信号处理函数也对这个全局变量进行操作,且没有正确的同步机制,就可能出现问题。假设一个函数func正在修改全局变量count的值,信号处理函数signal_handler也修改count的值,当func在执行过程中被信号中断,进入signal_handlersignal_handler修改count后返回funcfunc继续执行,此时count的值可能已经不符合func的预期。
    • 尽量简洁快速
      • 信号处理函数应该尽可能简洁和快速地执行。因为信号是异步到达的,它会打断当前程序的正常执行流程。如果信号处理函数执行时间过长,会影响程序的正常运行和系统的响应性能。例如,在一个实时系统中,长时间执行信号处理函数可能会导致其他重要任务(如数据采集、控制任务等)不能及时得到处理。
    • 保存和恢复上下文
      • 在信号处理函数中,应该注意保存和恢复可能被修改的寄存器和程序状态。虽然操作系统在进入信号处理函数时会自动保存部分上下文,但有些情况下(如在使用一些特殊的编程技巧或者底层操作时)可能需要手动保存和恢复。例如,如果在信号处理函数中使用了一些非可重入的函数,可能需要在调用这些函数之前保存相关的状态,调用之后恢复。

    在信号处理函数中使用malloc空间的情况

    • 在信号处理函数中使用malloc是不推荐的。因为malloc函数以及与之相关的内存分配函数(如callocrealloc)可能会被信号中断,而且它们通常不是可重入的。如果在信号处理函数中调用malloc,可能会导致以下问题:
      • 内存管理混乱:如果malloc函数在执行过程中被信号中断,而信号处理函数又调用了malloc,可能会破坏malloc函数内部的数据结构,导致内存管理系统出现混乱。例如,malloc函数通常会维护一个空闲内存块的链表,信号中断可能会使链表的状态不一致。
      • 死锁或程序崩溃:在一些复杂的情况下,可能会导致死锁。例如,如果内存分配器在获取锁(用于保护内存分配数据结构)的过程中被信号中断,信号处理函数又试图获取相同的锁来进行内存分配,就可能会导致死锁,最终导致程序崩溃。所以,最好避免在信号处理函数中使用malloc,如果确实需要分配内存,可以考虑使用一些特殊的、可重入的内存分配策略或者在信号处理函数之外进行内存分配。

你可能感兴趣的:(面试,职场和发展,1024程序员节)