谷动谷力

标题: 海康一面过!(纯Linux八股) [打印本页]

作者: sunsili    时间: 2023-10-5 23:31
标题: 海康一面过!(纯Linux八股)
海康一面过!(纯Linux八股)


今天给大家分享另一位网友的面经,只因为它比较特殊,全程Linux底层拷问。。。
全程四十分钟,把Linux操作系统,你知道的,不知道的,都问出来了。这些问题下来,如果你是电子信息专业的同学,那你能答多少呢?如果是计算机科学与技术或者是软件工程专业的同学,可能后边就废了。。。也可能不会问得那么偏。。可以说算是偏硬件的一个岗位吧。如果你有想学硬件的同学朋友,不妨也给他们瞅瞅。对于下边这些问题的回答,仅供大家参考,更详细的内容还请多看看书,那么理解就更深刻一点了。。1、平台总线、平台设备、平台驱动之间的关系?2、linux系统下一个可执行文件的虚拟地址空间是怎么分配的,用什么命令可以查看详细的分配空间分布?这个问题主要考察的是你对进程虚拟地址空间的理解,我们可以根据下图来进行更深刻的理解。操作系统加载可执行文件之后,就会创建一个进程,这个进程就有了自己的虚拟地址空间,而且每个进程的虚拟地址空间都一样。
如上图所画,进程的虚拟地址空间被统一划分成了多个区域。代码区和数据区域:来自于可执行文件,代码区和数据区挨着,代码区总是在0x08048000地址以上,0x08048000地址以下另有它用。运行时堆区域:它初始化大小为0,随着动态分配内存(malloc),运行时堆不断往高地址方向扩展,有个指针brk指向了堆的最高地址。共享库的内存映射区域:这个区域是一些标准的系统库,这个共享库在物理内存中只存储一份,每个进程将这个区域的虚拟地址映射到同一份共享库物理内存上。用户栈区域:这个区域紧挨着内核区域,处于高地址处,随着用户栈的出栈,入栈,动态扩展,入栈向低地址方向扩展,出栈则向高地址方向收缩,栈顶指针存储在栈寄存器(ESP)中。内核区域:这个区域是操作系统自己代码,数据,栈空间,内核在物理内存中只存储一份,每个进程将这个区域的虚拟地址映射到同一份内核物理内存上。下边是内核和共享库的映射关系
要查看详细的虚拟地址空间分配情况,可以使用pmap。pmap命令可以显示进程的内存映射情况,包括虚拟地址范围、映射文件、权限等信息。可以通过指定进程ID(PID)来查看特定进程的内存映射。pmap -x <PID>3、对于上面提到的各个地址段,内核是怎么管理的?4、如果一个进程在执行时直接访问物理地址会怎么样?5、段错误是怎么发生的,由谁触发的,发出的信号又是哪种?段错误是由操作系统检测到的,操作系统向程序发送了一个名为SIGSEGV(Segmentation Violation)的信号。这个信号表明程序尝试执行了非法的内存操作。一旦发生段错误,通常会导致程序异常终止,除非程序员在代码中明确处理了这种情况。6、linux内核中的缺页中断是怎么处理的?同时,也可以参考下边这张图:
7、linux内核是怎么做的进程切换的,切换进程的时候进程信息如何保存和恢复的,页表又是如何切换的?Linux内核中,进程切换是通过上下文切换来实现的。上下文切换是指将CPU的执行从一个进程或线程切换到另一个进程或线程,以实现多任务的并发执行。上面几个步骤详细说了进程信息的保存和恢复,以及页表是如何切换的。同时呢,进程切换是一个开销较大的操作,因为它涉及到保存和恢复大量的寄存器状态,以及切换页表等操作。因此,内核会尽量减少不必要的上下文切换,以提高系统的性能。同时,内核需要非常精确地管理各个进程的上下文信息,以确保切换的正确性和一致性。8、MMU是硬件还是软件,怎么工作的,进程切换页表的时候MMU有参与吗,具体是怎么参与的呢?MMU(Memory Management Unit)是计算机系统中的一个硬件组件,它负责管理内存的访问和地址映射。MMU的主要功能是将逻辑地址(也称为虚拟地址)映射到物理地址,以便CPU能够正确地访问内存中的数据。当操作系统切换进程时,它会改变MMU中的页表基地址,以便MMU可以开始使用新进程的页表来进行地址映射。确保了进程之间的内存隔离和安全性。参与过程:9、进程的页表基址是通过哪个结构体的哪个字段去访问的?进程的页表基址是通过进程的mm_struct结构体中的pgd字段来访问的。在Linux内核中,mm_struct结构体存储了有关进程的内存管理信息,而pgd字段指向了页全局目录(Page Global Directory),用于页表的顶层地址转换。
  1. #include <linux/sched.h>
  2. #include <asm/pgtable.h>

  3. // 获取当前进程的mm_struct
  4. struct mm_struct *mm = current->mm;

  5. // 获取当前进程的页表基址(pgd)
  6. pgd_t *pgd_base = mm->pgd;
复制代码


current是一个指向当前进程的task_struct的指针,可以通过#include <linux/sched.h>头文件访问。然后,通过current->mm来获取当前进程的mm_struct,再从中提取pgd字段,即页表基址。10、说一下主机的iic控制器对一个指定地址的从机设备中的指定寄存器读取数据的时序?IIC(Inter-Integrated Circuit)总线是一种用于在数字集成电路(IC)之间进行通信的串行通信协议。在I2C总线上,有一个主机设备(通常是微控制器或单板计算机)和一个或多个从机设备(例如传感器、外围设备等)之间进行通信。11、如果对上述过程使用示波器进行查看?12、写一个函数:双向链表中在指定节点后面插入新节点这算是出的比较友好的一道算法题了。以下代码可供参考。
  1. #include <iostream>

  2. // 双向链表节点定义
  3. struct Node {
  4. int
  5. data;
  6.     Node* prev;
  7.     Node* next;
  8.     Node(int val) : data(val), prev(nullptr), next(nullptr) {}
  9. };

  10. // 在指定节点后插入新节点
  11. void insertAfter(Node* prevNode, int newData)
  12. {
  13. if (prevNode == nullptr)
  14. {
  15. std::cout << "Previous node cannot be null." << std::endl;
  16. return;
  17.     }

  18.     Node* newNode = new Node(newData);
  19.     newNode->next = prevNode->next;
  20.     newNode->prev = prevNode;
  21. if
  22. (prevNode->next !=
  23. nullptr
  24. ) {
  25.         prevNode->next->prev = newNode;
  26.     }
  27.     prevNode->next = newNode;
  28. }

  29. // 打印链表
  30. void printList(Node* head)
  31. {
  32.     Node* current = head;
  33. while (current != nullptr)
  34. {
  35. std::cout << current->data << " ";
  36.         current = current->next;
  37.     }
  38. std::cout << std::endl;
  39. }

  40. int main()
  41. {
  42. // 创建一个双向链表: 1 <-> 2 <-> 3
  43.     Node* head = new Node(1);
  44.     Node* second = new Node(2);
  45.     Node* third = new Node(3);

  46.     head->next = second;
  47.     second->prev = head;
  48.     second->next = third;
  49.     third->prev = second;

  50. std::cout << "原始链表: ";
  51.     printList(head);

  52. // 在第二个节点后插入新节点
  53.     insertAfter(second, 4);

  54. std::cout << "插入后的链表: ";
  55.     printList(head);

  56. return 0;
  57. }
复制代码
大家都知道,海康并不是一个纯软件公司,所以能问上边这些问题都很正常。

正是秋招的好时候,大家一定要抓紧时间啊。有任何想了解或者迷茫的地方,可随时联系我,咱唠唠。







欢迎光临 谷动谷力 (http://bbs.sunsili.com/) Powered by Discuz! X3.2