Linux面试高频(Linux基础)
Linux基础
1 常见Linux指令⭐⭐⭐
Linux是一个开源的操作系统,其命令行界面(CLI)是用户与系统交互的主要方式。以下是一些常见的Linux指令及其介绍:
- cd:切换当前目录或工作目录。例如,cd /home/user表示进入/home/user目录。
- ls:列出当前目录或文件的列表。
- cp:复制一个或多个文件到指定的位置或将指定的内容写入目标位置的文件中。
- mv:移动或重命名一个或多个文件。
- rm:删除一个或多个文件。
- mkdir:创建一个新的目录。
- rmdir:删除一个空目录。
- pwd:显示当前工作目录的路径。
- ping:测试网络连接是否正常。
- top:显示系统的运行情况,包括进程和系统资源的使用情况。
- tar:打包、解包和压缩文件。
- grep:在文件中查找匹配的文本。
- find:在指定的目录下查找文件。
- chmod:修改文件或目录的权限。
- chown:更改文件或目录的所有者。
以上仅是Linux系统中的一部分常用命令,还有许多其他的命令可以用来管理文件系统、网络连接、进程等。学习Linux命令需要一定的时间和实践,但掌握它们可以大大提高工作效率。
2 常见GCC指令⭐⭐⭐
在Linux中,GCC(GNU Compiler Collection)是一个开源的编译器集合,包括了C、C++、Objective-C、Fortran等语言的编译器。以下是一些常用的GCC编译器命令和编译流程:
- 安装GCC:在大多数Linux发行版中,GCC已经预装了。如果没有预装,可以通过包管理器或从官方网站下载并安装。
- 编译一个C程序:使用gcc编译器可以编译C程序。例如,要编译一个名为“hello.c”的C程序,可以使用以下命令:
gcc hello.c -o hello
这将生成一个名为“hello”的可执行文件。
- 编译一个C++程序:使用g++编译器可以编译C++程序。例如,要编译一个名为“hello.cpp”的C++程序,可以使用以下命令:
g++ hello.cpp -o hello
这将生成一个名为“hello”的可执行文件。
- 编译一个Objective-C程序:使用g++编译器可以编译Objective-C程序。例如,要编译一个名为“hello.m”的Objective-C程序,可以使用以下命令:
g++ -fobjc-arc -framework Foundation hello.m -o hello
这将生成一个名为“hello”的可执行文件。
- 编译一个Fortran程序:使用gfortran编译器可以编译Fortran程序。例如,要编译一个名为“hello.f90”的Fortran程序,可以使用以下命令:
gfortran hello.f90 -o hello
这将生成一个名为“hello”的可执行文件。
- 编译一个Makefile项目:Makefile是一种用于自动化构建项目的脚本语言。要编译一个Makefile项目,需要先编写一个Makefile文件,然后使用make命令进行编译。例如,要编译一个名为“myproject”的项目,可以使用以下命令:
make
这将自动编译所有的源文件并生成可执行文件。
以上是一些常用的GCC编译器命令和编译流程,但还有很多其他命令和选项可以使用。在使用GCC时,应该仔细阅读文档以了解所有可用的选项和参数。
3 常见GDB调试指令⭐⭐⭐
GDB(GNU Debugger)是一个开源的调试器,它可以用来调试C、C++和Fortran等编程语言程序。以下是一些常见的GDB调试指令及其介绍:
- break point:在指定位置设置断点,使程序停止在此处。例如,break file.c:50表示在file.c文件的第50行设置一个断点。
- continue:跳过当前正在执行的代码,继续执行下一条指令。例如,continue命令可以在程序中跳过一段代码并继续执行后续代码。
- step into:进入函数内部,查看函数的参数和局部变量的值。例如,step into命令可以进入名为my_function的函数内部。
- step over:跳过函数调用和表达式,只查看函数或表达式的值。例如,step over命令可以跳过my_function函数调用和表达式。
- print:打印变量的值。例如,print variable可以打印变量的值。
- info registers:显示当前寄存器的值。例如,info registers可以显示当前寄存器的值。
- list:列出当前源代码文件的所有行号。例如,list命令可以列出当前源代码文件的所有行号。
- next:逐行执行程序,直到遇到下一个断点。例如,next命令可以逐行执行程序,直到遇到下一个断点。
- finish:结束调试会话。例如,finish命令可以结束调试会话。
以上仅是GDB中的一部分常用指令,还有许多其他的指令可以用来查看内存状态、单步执行程序、修改变量值等。学习GDB指令需要一定的时间和实践,但掌握它们可以大大提高程序员的调试效率。
4 常见Linux驱动开发指令⭐⭐⭐⭐
在Linux中,可以使用Shell脚本来加载和卸载驱动程序。以下是一些常用的指令:
- 加载驱动程序:使用modprobe命令可以加载模块文件并将其注册到内核中。例如,要加载名为"driver.ko"的驱动程序,可以使用以下命令:
sudo modprobe driver.ko
- 卸载驱动程序:使用rm命令可以删除已经加载的模块文件。例如,要删除名为"driver.ko"的驱动程序,可以使用以下命令:
sudo rm driver.ko
需要注意的是,加载和卸载驱动程序需要具有足够的权限才能执行。通常情况下,只有root用户或者拥有相关权限的用户才能够执行这些指令。同时,为了避免误删或覆盖其他重要的文件,建议在使用这些指令时谨慎操作。
5 常见Linux文件操作命令⭐⭐⭐
以下是一些常见的Linux文件操作命令:
- ls:列出当前目录下的文件和子目录。
- cd:切换到指定的目录。
- pwd:显示当前工作目录的路径。
- mkdir:创建一个新的目录。
- rmdir:删除一个空目录。
- cp:复制文件或目录。
- mv:移动或重命名文件或目录。
- rm:删除文件或目录。
- touch:创建一个新文件或更新现有文件的时间戳。
- cat:将文件内容输出到终端。
- less:逐页查看文件内容。
- head:显示文件的前几行。
- tail:显示文件的后几行。
- grep:在文件中查找匹配的字符串。
- find:在指定目录及其子目录中查找文件。
- chmod:更改文件或目录的权限。
17.chown:更改文件或目录的所有者。
- tar:打包或解压文件或目录。
- zip:压缩或解压文件或目录。
- gzip:压缩文件或目录。
- gunzip:解压缩gzip格式的文件。
- unzip:解压缩zip格式的文件。
这些命令只是Linux文件操作的一部分,还有很多其他命令可以用于不同的文件操作任务。
6 Linux权限管理命令⭐⭐⭐
Linux权限管理是Linux系统中非常重要的一部分,它用于控制用户和组对文件、目录和其他资源的访问权限。在Linux中,每个文件和目录都有一个所有者(owner)、一个所属组(group)和一个其他人(others)三部分组成,其中所有者和所属组决定了文件或目录的访问权限,而其他人则决定了其他用户对该文件或目录的访问权限。
以下是一些常用的Linux权限管理命令:
- chmod:更改文件或目录的权限。可以使用数字模式来指定权限,例如755表示所有者有读写执行权限,所属组有读写执行权限,其他人只有读权限。
- chown:更改文件或目录的所有者。
- chgrp:更改文件或目录的所属组。
- su:切换用户身份。
- sudo:以超级用户身份运行命令。
- pwd:显示当前工作目录的路径。
- whoami:显示当前登录用户的用户名。
- groups:列出当前用户所属的所有组。
- id:显示当前用户的UID和GID。
- usermod:修改用户的属性。
- groupmod:修改用户的组属性。
- adduser:创建新用户。
- deluser:删除用户。
- passwd:修改用户的密码。
- userdel:删除用户。
这些命令只是Linux权限管理的一部分,还有很多其他命令可以用于不同的权限管理任务。
7 Linux磁盘管理及命令⭐⭐⭐⭐
Linux磁盘管理是Linux系统中非常重要的一部分,它用于管理硬盘、分区和文件系统等。在Linux中,可以使用命令行工具来管理磁盘和文件系统,以下是一些常用的Linux磁盘管理命令:
- fdisk:创建、删除、格式化和挂载分区表。
- mkfs:创建、删除、格式化和挂载文件系统。
- lsblk:列出所有可用的块设备。
- blkid:显示每个块设备的UUID。
- mount:将文件系统挂载到指定目录。
- umount:卸载已挂载的文件系统。
- fsck:检查和修复文件系统错误。
- resize2fs:调整文件系统的块大小。
- btrfs-monitor:监视和报告Btrfs文件系统的健康状况。
- ddrescue:备份和恢复损坏的分区。
这些命令只是Linux磁盘管理的一部分,还有很多其他命令可以用于不同的磁盘管理任务。
8 linux下检查内存状态的命令⭐⭐⭐⭐
- free命令:用于查看系统内存的使用情况,包括总内存、已用内存、可用内存等。示例命令:free -h
- top命令:除了显示系统的整体资源使用情况外,还可以查看每个进程的内存使用情况。示例命令:top
- vmstat命令:显示系统的虚拟内存统计信息,包括内存使用情况、交换空间使用情况等。示例命令:vmstat
- /proc/meminfo文件:该文件提供了系统内存的详细信息,可以使用cat或者less命令来查看。示例命令:cat /proc/meminfo
- pmap命令:用于显示进程内存映射的详细信息,包括内存地址、权限、映射的文件等。示例命令:pmap <进程ID>
9 什么是Uboot?⭐⭐⭐⭐⭐
Bootloader(引导加载程序)是一种在操作系统启动前运行的小型程序,负责初始化硬件并加载操作系统内核到内存中。它通常存储在设备的固件(如BIOS或UEFI)中,是系统启动的关键步骤。
Uboot启动过程中做了哪些事:
1、汇编阶段
切换到svc模式;
为了保证启动稳定,关闭看门狗、中断、MMU、cache;
初始化硬件,要做的操作有初始化时钟,串口,内存等硬件;
时钟很多硬件都要用到,所以先初始化;
uboot搬到内存中;
准备好C语言要使用的栈空间;
2、C代码阶段
大部分硬件的初始化;
把内核搬到内存运行,搬到内存运行会快很多。
加载内核;
10 文件系统⭐⭐⭐
什么是根文件系统?
根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所挂载(mount)的第一个文件系统,内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。我们要明白文件系统和内核是完全独立的两个部分。在嵌入式中移植的内核下载到开发板上,是没有办法真正的启动Linux操作系统的,会出现无法加载文件系统的错误。
根文件系统为什么那么重要?
根文件系统之所以在前面加一个”根“,说明它是加载其它文件系统的”根“,那么如果没有这个根,其它的文件系统也就没有办法进行加载的。
根文件系统包含系统启动时所必须的目录和关键性的文件,以及使其他文件系统得以挂载(mount)所必要的文件。例如:
init进程的应用程序必须运行在根文件系统上;
根文件系统提供了根目录“/”;
linux挂载分区时所依赖的信息存放于根文件系统/etc/fstab这个文件中;
shell命令程序必须运行在根文件系统上,譬如ls、cd等命令;
总之:一套linux体系,只有内核本身是不能工作的,必须要rootfs(上的etc目录下的配置文件、/bin /sbin等目录下的shell命令,还有/lib目录下的库文件等···)相配合才能工作。
Linux启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。成功之后可以自动或手动挂载其他的文件系统。因此,一个系统中可以同时存在不同的文件系统。在 Linux 中将一个文件系统与一个存储设备关联起来的过程称为挂载(mount)。使用 mount 命令将一个文件系统附着到当前文件系统层次结构中(根)。在执行挂装时,要提供文件系统类型、文件系统和一个挂装点。根文件系统被挂载到根目录下“/”上后,在根目录下就有根文件系统的各个目录,文件:/bin /sbin /mnt等,再将其他分区挂接到/mnt目录上,/mnt目录下就有这个分区的各个目录和文件。
11 文件系统的常用目录⭐⭐⭐
Linux文件系统中一般有如下几个目录:
/bin目录
该目录下存放所有用户都可以使用的、基本的命令,这些命令在挂接其它文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中。
/bin目录下常用的命令有:cat,chgrp,chmod,cp,ls,sh,kill,mount,umount,mkdir,mknod,test等,我们在利用Busybox制作根文件系统时,在生成的bin目录下,可以看到一些可执行的文件,也就是可用的一些命令。
/sbin 目录
该目录下存放系统命令,即只有管理员能够使用的命令,系统命令还可以存放在/usr/sbin,/usr/local/sbin目录下,/sbin目录中存放的是基本的系统命令,它们用于启动系统,修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin,所以/sbin目录必须和根文件系统在同一个分区中。
/sbin目录下常用的命令有:shutdown,reboot,fdisk,fsck等,本地用户自己安装的系统命令放在/usr/local/sbin目录下。
/dev目录
该目录下存放的是设备文件,设备文件是Linux中特有的文件类型,在Linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件。比如通过"dev/ttySAC0"文件可以操作串口0,通过"/dev/mtdblock1"可以访问MTD设备的第2个分区。
/etc目录
该目录下存放着各种配置文件,对于PC上的Linux系统,/etc目录下的文件和目录非常多,这些目录文件是可选的,它们依赖于系统中所拥有的应用程序,依赖于这些程序是否需要配置文件。在嵌入式系统中,这些内容可以大为精减。
/lib目录
该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。运行根文件系统中的可执行程序,比如:/bin /sbin 目录下的程序。
/home目录
用户目录,它是可选的,对于每个普通用户,在/home目录下都有一个以用户名命名的子目录,里面存放用户相关的配置文件。
/root目录
根用户的目录,与此对应,普通用户的目录是/home下的某个子目录。
/usr目录
/usr目录的内容可以存在另一个分区中,在系统启动后再挂接到根文件系统中的/usr目录下。里面存放的是共享、只读的程序和数据,这表明/usr目录下的内容可以在多个主机间共享,这些主要也符合FHS标准的。/usr中的文件应该是只读的,其他主机相关的,可变的文件应该保存在其他目录下,比如/var。/usr目录在嵌入式中可以精减。
/var目录
与/usr目录相反,/var目录中存放可变的数据,比如spool目录(mail,news),log文件,临时文件。
/proc目录
这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,它没有实际的存储设备,里面的目录,文件都是由内核临时生成的,用来表示系统的运行状态,也可以操作其中的文件控制系统。
/mnt目录
用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一引起空的子目录,比如/mnt/cdram /mnt/hda1 。用来临时挂载光盘、硬盘。
/tmp目录
用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以/tmp目录必须存在并可以访问。
12 中断⭐⭐⭐⭐⭐
硬中断、软中断是什么?有什么区别
硬中断
由与系统相连的外设(比如网卡、硬盘)自动产生的,主要是用来通知操作系统系统外设状态的变化。
可屏蔽中断 ——当 CPU 接收到更高优先级的中断时,这些中断可以被延迟。
不可屏蔽中断 ——无法延迟这些中断。 CPU 应该立即考虑它们。
软中断
为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成。
区别
- 引发对象:硬中断是由外设引发的,软中断是执行中断指令产生的,无需外部施加中断请求信号。
- 提供中断号:硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。
- 耗时:硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。
硬中断(Hardware Interrupts)是由外部设备发起的,通常用于表示设备需要CPU的注意,例如I/O操作完成、定时器中断等。硬中断通常是可屏蔽的,这意味着在处理某个硬中断过程中,CPU可以禁止响应其他硬中断,以防止嵌套中断带来的问题。当然,也有一些特定情况下的硬中断是不可屏蔽的,例如非屏蔽中断(Non-Maskable Interrupt, NMI)。
软中断(Software Interrupts)则是由软件主动触发的,通常用于实现系统调用、异常处理等。软中断的可屏蔽性取决于具体实现和系统需求。在某些情况下,软中断可能是不可屏蔽的,以确保某些关键任务或异常处理能够得到优先执行。然而,在其他情况下,软中断可能是可以屏蔽的,以避免嵌套中断或提高系统性能。
13 中断上半部和下半部理解⭐⭐⭐⭐⭐
设备的中断会打断内核中进程的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽可能地短小精悍。但是,这个良好的愿望往往与现实并不吻合。在大多数真实的系统中,当中断到来时,要完成的工作往往并不会是短小的,它可能要进行较大量的耗时处理。
如上图描述了Linux内核的中断处理机制
为了在中断执行时间尽可能短和中断处理需完成大量工作之间找到一个平衡点,Linux将中断处理程序分解为两个半部:顶半部(top half)和底半部(bottom half)。
顶半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态并清除中断标志后就进行“登记中断”的工作。“登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。这样,顶半部执行的速度就会很快,可以服务更多的中断请求。
现在,中断处理工作的重心就落在了底半部的头上,它来完成中断事件的绝大多数任务。底半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断,这也是底半部和顶半部的最大不同,因为顶半部往往被设计成不可中断。底半部则相对来说并不是非常紧急的,而且相对比较耗时,不在硬件中断服务程序中执行。
尽管顶半部、底半部的结合能够改善系统的响应能力,但是,僵化地认为Linux设备驱动中的中断处理一定要分两个半部则是不对的。如果中断要处理的工作本身很少,则完全可以直接在顶半部全部完成。
其实上面这一段大致说明一个问题,那就是:中断要尽可能耗时比较短,尽快恢复系统正常调试,所以把中断触发、中断执行分开,也就是所说的“上半部分(中断触发)、底半部(中断执行)”,其实就是我们后面说的中断上下文。下半部分一般有tasklet、工作队列实现,触摸屏中中断实现以工作队列形式实现的。
中断下半部的处理
对于一个中断,如何划分出上下两部分呢?哪些处理放在上半步,哪些放在下半部?
这里有一些经验可供借鉴:
如果一个任务对时间十分敏感,将其放在上半部。
如果一个任务和硬件有关,将其放在上半部。
如果一个任务要保证不被其他中断打断,将其放在上半部。
其他所有任务,考虑放在下半部。
实现下半部的三种中断机制
1.软中断
软中断是一组静态定义的下半部接口,有 32 个,可以在所有处理器上同时执行,类型相同也可以;在编译时静态注册。
2.tasklet
第二种机制是基于软中断实现的,灵活性强,动态创建的下半部实现机制。两个不同类型的 tasklet 可以在不同处理器上运行,但相同的不可以,可以通过代码动态注册。
在 SMP 上,调用 tasklet 是会检测 TASKLET_STATE_SCHED 标志,如果同类型在运行,就退出函数。
tasklet 由于是基于软中断实现的,所以也允许响应中断。但不能睡眠(我认为不能睡眠原因是它们内部有 spin lock)。
3.工作队列
工作队列(work queue)是另外一种将中断的部分工作推后的一种方式,它可以实现一些tasklet不能实现的工作,比如工作队列机制可以睡眠。这种差异的本质原因是,在工作队列机制中,将推后的工作交给一个称之为工作者线程(worker thread)的内核线程去完成(单核下一般会交给默认的线程events/0)。因此,在该机制中,当内核在执行中断的剩余工作时就处在进程上下文(process context)中。也就是说由工作队列所执行的中断代码会表现出进程的一些特性,最典型的就是可以重新调度甚至睡眠。
对于tasklet机制(中断处理程序也是如此),内核在执行时处于中断上下文(interrupt context)中。而中断上下文与进程毫无瓜葛,所以在中断上下文中就不能睡眠。因此,选择tasklet还是工作队列来完成下半部分应该不难选择。当推后的那部分中断程序需要睡眠时,工作队列毫无疑问是你的最佳选择;否则,还是用tasklet吧。
14 ioctl和unlock_ioctl有什么区别?⭐⭐⭐
ioctl和unlock_ioctl是Linux内核中的两个函数。
ioctl: 这是一个系统调用接口,用于在用户空间和内核空间之间进行通信。它允许用户程序通过设备文件发送命令和参数给设备驱动程序,并执行相应的操作。设备驱动程序可以根据收到的命令来执行不同的操作。
unlocked_ioc
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
该专栏面向嵌入式开发工程师、C++开发工程师,包括C语言、C++,操作系统,ARM架构、RTOS、Linux基础、Linux驱动、Linux系统移植、计算机网络、数据结构与算法、数电基础、模电基础、5篇面试题目、HR面试常见问题汇总和嵌入式面试简历模板等文章。超全的嵌入式软件工程师面试题目和高频知识点总结! 另外,专栏分为两个部分,大家可以各取所好,为了有更好的阅读体验,后面会持续更新!!!