进程上下文和中断上下文的主要区别在于其用途和执行环境的差异。进程上下文主要用于保存和恢复进程的状态信息,以实现进程的切换和调度;而中断上下文则主要用于保存和恢复中断处理程序的状态信息,以实现中断的处理和返回。
7、一个进程可以创建多少线程,和什么有关
一个进程可以创建的线程数受到操作系统和计算机硬件的限制。
具体的限制因操作系统和硬件的不同而有所不同。在操作系统方面,一些操作系统可能会限制进程可以创建的最大线程数,例如,在一些嵌入式操作系统中,可能只允许创建几个线程。而在其他操作系统中,可能会允许创建更多的线程。例如Windows Server操作系统限制每个进程可以创建2000个线程。
此外,计算机硬件的性能也会对线程数产生影响。在实际情况中,如果创建过多的线程,可能会导致系统性能下降,因为每个线程都需要占用一定的内存和计算资源。因此,需要根据具体的计算机硬件和应用程序的需求来调整线程数。
总的来说,一个进程可以创建的线程数取决于操作系统和计算机硬件的限制,而具体的限制条件需要根据实际情况来确定。
8、线程通讯(锁):
这些锁都是用于线程同步和互斥的机制,以下是它们的简要介绍:
- 信号量(Semaphore):
信号量是一种计数信号,可以用于控制多个线程对共享资源的访问。它通常由一个计数器和一个等待队列组成,计数器用于记录可用的资源数量,当计数器为零时,表示没有可用的资源,线程将被阻塞。等待队列中保存了被阻塞的线程,直到有可用的资源为止。
- 读写锁(Read-Write Lock):
读写锁是一种用于控制对共享资源的读写访问的锁。它允许多个线程同时读取共享资源,但只允许一个线程进行写操作。读写锁的实现通常会根据读写操作的特性进行优化,以最大化并发性能。
- 条件变量(Condition Variable):
条件变量是一种用于线程之间的通信机制。它可以用于在某个条件满足时,唤醒一个或多个等待的线程。条件变量通常与互斥锁一起使用,用于实现复杂的同步和通知机制。
- 互斥锁(Mutex):
互斥锁是一种用于保护共享资源的机制。它允许多个线程同时访问共享资源的一部分,但只允许一个线程同时访问共享资源的整个区域。当一个线程需要访问共享资源的互斥锁时,其他线程将被阻塞,直到该线程释放锁为止。
- 自旋锁(Spinlock):
自旋锁是一种基于忙等待的锁机制。当一个线程尝试获取自旋锁时,如果自旋锁已经被其他线程占用,则该线程会在循环中等待自旋锁被释放。自旋锁的好处是可以避免线程进入阻塞状态,从而减少上下文切换的开销,但缺点是可能会消耗大量的CPU时间。
这些锁都是为了实现线程同步和互斥而设计的机制,但每种锁都有其特点和应用场景,需要根据实际情况选择合适的锁机制。
9、什么叫临界区?
临界区是指一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性,需要在同一时间只能被一个线程执行。临界区用于保护共享资源,以避免多个线程同时访问或修改造成的数据竞争和不确定性。
第二章:C/C++面试题
1、new 和 malloc 的区别?
new 和 malloc 都是用于动态分配内存的方法,但是它们在以下方面存在一些区别:
- 内存分配位置:new 操作从自由存储区为对象动态分配内存空间,而 malloc 函数从堆上动态分配内存。自由存储区不仅可以是堆,还可以是静态存储区,这取决于 new 在哪里为对象分配内存。
- 返回类型安全性:new 操作符内存分配成功时,返回的是对象类型的指针,与对象类型严格匹配,无需进行类型转换,因此 new 是符合类型安全性的操作符。而 malloc 函数在内存分配成功后返回的是 void*,需要通过强制类型转换将 void* 指针转换成所需类型。
- 内存分配失败时的返回值:当 new 内存分配失败时,会抛出 bac_alloc 异常,不会返回 NULL;而 malloc 内存分配失败时,返回 NULL。
- 是否需要指定内存大小:使用new申请内存时,不需要指定内存块的大小,编译器会根据类型信息自行计算;而malloc则需要显式地指出所需内存块的大小。
总的来说,new和malloc在内存分配位置、返回类型安全性、内存分配失败时的返回值以及是否需要指定内存大小等方面存在差异。
2、malloc的底层实现
(1)当开辟的空间小于 128K 时,调用 brk()函数,malloc 的底层实现是系统调用函数 brk(),其主要移动指针 _enddata(此时的 _enddata 指的是 Linux 地址空间中堆段的末尾地址,不是数据段的末尾地址)
(2)当开辟的空间大于 128K 时,mmap()系统调用函数来在虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空间来开辟。
**3、**在1G内存的计算机中能否malloc(1.2G)?为什么?
在1G内存的计算机中,无法使用malloc(1.2G)。
在C语言中,malloc()函数用于动态分配内存。当使用malloc()请求超过可用内存的大小时,会发生两种情况:
- 分配失败:如果请求的内存大小超过了操作系统可用的内存大小,malloc()将返回NULL,表示分配失败。在这种情况下,程序将无法继续执行。
- 分配成功但导致崩溃:如果请求的内存大小超过了可用的内存大小,但malloc()仍然返回了非NULL的指针,这通常是由于操作系统使用了内存映射技术或虚拟内存机制。这种情况下,程序可能会继续执行,但它可能会遇到未定义的行为,例如访问已分配的内存时崩溃或导致性能下降。
在1G内存的计算机中,由于可用内存只有1GB,因此无法使用malloc(1.2G),因为这将请求超过可用内存大小的内存大小。如果尝试这样做,malloc()将返回NULL,并且程序将无法继续执行。
4、指针与引用的相同和区别;如何相互转换?
引用时C++独有的特性,而指针则是C/C++都有的,它们有一些相似之处,但也有很多区别。
- 相同点:
- 它们都是用来间接访问内存的方式。
- 它们本身都占用内存(指针占用内存来存储所指向对象的地址,引用则是对象的别名,所以其实引用本身也是对象)。
- 它们都可以用来访问动态分配的内存(使用malloc()等函数)。
- 不同点:
- 指针是一个实体,它存储的是一个地址,指向内存的一个存储单元。而引用是原变量的一个别名,它存储的是原变量的值。
- 指针可以被重新赋值,指向不同的对象,而引用在声明后就不能改变其指向的对象。
- 指针可以被const修饰,而引用不能被const修饰。
- sizeof运算符在操作引用时得到的是对象本身的大小,而操作指针时得到的是指针变量本身的大小。
- 相互转换:
- 指针转引用:将指针变量的值直接赋值给引用变量即可。
- 引用转指针:可以使用类型转换操作符(*)将引用转换为指针。
总的来说,指针和引用在功能和使用上有一些区别,需要根据具体情况选择使用哪种方式。
5、extern"C”的作用
extern "C"是一个C语言链接器的关键字,它有以下几个作用:
- 使得在C++中使用C编译方式成为可能,指明该函数使用C编译方式。
- 在某些情况下,使用extern "C"声明函数,可以使得C++编译器按照C语言的方式对函数进行编译和链接,从而能够正确地调用该函数。
- extern "C"可以用于在C++中调用由C语言编写的库函数。因为C++与C语言的函数调用方式不同,使用extern "C"可以指明该函数使用C语言的方式进行链接,避免因为链接方式不同而导致的错误。
- 在使用动态链接库(DLL)时,使用extern "C"可以保证函数的导出和导入与C语言一致。
总之,extern "C"的作用是为了在C++中使用C语言的编译和链接方式,并且在一些特定情况下保证函数的正确导出和导入。
6、重写memcpy()函数需要注意哪些问题,(strcat strncat strcmp strcpy)那些函数会导致内存溢出?
重写
memcpy()
函数时需要注意以下问题:
- 源地址和目标地址的正确性:确保源地址和目标地址是有效的、合法的内存地址。在复制过程中,如果源地址无效或目标地址不足,会导致错误或未定义的行为。
- 复制的字节数:确保复制的字节数不超过目标地址的剩余空间,以避免内存溢出。在复制过程中,如果目标地址的剩余空间不足以容纳要复制的数据,会导致内存溢出。
- 目标地址的内存分配:如果目标地址是动态分配的内存区域(例如通过
malloc()
函数分配的内存),则在复制之前需要确保目标地址的内存已经分配,并且在复制完成后及时释放,以避免内存泄漏。- 边界检查和错误处理:对于类似于
strcat()
、strncat()
、strcmp()
、strcpy()
等函数,在处理字符串时需要注意边界检查,确保输入的字符串不会超过目标地址的限制。对于错误的输入,应该有适当的错误处理机制,例如报错或者返回错误码。下面是关于
strcat()
、strncat()
、strcmp()
、strcpy()
函数的一些注意事项:
strcat()
函数用于将一个字符串追加到另一个字符串的末尾。如果目标字符串的长度不足,或者目标字符串的剩余空间不足以容纳要追加的数据,就会导致内存溢出。strncat()
函数类似于strcat()
,但是可以指定要复制的字节数。在使用strncat()
时,需要确保目标字符串的剩余空间足够大,以避免内存溢出。strcmp()
函数用于比较两个字符串是否相等。这个函数不会导致内存溢出,但是需要注意输入的字符串长度,如果输入的字符串过长可能会导致性能问题。strcpy()
函数用于将一个字符串复制到另一个字符串中。在使用strcpy()
时,需要确保目标字符串的长度足够大,以避免内存溢出。此外,还需要注意源字符串是否具有足够的空间,以避免访问越界的问题。
7、char 和 int 之间的转换
在C/C++中,可以使用以下方式将char和int之间进行转换:
- 将char转换为int:
- 使用强制类型转换,将char的ASCII码值转换为int。例如,对于char c = ‘A’;,可以使用int i = (int)c;来将c转换为int类型的i。
- 如果char类型的数据只包含ASCII码值,可以使用自然的隐式转换。例如,对于char c = ‘A’;,可以使用int i = c;来将c转换为int类型的i。
- 将int转换为char:
- 如果int类型的数据是ASCII码值,可以直接赋值给char类型的变量。例如,对于int i = 65;,可以使用char c = i;来将i转换为char类型的c。
- 如果int类型的数据不是ASCII
8、static的用法(定义和用途) static静态变量,只初始化一次
在编程中,
static
是一个关键字,用于指明变量或函数的特性。在C语言中,static
可以用于以下两种情况:
- 定义静态变量:
- 静态变量是在程序执行期间只初始化一次的变量。它们在程序开始时被分配内存,并在程序结束时被释放。
- 静态变量默认值为0,但也可以在定义时显式地初始化。
- 静态变量可以用于记录某些状态或计数器,因为它们不会在程序运行期间被重新初始化。
- 定义静态函数:
- 静态函数是在程序执行期间只初始化一次的函数。它们在程序开始时被分配内存,并在程序结束时被释放。
- 静态函数只在其定义的模块中被使用,不能被其他模块直接调用。
- 静态函数可以用于实现某些特定的功能,例如加密、解密、字符串处理等。
9、const的用法(定义和用途)
- 告诉编译器该变量的值不能被修改:
使用 const 关键字定义变量可以明确告知编译器该变量的值不可被修改,这有助于编译器进行类型检查和代码优化。- 避免不必要的内存分配:
使用 const 关键字定义变量可以避免不必要的内存分配,因为编译器会将这些常变量存储在符号表中,而不是在内存中。- 在函数中使用:
在函数中使用 const 关键字可以确保函数不会修改传入参数的值,这有助于提高函数的可读性和安全性。同时,函数也可以返回 const 类型的值,以确保函数返回的值不会被修改。
10、const 常量和 #define 的区别(编译阶段、安全性、内存占用等)
const 常量和#define 都是在C/C++中用于定义常量,但它们之间有一些重要的区别。
- 定义方式:
const 关键字用于定义常量,它是类型安全的,可以用于声明各种类型的常量,如整数、浮点数、字符等。而#define 是通过预处理器定义的,它可以定义任何类型的常量,包括字符串和其他复杂的表达式。- 编译阶段:
const 是在编译阶段处理的,它在编译时被解析为常量表达式,并且可以直接内联到代码中。而#define 是由预处理器在编译预处理阶段处理的,它会被替换为相应的常量表达式。- 类型安全:
const 关键字是类型安全的,它需要在声明时指定类型,并且在编译时进行类型检查。而#define 没有类型检查,它只是一个简单的文本替换,没有类型检查和语法检查。- 内存占用:
const 常量是静态分配内存的,它们被存储在程序的数据区中,并且在编译时就已经确定了其值。而#define 只是一个符号,在编译预处理阶段被替换为相应的值,因此它不会分配内存,只是在编译时进行了一次文本替换。- 可读性和安全性:
const 常量具有更好的可读性和安全性,它们可以提供更明确的语义,告诉读者该值是不能被修改的。而#define 只是一个预处理器指令,容易被误用或滥用,因此它的可读性和安全性相对较低。综上所述,const 常量提供了更好的类型安全性和可读性,并且在编译时进行内存分配,同时也具有更好的性能。而#define 则更灵活,可以在编译预处理阶段进行一些复杂的操作,但它的类型安全性和可读性相对较低。在实际编程中,应根据具体情况选择使用 const 常量还是 #define。
11、 volatile作用和用法
volatile 是一个修饰符,用于声明在多线程环境下可能会被意外修改的变量。
在多线程环境下,由于有多个线程同时访问共享变量,可能会导致一些意外的行为,如一个线程正在修改变量时,另一个线程正在读取该变量,读取到的值可能不是最新的值。这种情况下,就需要使用 volatile 关键字来确保变量的可见性和一致性。
具体来说,使用 volatile 关键字可以保证以下几点:
- 保证变量的可见性:当一个线程修改变量的值时,其他线程会立即看到这个最新的值。
- 防止指令重排:由于编译器可能会对代码进行优化,导致一些指令的执行顺序与代码中的顺序不一致。使用 volatile 关键字可以防止这种指令重排,确保代码的执行顺序与代码中的顺序一致。
需要注意的是,虽然使用 volatile 关键字可以保证变量的可见性和一致性,但它不能保证线程之间的同步。如果需要保证线程之间的同步,还需要使用其他的同步机制。
12、变量的作用域(全局变量和局部变量)
变量的作用域是指变量在程序中可以使用的范围。在C/C++中,变量可以分为全局变量和局部变量两种。
- 全局变量(global variable):
全局变量是在函数外部定义的变量,它们的作用域是整个程序,可以从头文件一直使用到程序结束。全局变量通常在程序启动时初始化,并且可以被程序中的多个函数共同使用。- 局部变量(local variable):
局部变量是在函数内部定义的变量,它们的作用域仅限于函数内部。当函数执行结束时,局部变量会被销毁,其内存空间也会被释放。
13、sizeof 与 strlen (字符串,数组)
sizeof() 是 C/C++ 中的运算符,用于获取变量或数据类型在内存中所占用的字节数。而 strlen() 是 C/C++ 中的函数,用于计算字符串的长度(不包括字符串结束符 ‘\0’)。
对于字符串和数组,sizeof() 和 strlen() 的使用有以下区别:
- 字符串:
- sizeof() 运算符用于获取整个字符串占用的字节数,包括字符串结束符 ‘\0’。例如,对于 char str[] = “hello”;,sizeof(str) 的结果是 6(包括 ‘\0’)。
- strlen() 函数用于获取字符串的长度,即从字符串的起始地址开始,直到遇到结束符 ‘\0’ 前,所经过的字节数。例如,对于 char str[] = “hello”;,strlen(str) 的结果是 5。
- 数组:
- sizeof() 运算符用于获取整个数组占用的字节数。例如,对于 int arr[] = {1, 2, 3};,sizeof(arr) 的结果是 sizeof(int) * 数组长度(即 sizeof(int) * 3)。
- strlen() 函数通常不用于数组,因为数组没有结束符 ‘\0’,无法计算其长度。
需要注意的是,sizeof() 运算符的结果是一个编译时确定的常量表达式,而 strlen() 函数需要在运行时逐个字符地计算字符串的长度。在使用时需要根据具体情况选择合适的函数,并注意避免越界访问和空指针异常等问题。
14、经典的sizeof(struct)和内存对齐(一字节对齐)
在C语言中,sizeof()运算符用于获取变量或数据类型的大小。对于struct类型,sizeof()运算符将返回整个struct的大小,而不是每个成员变量的大小之和。这是因为C语言中的struct成员变量的对齐方式可能会导致struct的大小不是成员变量大小的整数倍。
内存对齐是为了提高内存访问的效率而引入的一种内存布局技术。在32位系统中最小的对齐单位是4字节,在64位系统中最小的对齐单位是8字节。根据对齐单位,编译器会在变量或结构体成员之间插入填充字节,使得它们的起始地址满足对齐要求。
对于一字节对齐,它通常用于一些特定的数据类型,例如char类型,它占用的空间就是1个字节。对于一些需要按照字节进行访问的数据类型,例如位域(bit-fields),也需要使用一字节对齐来保证数据的正确性。
在使用sizeof()运算符时,需要注意以下几点:
- 对于结构体或联合体类型,sizeof()返回的大小是整个结构体或联合体的占据的内存空间,而不是每个成员变量的大小之和。
- 在进行内存对齐时,需要考虑结构体成员变量的类型、大小和顺序,以及编译器和系统的对齐要求。合理地规划内存布局可以提高程序的性能和效率。
- 对于一些特殊的对齐要求,例如一字节对齐,需要使用特定的语法或技巧来实现,例如使用#pragma pack指令或者在结构体中使用特定的对齐修饰符(如__attribute__((packed))。
总之,sizeof()运算符和内存对齐是C语言中重要的概念,需要熟悉和掌握它们的使用方法和注意事项,以便更好地管理内存和提高程序的性能和效率。
推荐博客地址:http://t.csdn.cn/sAlrc
15、const * char 与const char *
“const * char” 和 “const char *” 都是C语言中的指针声明,但它们有一些不同之处。
- “const * char”:
- 这个声明表示一个指向常量字符的指针。这里的"const"修饰的是指针所指向的数据,即字符数据是不可修改的,而指针本身是可修改的。
- 可以用作指向字符数组的首地址,或者指向字符串的地址。
- “const char *”:
- 这个声明表示一个指向字符常量的指针。这里的"const"修饰的是指针本身,即指针自身是
不可修改的,而指针所指向的数据是可修改的。- 通常用于指向字符串字面值或字符常量。
总结来说,"const * char"表示一个指向常量字符的指针,而"const char *"表示一个指向字符常量的指针。在C语言中,对于字符串字面值或字符常量,通常使用"const char *"来声明指针。
16、inline函数
inline
是一个C/C++中的关键字,用于告诉编译器该函数的实现应该在编译时进行内联。内联函数的目的是为了减少程序的运行时开销,通过在编译时将函数的调用替换为函数的实现,避免了函数调用的额外开销,提高了程序的执行效率。
17、内存四区,什么变量分别存储在什么区域,堆上还是栈上。
在执行一个C/C++语言程序时,会将程序分配到的内存分为四个区域:栈区、堆区、全局区(静态区)和代码区。每个程序都有唯一的四个内存区域,我们需要熟悉和了解各个区域的特性,例如存储什么类型的数据,有谁去申请开辟,又有谁去管理释放。
- 栈区:栈区是用来存储函数调用和局部变量的一块内存区域,它是在程序执行时自动分配的,并且只在函数调用时使用,不会造成内存泄漏。在函数返回后,栈区会自动释放。
- 堆区:堆区是用来存储动态分配的内存的一块内存区域,它是由程序员分配和释放的。在程序执行时,通过malloc等函数从堆区分配一块内存,使用完后需要手动释放,否则会造成内存泄漏。
- 全局区(静态区):全局区是用来存储全局变量和静态变量的一块内存区域,它是在程序执行时就已经分配好并且一直存在直到程序结束时才被释放。
- 代码区:代码区是用来存储程序代码的一块内存区域,它是在程序执行时就已经分配好并且一直存在直到程序结束时才被释放。
需要注意的是,这四个区域是按照顺序依次排列的,并且每个区域的存储数据类型和申请方式都有所不同,需要根据具体情况进行管理和使用。
第三章 :网络编程
1 、TCP、UDP的区别
TCP(传输控制协议)和UDP(用户数据报协议)是两种常见的网络传输协议,它们有以下区别:
- 连接:TCP是面向连接的协议,而UDP是无连接的协议。在发送数据前,TCP通过三次握手建立连接,而UDP不需要建立连接。
- 可靠性:TCP提供可靠的数据传输,保证了数据的可靠性和完整性。TCP通过序号、确认应答和超时重传等机制来实现可靠性。而UDP不提供可靠性保证,数据可能丢失、损坏或重复。
- 传输速度:UDP比TCP快,因为它不需要建立连接和提供可靠性保障。在一些场景中,如视频、音频等实时应用,UDP更适合。
- 数据量限制:UDP的数据包大小通常受限于底层网络协议(如以太网),一般不超过1472字节。而TCP的数据包大小则取决于操作系统和底层网络协议。
- 应用场景:TCP适用于可靠性要求高的应用场景,如网页浏览、电子邮件等。UDP适用于对速度和实时性要求高的应用场景,如在线游戏、视频流等。
- 头部开销:TCP的首部较大,为20个字节,而UDP的首部较小,为4个字节。
总的来说,TCP提供了可靠的连接和数据传输,但相对较慢;而UDP快,但数据传输不可靠。在选择传输协议时,需要根据具体的应用场景来权衡。
2、TCP、UDP的优缺点
TCP和UDP都有各自的优缺点,以下是它们的比较:
TCP的优点:
- 可靠:TCP提供了可靠的连接,保证了数据的可靠性和完整性,适用于对数据准确性要求高的场景。
- 高速:TCP的传输速度相对较快,尤其是在底层网络支持的情况下。
- 适应性:TCP的协议规范和实现独立于底层网络协议,可以适应不同的网络环境。
- 多路复用:TCP通过连接机制实现了多路复用,可以同时处理多个数据流,提高了传输效率。
TCP的缺点:
- 连接管理:TCP需要建立连接和断开连接的过程,这可能会导致一定的开销和延迟。
- 数据顺序:TCP在传输过程中需要维护数据顺序,如果某个数据包丢失或乱序,需要重新发送,这可能会影响传输速度。
- 数据量限制:TCP的数据包大小受限于底层网络协议,如以太网的最大传输单元(MTU),超过该限制可能导致数据被分割为多个小包传输。
- 流量控制:TCP的传输速率受限于底层网络协议的带宽,如果发送速率过快,可能导致数据包丢失或传输受阻。
UDP的优点:
- 快速:UDP的传输速度相对较快,尤其适用于实时性要求高的场景,如在线游戏、视频流等。
- 灵活性:UDP的数据包大小灵活,可以根据应用需要调整,同时不需要维护数据顺序。
- 简单:UDP的协议相对简单,易于实现和维护,适用于一些轻量级的应用场景。
- 多路复用:UDP的多路复用机制可以提高传输效率,同时处理多个数据流。
UDP的缺点:
- 不可靠:UDP不提供可靠性保障,数据可能丢失、损坏或重复,适用于一些对数据准确性要求不高的场景。
- 数据顺序:UDP的数据包顺序需要应用程序自行处理,适用于一些简单的应用场景。
- 数据量限制:UDP的数据包大小通常受限于底层网络协议(如以太网),一般不超过1472字节。4. 流控制:UDP的发送速率受限于底层网络协议的带宽和接收方的能力,可能导致数据包丢失或传输受阻。
- 数据错误处理:UDP的数据包在传输过程中可能发生错误,如损坏或重复,应用程序需要自行处理这些问题。
- 连接管理:UDP没有建立连接和断开连接的过程,因此不需要维护连接状态,这使得它更加轻量级,适用于一些简单的应用场景。
3、TCP UDP适用场景
TCP和UDP都有各自的适用场景。
TCP(传输控制协议)通常用于对准确性要求相对高的场景,比如文件传输、接受邮件和远程登录。TCP在传输数据时需要进行数据的确认、重发、排序等操作,因此相对来说效率没有UDP(用户数据报协议)高。
UDP常用于对实时性要求高的场景,例如在线游戏、流媒体传输。UDP不可靠,因此它不适用于需要保证数据完整性和可靠性的应用场景。但因为UDP不需要进行连接管理、确认和重传等操作,所以它的数据传输速度比TCP快。同时,UDP还支持多播技术,可以将数据包发送到多个目的地。
总的来说,TCP和UDP都有各自的优点和适用场景,选择使用哪一种协议,需要根据具体的需求来决定。
4、TCP为什么是可靠连接
TCP之所以被认为是可靠连接,是因为它采用了以下几种机制来确保数据的可靠传输:
- 确认和应答机制:TCP通过发送数据后等待接收方的确认,以及在数据传输过程中采用应答机制,确保每个数据包都被正确接收。如果接收方没有确认收到某个数据包,发送方将重新发送该数据包,直到接收方确认收到为止。
- 序列号:TCP给每个数据包分配一个独特的序列号,接收方可以根据序列号对数据包进行排序,确保数据包的顺序正确。
- 超时重传:如果发送方在一定时间内没有收到接收方的确认,将重新发送数据包,以确保数据包能够被正确传输。
- 流量控制:TCP通过流量控制机制,确保发送速度不会超过接收方处理速度,避免数据包丢失或传输错误。
- 拥塞控制:当网络拥塞时,TCP会减缓发送速度,以避免过度拥塞网络,导致数据包丢失。
这些机制的结合,使得TCP能够在网络环境下实现可靠的连接。
5、OSI典型网络模型,简单说说有哪些
OSI(开放式系统互联)典型网络模型是由七个层次组成的模型,每个层次都有其特定的功能和服务。以下是这七个层次及其简要功能:
- 物理层:这一层的主要功能是建立物理连接,将比特流从一个地方传输到另一个地方,并进行数模转换和模数转换。
- 数据链路层:这一层的主要功能是为网络层提供服务,确保数据在物理层上的传输可靠。数据链路层还提供了一些错误检测和校正功能。
- 网络层:这一层的主要功能是进行逻辑地址寻址,实现不同网络之间的路径选择。
- 传输层:这一层的主要功能是定义传输数据的协议端口号,并进行流控和差错校验。其中最常用的协议是TCP(传输控制协议)和UDP(用户数据报协议)。
- 会话层:这一层的主要功能是建立、管理和终止会话。会话层还提供了一些同步和对话控制的功能。
- 表示层:这一层的主要功能是将数据进行加密、解密、压缩、解压缩等处理,以确保数据的完整性和一致性。
- 应用层:这一层是OSI模型的最上层,它提供了网络服务与最终用户的一个接口,用户可以操作的一个界面终端。
这些层次协同工作,共同构成了一个完整的、完善的网络模型。
6、三次握手、四次挥手
三次握手和四次挥手是TCP协议中建立连接和关闭连接的过程。
三次握手:
- 客户端向服务器发送SYN(同步)报文,请求建立连接。此时,客户端的SYN=1,ACK=0。
- 服务器收到客户端的SYN报文后,向客户端发送SYN+ACK(同步+确认)报文,表示确认收到客户端的请求,并请求建立连接。此时,服务器的SYN=1,ACK=1。
- 客户端收到服务器的SYN+ACK报文后,向服务器发送ACK(确认)报文,表示确认收到服务器的请求。此时,客户端的ACK=1。
四次挥手:
- 客户端向服务器发送FIN(结束)报文,请求关闭连接。此时,客户端的FIN=1,ACK=0。
- 服务器收到客户端的FIN报文后,向客户端发送ACK(确认)报文,表示确认收到客户端的请求关闭连接的请求。此时,服务器的ACK=1。
- 服务器向客户端发送FIN(结束)报文,请求关闭连接。此时,服务器的FIN=1,ACK=0。
- 客户端收到服务器的FIN报文后,向服务器发送ACK(确认)报文,表示确认收到服务器的请求关闭连接的请求。此时,客户端的ACK=1。
这样,TCP连接就建立成功了,并且确保了连接的可靠性。
第四章:常见算法
1、什么是 pid 算法,pid 算法有什么调参经验?
PID算法是一种常见的控制算法,用于调整系统的输入信号,以使系统的输出达到预期的目标。PID算法的名字来源于其三个基本的控制元素:比例(P)、积分(I)和微分(D)。
PID算法的主要作用是将系统的实际输出与期望输出之间的误差最小化。具体的调参经验如下:
- 比例系数(P):比例系数主要影响系统的稳定性。比例系数过大可能导致系统不稳定,而比例系数过小则可能导致系统的反应速度过慢。因此,在调参时需要根据实际情况选择适当的比例系数。
- 积分系数(I):积分系数主要影响系统的精度。积分系数过大可能导致系统出现过度调整,而积分系数过小则可能导致系统的精度不够。在调参时,需要根据系统的精度要求选择适当的积分系数。
- 微分系数(D):微分系数主要影响系统的响应速度。微分系数过大可能导致系统反应过快,而微分系数过小则可能导致系统的响应速度过慢。在调参时,需要根据系统的响应速度要求选择适当的微分系数。
此外,还有其他一些调参经验,如:
- 一般来说,先调整比例系数,再调整积分系数和微分系数。
- 在调整积分系数和微分系数时,需要根据系统的延迟时间和响应速度进行综合考虑。
- 在调整参数时,需要进行逐步调整,以避免参数过大或过小的跳跃。
总之,PID算法的调参是一个需要综合考虑各种因素的过程,需要根据实际情况进行灵活的调整
2、常见的滤波算法有哪些,如何实现的?
常见的滤波算法包括:一阶补偿滤波,算术平均滤波,中位值滤波,限幅平均滤波,滑动平均滤波和卡尔曼滤波。
推荐博客地址:http://t.csdn.cn/YHNiK
3、各种排序算法的时间空间复杂度、稳定性
4、十大排序算法的实现
推荐博客地址:http://t.csdn.cn/Wmf3V
5、二分法查找的原理
二分法查找(也称为折半查找)是一种在有序数组中查找特定元素的搜索算法。其基本原理如下:
- 首先,将数组按升序排列,使得数组中的元素位于一个单调递增的序列中。
- 接着,选择数组的中间元素作为查找的起始点,如果该元素正好是要查找的元素,则查找过程结束,否则执行下一步。
- 如果要查找的元素大于中间元素,则在数组的右半部分重复步骤2;如果要查找的元素小于中间元素,则在数组的左半部分重复步骤2。
- 如果在某一步中,数组为空,则表示查找失败,即要查找的元素不在数组中。
通过不断将查找区间缩小一半,最终可以找到要查找的元素,或者确定该元素不在数组中。
第五章: Linux操作系统常见面试题
1、Linux内核的组成部分
Linux内核主要由以下几个部分组成:
- 进程调度(SCHED):负责控制CPU对进程的访问,使多个进程能够在同一时间竞争CPU资源。
- 内存管理(MM):负责管理系统的内存,包括物理内存和虚拟内存,以及内核对内存的访问控制。
- 虚拟文件系统(VFS):提供对文件系统的统一抽象接口,使得不同的文件系统能够在更高层次上进行访问和操作。
- 网络接口(NET):提供对网络协议的支持,包括网络接口驱动程序和网络协议的处理。
- 进程间通信(IPC):提供进程之间的通信机制,包括信号、管道、共享内存等。
- 模块管理器(MODULES):负责加载和卸载内核模块,以及与模块相关的管理和维护工作。
- 系统调用接口(SYSCALLS):提供用户空间与内核空间之间的接口,使得用户程序能够调用内核提供的系统功能。
这些组成部分共同构成了Linux内核,使得Linux操作系统具有了强大的功能和灵活性。
2、用户空间与内核通信方式有哪些?
用户空间与内核空间之间的通信方式有以下几种:
- 系统调用(System Call):当用户程序需要内核提供服务时,可以通过系统调用接口发起系统调用。系统调用是一种由用户程序主动发起的从用户空间向内核空间发送的请求,通常用于完成一些与硬件或系统相关操作,如文件操作、进程控制、内存管理等。
- 中断(Interrupt):当硬件或软件事件发生时,可以通过中断接口将控制权转移到内核空间。中断通常用于处理异步事件,如硬件中断、时钟中断、进程中断等。
- 信号(Signal):信号是一种由内核或进程发送的异步事件,用于通知用户程序某些事件已经发生或需要立即执行某个操作。信号通常用于进程间的通信和同步,如进程终止信号、错误信号等。
- 管道(Pipe):管道是一种半双工的通信机制,用于实现进程之间的数据传输和共享。管道可以是字节流或消息流,可以用于实现进程之间的父子进程通信或进程与内核模块之间的通信。
- 共享内存(Shared Memory):共享内存是一种高效的进程间通信方式,通过将同一块物理内存映射到多个进程的虚拟地址空间中,使得多个进程可以同时访问和修改该内存区域中的数据。共享内存可以实现不同进程之间的数据共享和同步。
这些通信方式提供了用户空间和内核空间之间的接口,使得用户程序能够调用内核提供的系统功能,同时也实现了用户空间和内核空间之间的数据传输和通信。
3、系统调用read()/write(),内核具体做了哪些事情
当用户程序调用read()或write()系统调用时,内核会执行以下一系列操作:
- 验证参数:内核会验证用户程序传递的参数是否合法,包括文件描述符、缓冲区地址、字节数等。如果参数不合法,内核将返回错误码,并终止系统调用。
- 权限检查:内核会检查用户程序是否有足够的权限执行读取或写入操作。如果权限不足,内核将返回错误码,并终止系统调用。
- 数据传输:如果参数合法且权限检查通过,内核会根据文件描述符所代表的文件类型和文件偏移量,将数据从文件或设备中读取或写入到用户程序的缓冲区中。
- 缓冲区管理:内核会管理文件缓冲区,确保读取和写入的数据正确地缓存和刷新。在读取和写入完成后,内核会将缓冲区中的数据传递给用户程序或从用户程序接收数据。
- 内存管理:在读取和写入数据时,内核会管理内存,确保数据的正确存储和访问。如果需要,内核会分配或释放内存,以适应数据的大小和访问需求。
- 错误处理:在读取或写入操作完成后,内核会检查是否有错误发生。如果有错误发生,内核会记录错误码,并将错误信息传递给用户程序。
- 返回结果:如果读取或写入操作成功完成,内核会将结果返回给用户程序,包括读取或写入的字节数等。
总之,当用户程序调用read()或write()系统调用时,内核会执行一系列操作来验证参数、检查权限、传输数据、管理缓冲区和内存,并处理错误,并将结果返回给用户程序。这些操作保证了用户程序与内核之间的安全和正确的数据传输。
4、系统调用与普通函数调用的区别
系统调用和普通函数调用在以下几个方面存在区别:
- 调用方式:系统调用是通过中断实现的,而普通函数调用是通过函数调用实现的。系统调用会将控制权从用户空间切换到内核空间,而普通函数调用是在用户空间内进行的。
- 调用层次:系统调用是操作系统提供给应用程序的接口,可以访问硬件和系统资源,而普通函数调用通常是为了实现应用程序内部的逻辑。
- 调用时长:系统调用的处理速度通常比普通函数调用慢,因为系统调用需要额外的开销,例如中断处理和上下文切换。
- 调用安全性:系统调用在执行时具有更高的安全性,因为它们会进行参数验证和权限检查,而普通函数调用通常不会进行这些检查。
- 调用范围:系统调用是全局的,可以跨越不同的进程和用户空间,而普通函数调用是局部的,仅限于当前进程和用户空间。
总之,系统调用和普通函数调用在调用方式、调用层次、调用时长、调用安全性和调用范围等方面存在明显的区别。系统调用是为了实现操作系统提供给应用程序的接口,而普通函数调用则是为了实现应用程序内部的逻辑。
5、内核态,用户态的区别
内核态和用户态是操作系统中的两个不同运行级别,它们之间存在以下区别:
- 权限不同:内核态拥有更高的权限,可以访问系统的所有资源,而用户态只能访问受限的资源,例如内存、硬件设备等。
- 访问资源:在内核态下,操作系统可以直接访问系统资源,而不需要通过用户程序的请求,而在用户态下,用户程序需要通过系统调用才能访问系统资源。
- 运行空间:内核态运行在内核空间,拥有系统的全部内存空间,而用户态运行在用户空间,只拥有有限的内存空间。
- 任务执行顺序:内核态是抢占式执行,优先级更高,可以中断用户态任务的执行,而用户态是协作式执行,优先级较低。
- 驱动程序:内核态下运行的是驱动程序,用于管理硬件设备的接入和访问,而用户态下运行的则是应用程序和库函数。
内核态和用户态之间的区别是为了保证系统的安全性和稳定性。通过限制内核态的访问权限和资源,可以防止恶意程序对系统造成损害,同时也提供了更好的灵活性和可扩展性。
6、bootloade、内核、根文件的关系
BootLoader、内核和根文件系统是操作系统启动的三个重要组成部分,它们之间的关系如下:
- BootLoader:BootLoader是操作系统启动时首先执行的程序,它的作用是加载内核并将控制权交给内核。BootLoader通过读取内核映像文件,将其加载到内存中,然后跳转到内核的入口地址,使内核开始执行。
- 内核:内核是操作系统的核心部分,它负责管理系统的进程、内存、设备驱动等核心功能。在内核启动后,它会检查文件系统并挂载根文件系统到系统中。
- 根文件系统:根文件系统是操作系统的文件系统,它包含了操作系统的基本文件和目录。在内核启动后,它会检查文件系统并挂载根文件系统到系统中,使应用程序能够访问和操作这些文件和目录。
在操作系统的启动过程中,BootLoader会加载内核并将控制权交给内核,内核会初始化系统的各个组件,然后检查和挂载根文件系统到系统中。这样,应用程序就可以通过根文件系统访问和操作文件和目录。因此,BootLoader、内核和根文件系统之间的关系是相互依存、缺一不可的。
7、Bootloader启动过程
推荐博客地址:http://t.csdn.cn/e8hzp
8、Linux下检查内存状态的命令
在Linux下,可以使用以下命令来检查内存状态:
- free命令:可以显示系统内存状态,包括物理内存、已使用的内存、空闲内存、缓存等。
- vmstat命令:可以显示虚拟内存状态,包括内存使用情况、缓存、进程状态等。
- top命令:可以实时查看系统内存使用情况,包括内存使用率、进程状态、CPU使用率等。
- htop命令:可以以更为详细的格式显示系统内存使用情况,包括每个进程的内存使用情况、CPU使用率等。
- sar命令:可以生成系统性能报告,包括内存使用情况、CPU使用率、磁盘使用情况等。
- dstat命令:可以显示系统运行状态,包括内存使用情况、CPU使用率、磁盘使用情况等。
这些命令可以帮助用户实时查看系统内存状态,并对系统性能进行监控和分析。
9、大小端的区别以及各自的优点,哪种时候用。(判断大小端的三种方式)
大小端(Endian)是指计算机在存储和表示数据时,对于多字节的数据类型(如整数、长整数等),以不同的字节顺序进行存储和表示。
- 大端(Big-Endian)表示将数据的高位字节存储在低地址处,而低位字节存储在高地址处。
- 小端(Little-Endian)表示将数据的低位字节存储在低地址处,而高位字节存储在高地址处。
以下是一些关于大小端的优点和适用场景:
- 优点:
- 对于网络传输和跨平台通信,使用小端可以减少数据在不同平台之间的转换次数,提高传输效率。
- 对于大多数应用程序来说,使用小端可以减少内存的使用,因为低位字节可以更早地被释放掉。
- 对于一些特定的算法和数据结构,使用小端可以提高数据的局部性,从而提高缓存的命中率。
- 适用场景:
- 对于需要跨平台传输数据的应用程序,例如网络通信、分布式系统等,建议使用小端。
- 对于需要处理大量整数或浮点数运算的应用程序,例如科学计算、图形处理等,建议使用大端。
- 对于需要处理大量字符串操作的应用程序,例如文本编辑器、编译器等,建议使用小端。
判断大小端的方式有以下三种:
- 编写一个多字节的数据类型(例如int、long、float或double),并将其存储到内存中。然后,读取内存中的数据,并逐字节地检查它们的值,以确定字节顺序。
- 使用C标准库中的函数,例如htonl(主机字节序)和ntohl(网络字节序),将一个整数从主机字节序转换为网络字节序,或将网络字节序转换为主机字节序。通过比较转换前后的值来确定字节顺序。
- 读取和处理操作系统提供的系统寄存器或寄存器映射,以确定字节顺序。不同的处理器架构可能有不同的方式来读取和处理这些寄存器。
需要注意的是,不同的处理器架构可能具有不同的字节顺序,因此在进行跨平台通信或处理不同架构的数据时,应该特别注意字节顺序的问题。
10、一个程序从开始运行到结束的完整过程(四个过程)
1、编译预处理:处理伪指令
1> 头文件包含
2> 宏定义
3> 条件编译 #if #endif
gcc -E xxx.c -o xxx.i
2、编译:把预处理之后的文件进行语法分析,生成汇编代码
gcc -S xx.i -o xx.s
3、汇编:将汇编文件生成机器代码(二进制代码)
as xx.s -o xx.o
4、链接:去指定路径下找库函数 (头文件包含的是声明,具体实现封装在库中)
gcc xx.o -o xx
11、什么是堆,栈,内存泄漏和内存溢出?
堆(Heap)和栈(Stack)是计算机内存管理中的两个重要概念,而内存泄漏和内存溢出则是与内存管理相关的两个问题。
- 堆(Heap):堆是一种动态分配的内存区域,它由程序员分配和释放。堆的大小受系统限制,通常位于栈的顶部。在程序运行过程中,当需要分配大块内存时,可以通过调用malloc等函数从堆中分配。堆的优点是可以灵活地分配和释放内存,但需要注意的是,由于堆是动态分配的,可能会存在内存碎片和内存泄漏的问题。
- 栈(Stack):栈是一种静态分配的内存区域,它由系统自动分配和释放。栈的大小也受系统限制,通常位于内存的底部。在程序运行过程中,当需要分配局部变量或函数参数时,系统会自动从栈中分配内存。栈的优点是分配和释放内存的速度较快,但缺点是空间有限且大小固定,无法分配大块内存。
- 内存泄漏(Memory Leak):内存泄漏指的是程序中的某些代码由于某些原因未能释放已经不再需要的内存,导致内存无法被再次使用。这通常会导致程序运行过程中出现内存不足的情况,从而导致程序性能下降或崩溃。
- 内存溢出(Out of Memory):内存溢出指的是程序在运行过程中所需的内存超过了系统所能提供的内存大小。这通常会导致程序崩溃或出现不可预测的行为。为了解决内存溢出问题,程序员需要优化程序的内存使用,避免无谓的内存占用和泄漏。
综上所述,堆、栈、内存泄漏和内存溢出是计算机内存管理中的重要概念。程序员需要合理地使用堆和栈,避免内存泄漏和
12、堆和栈的区别
堆(Heap)和栈(Stack)是计算机内存管理中的两个重要概念,它们有以下区别:
- 分配方式:堆是动态分配的,而栈是静态分配的。在程序运行过程中,当需要分配大块内存时,可以通过调用malloc等函数从堆中分配,而栈则是在程序编译时就已经分配好的。
- 大小限制:栈的大小是有限的,而堆的大小受系统限制。栈通常位于内存的底部,大小固定,而堆位于栈的顶部,可能会占用系统的大部分内存。
- 内存释放:堆的内存释放需要手动进行,而栈的内存由系统自动释放。在程序运行过程中,如果需要释放堆中的内存,需要手动调用free函数,而栈中的内存会自动释放。
- 使用场景:栈通常用于存储局部变量、函数参数和返回地址等,而堆通常用于存储动态创建的数据结构和对象。
需要注意的是,由于堆是动态分配的,可能会存在内存碎片和内存泄漏的问题。同时,由于栈的大小有限制,如果分配过多的局部变量或递归调用导致栈溢出的问题也可能出现。
综上所述,堆和栈在分配方式、大小限制和内存释放方面存在明显的区别。程序员需要合理地使用堆和栈来满足程序的需求,避免内存泄漏和溢出问题。
13、死锁的原因、条件
死锁的原因主要有以下几个:
- 系统资源不足。在这种情况下,进程的资源请求可能无法得到满足,导致死锁。
- 进程运行推进的顺序不合适,或者由于进程运行推进顺序与速度不同,也可能导致死锁。
- 资源分配不当也是死锁的原因之一。
产生死锁的四个必要条件如下:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要上述条件之一不满足,就不会发生死锁,而只要系统发生死锁,这些条件必然成立。
14、硬链接与软链接的区别
硬链接和软链接在以下几个方面存在区别:
- 链接原理:硬链接是指通过文件索引节点(inode)进行的链接,而软链接(也叫符号链接)则是指通过文件路径进行的链接。
- 链接范围:硬链接仅能在同一文件系统内创建,而软链接可以跨越不同的文件系统。
- 文件名和文件路径:硬链接保持文件名不变,但文件路径可以自由更改,而软链接实际上是对文件的一个快捷方式,更改软链接的文件路径不会影响原始文件。
- 文件大小:硬链接和原始文件具有相同的大小,而软链接则保存了文件路径的长度。
- 创建方式:在Linux系统中,可以使用ln命令创建硬链接和软链接。创建硬链接时,需要使用-l选项,而创建软链接时,需要使用-s选项。
- 颜色区分:在Linux系统中,硬链接的文件名通常是白色的,而软链接的文件名则是蓝色的。
总结来说,硬链接和软链接在功能上有明显的区别。硬链接强调文件数据块的一致性,可以在不同的目录中创建相同的文件的多个副本,而软链接则强调指向文件的路径,可以跨越不同的文件系统。
15、虚拟内存,虚拟地址与物理地址的转换
虚拟内存是一种计算机内存管理技术,它将计算机的物理内存与硬盘上的虚拟内存结合起来,通过将部分硬盘空间作为内存来使用,从而实现扩大内存的效果。
在虚拟内存中,每个进程都有其独立的虚拟地址空间,用于存储进程的数据和代码。虚拟地址是进程用来访问内存的地址,它与物理地址不同,虚拟地址需要通过虚拟内存管理模块进行转换,才能与物理地址相对应。
虚拟地址通常包括两部分:基址和偏移量。基址是虚拟地址的起始值,而偏移量则是进程中某个数据或指令在虚拟地址中的位置。通过将基址和偏移量相加,可以得到进程在虚拟地址空间中的实际位置。
在虚拟内存中,为了将虚拟地址转换为物理地址,需要使用页表这个数据结构。页表记录了进程的虚拟地址和对应的物理地址之间的映射关系。当进程访问虚拟地址时,操作系统会根据页表中的映射关系,将虚拟地址转换为物理地址,并将数据从磁盘上的虚拟内存映射到物理内存中。
总结来说,虚拟内存、虚拟地址和物理地址之间的转换是通过页表实现的。通过将虚拟地址与页表中的映射关系相加,可以得到对应的物理地址,从而实现虚拟地址到物理地址的转换。(Linux貌似是通过内存管理单元MMU进行虚拟映射的!)
16、计算机中,32bit与64bit有什么区别
计算机的位数一般指操作系统的位数,32位操作系统可以寻址2的32次方个字节的内存范围,而64位操作系统则可寻址2的64次方个字节的内存范围。以下是它们的主要区别:
- 数据处理能力:32位计算机的CPU一次最多能处理32位数据,例如它的EAX寄存器就是32位的,而64位计算机一次可以处理8个字节,因此64位计算机数据处理能力更强,速度更快。
- 内存容量支持:32位的系统许多支持4G的内存,而64位系统则可以支持上百G的内存。
- 软件运行版本:64位的系统能够兼容32位的软件,但32位的系统不能向上兼容。
- 应用场景:由于64位数据处理能力更强,因此适合处理大规模数据运算和复杂任务,如大型数据库、虚拟化和科学计算等应用场景。而32位系统则更适合于小规模数据处理和简单任务,如Web浏览、电子邮件和轻度办公等应用场景。
总的来说,64位计算机相比32位计算机具有更强的数据处理能力和更大的内存支持,适用于大规模数据运算和复杂任务。而32位计算机则适用于小规模数据和简单任务。同时,64位操作系统也提供了更高的安全性。
17、中断和异常的区别
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数嵌入式工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)
最后
资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!
能向上兼容。
- 应用场景:由于64位数据处理能力更强,因此适合处理大规模数据运算和复杂任务,如大型数据库、虚拟化和科学计算等应用场景。而32位系统则更适合于小规模数据处理和简单任务,如Web浏览、电子邮件和轻度办公等应用场景。
总的来说,64位计算机相比32位计算机具有更强的数据处理能力和更大的内存支持,适用于大规模数据运算和复杂任务。而32位计算机则适用于小规模数据和简单任务。同时,64位操作系统也提供了更高的安全性。
17、中断和异常的区别
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数嵌入式工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-EOPbN7dN-1712390437916)]
[外链图片转存中…(img-9mIPefxb-1712390437918)]
[外链图片转存中…(img-9smvrETa-1712390437919)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!
[外链图片转存中…(img-f0XFjEpt-1712390437919)]
[外链图片转存中…(img-SGHFGiYV-1712390437920)]
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)
最后
资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!