首页/学习资料/大华嵌入式一面面经(下)/
大华嵌入式一面面经(下)
2024-04-18 17:42:33569浏览
大华嵌入式一面面经,个人经历回顾版

5、了解死锁么,它是咋么产生的以及如何解决?

死锁产生原因:

多个并发进程因争夺系统资源而产生相互等待的现象。

死锁的必要条件:

1、互斥:一个资源每次只能被一个进程(线程)使用,如果另一个进程(线程)请求该资源,那么它必须等待直到该资源被释放

2、占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其它进行释放该资源

3、不可抢占:资源不能被抢占,即只能由持有它的进程(线程)主动释放。

4、循环等待:存在一个进程(线程)的资源需求序列{P1, P2, ..., Pn},其中P1持有P2所需的资源,P2持有P3所需的资源,以此类推,Pn持有P1所需的资源,形成了一个循环等待的情况。

解决死锁:

1、预防死锁:

  • 破坏互斥条件:允许多个进程(线程)同时访问共享资源。
  • 破坏请求和保持条件:要求进程(线程)在获取所有资源之前不保持任何资源。
  • 破坏不可抢占条件:允许系统抢占进程(线程)持有的资源。
  • 破坏循环等待条件:对资源进行排序,强制所有进程按照相同的顺序请求资源。

2、避免死锁:

通过仔细地资源分配和进程调度,使系统永远不会进入死锁状态。常见的避免死锁的算法包括银行家算法等。

3、检测死锁:

周期性地检查系统中是否存在死锁,一旦检测到死锁,就采取措施解除死锁。常见的死锁检测算法包括资源分配图算法等。

4、解除死锁:

当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常用方法:

①剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,解除死锁状态

②撤销进程:可以直接撤销死锁进程或撤销代价最小的进程,直至有足够的资源可以使用,死锁状态消除为止


6、物理地址与虚拟地址,如何映射

物理地址:物理地址是内存条上真实存在的地址,是实际硬件上的地址,每个存储单元中都有一个唯一的物理地址,

虚拟地址:虚拟地址是由程序生成的地址。当程序访问内存时,它使用的是虚拟地址。

目的:通过使用虚拟地址,操作系统可以让每个程序都认为自己在使用一大块连续的内存空间,这块虚拟的内存空间由操作系统管理。虚拟内存技术允许多个程序并发运行,而不会相互干扰,提高了内存的使用效率和系统的安全性。

映射方式(从物理地址到虚拟地址的映射):

1、使用内存管理接口

操作系统通常提供了一系列的内存管理接口,允许内核代码请求页面映射,修改页表等。如,在Linux中,函数如vmalloc可以用于分配连续的虚拟地址空间,而底层的物理内存可能是非连续的。这些接口更多地被用于管理虚拟内存,关系到物理地址的管理和映射。

2、通过文件系统接口

某些操作系统提供了特殊的文件系统或文件系统接口,允许用户空间程序以文件的形式访问硬件设备或物理内存。例如,Linux的/dev/mem设备文件允许直接访问物理内存地址空间。通过打开并mmap这个设备文件,用户空间程序可以将特定的物理内存区域映射到其虚拟地址空间中。

int fd = open("/dev/mem", O_RDWR);
void *vaddr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, phys_addr);

3、Linux中映射物理地址到虚拟地址

Linux内核提供几种机制来将物理地址映射到内核的虚拟地址空间,最常用的是ioremap函数,ioremap用于映射物理内存地址到虚拟地址空间,通常用于设备驱动程序中以访问映射硬件的寄存器。

实例:假设有一个设备寄存器位于物理地址0x1FC00000,我们需要从Linux内核模块中访问这个寄存器

#include <linux/io.h>

void __iomem *my_reg;

// 映射物理地址0x1FC00000到虚拟地址空间
my_reg = ioremap(0x1FC00000, PAGE_SIZE);
if (!my_reg) {
printk("Cannot map register address\n");
return -ENOMEM;
}

// 使用ioread32(), iowrite32()等函数访问映射的寄存器
unsigned int val = ioread32(my_reg);

// 完成后解除映射
iounmap(my_reg);

ioremap函数将指定的物理地址范围映射到内核虚拟地址空间,返回一个指向这段虚拟地址的指针。这样,内核代码就可以通过这个虚拟地址来访问物理内存或设备寄存器。


7、TCP和UDP的区别以及TCP如何保证数据传输的稳定性

TCP与UDP的区别:

1、TCP是面向连接的,UDP是面向无连接的

2、TCP提供可靠的数据传输,UDP不提供可靠性保证

3、TCP能够使用滑动窗口机制来控制数据的发送速率,接收方通过窗口大小来告知发送方自己的接收能力,从而避免了发送过快导致接收方无法处理的情况,且还有拥塞控制机制,通过拥塞窗口和慢启动等算法来控制网络拥塞,而UDP没有流量控制和拥塞控制机制,可能会导致网络拥塞或丢包。

TCP的原理及数据传输稳定性

1、连接导向

TCP是面向连接的协议,通信双方在传输数据之前需要先建立连接。连接的建立包括三次握手的过程,确保通信双方都能够正常通信。

2、可靠性

TCP提供可靠的数据传输,通过序号、确认和重传机制确保数据的可靠性。每个数据包都有一个序号,接收方收到数据后会发送确认消息,发送方在一定时间内没有收到确认消息则会重传数据包。

3、流量控制

TCP使用滑动窗口机制来控制数据的发送速率,接收方通过窗口大小来告知发送方自己的接收能力,从而避免了发送过快导致接收方无法处理的情况。

4、拥塞控制

TCP通过拥塞窗口和慢启动等算法来控制网络拥塞。当网络出现拥塞时,TCP会减小发送窗口,降低发送速率,从而避免网络拥塞进一步恶化。


8、虚函数与纯虚函数了解么

虚函数:在基类中声明为虚拟的成员函数,允许在派生类中重新定义(override)该函数以实现多态性。通过使用虚函数,程序可以根据对象的实际类型调用适当的函数。

虚函数特点:

1、虚函数在基类中使用关键字 virtual 声明,并且在派生类中可以被重写(覆盖)。

2、虚函数在基类中有一个实现,但可以在派生类中重新实现(覆盖)。

3、如果派生类中未定义某个虚函数的实现,那么将使用基类中的实现。4、虚函数允许基类的指针或引用根据指向的实际对象类型调用相应的函数。

实例:

class Base {
public:
virtual void foo() {
cout << "Base::foo() called" << endl;
}
};

class Derived : public Base {
public:
void foo() override {
cout << "Derived::foo() called" << endl;
}
};

纯虚函数:在基类中声明为虚拟的成员函数,但没有在基类中提供实现。它只是一个函数原型,留给派生类去实现。纯虚函数使得基类成为抽象类,不能被实例化,但可以作为接口定义使用。

纯虚函数特点:

1、纯虚函数在基类中使用 virtual 关键字声明,并且用 =0表示没有实现。2、派生类必须实现纯虚函数,否则派生类也会变成抽象类。

3、抽象类(包含至少一个纯虚函数的类)不能被实例化,只能作为接口使用。

实例:

class AbstractBase {
public:
virtual void pureVirtualFunction() = 0; // 纯虚函数声明
};

class ConcreteDerived : public AbstractBase {
public:
void pureVirtualFunction() override {
cout << "ConcreteDerived::pureVirtualFunction() called" << endl;
}
};

在纯虚函数中,= 0是用来告诉编译器,该函数没有实现,需要在派生类中实现。因此,派生类必须提供实现,否则它也会变成抽象类。

友情链接: