嵌入式面试准备

自旋锁与互斥锁区别以及使用场景

自旋锁:一种非阻塞锁,当一个线程尝试获取自旋锁时,如果锁已经被其它线程持有,则该线程会一直忙等待(自旋),直到锁被释放。
互斥锁:一种阻塞锁,当一个线程尝试获取互斥锁时,如果锁已经被其它线程持有,则该线程会被阻塞,并进入等待队列,直到锁被释放。

两者区别
自旋锁属于忙等待,互斥锁属于阻塞等待。
自旋锁CPU占用高,互斥锁CPU占用低。
自旋锁上下文切换少,互斥锁上下文切换多。
自旋锁适用于临界区执行时间短,且竞争不激烈;互斥锁适合于临界区执行时间长,或竞争激烈。

使用场景

自旋锁

  • 临界区代码执行时间非常短,如果临界区代码执行时间很短,那么线程自旋等待的时间也很短,这样可以避免频繁的上下文切换,提高系统性能。
  • 多处理系统:在多处理系统中,当一个处理器上的线程获取失败时,可以立即尝试获取其它处理器的锁,这样可以提高并发性。

互斥锁

  • 临界区代码执行时间较长:如果临界区代码执行时间较长,那么线程自旋等待的时间也会很长,就会浪费大量的CPU资源。
  • 竞争激烈的场景:如果多个线程频繁竞争锁,那么使用自旋锁会导致大量的上下文切换,从而降低系统性能。
  • 等待时间不确定的场景:那么使用自旋锁可能会导致线程长时间占用CPU,从而影响其他线程的执行。

在这里插入图片描述
char型数组a的长度为10,则其在内存中所占的字节数为10,。

在这里插入图片描述
4、5
在这里插入图片描述
只能一开始定义char *c = “good”;

在这里插入图片描述
输出字符的时候以’\0’结束

在这里插入图片描述
插入排序的原理:始终定义第一个元素为有序的,然后将元素逐个插入到有序排列指针,其特点是要不断的移动数据,空出一个适当的位置,把它插入到里面去。

选择排序的原理是:每次在无序队列中选择出最小值或最大值,放到有序队列的最后,并从无需队列中去除该值。

在这里插入图片描述
在一个有8个int数据的数组中,随机给出数组的数据,找出最大和第二大元素一定需要进行9几次比较?

有一个100x90的稀疏矩阵,非零元素有10个,每个整型数占2字节,则用三元组表示该矩阵时,所需的字节数是。
将非零元素所在行、列、非零元素的值构成一个三元组(i,j,v)6字节,一共10个,60字节,再加上三个整数存储矩阵的行数、列数和总元素。3*2=6.

内联函数的使用场景与注意事项

内联函数是一种在编译时将函数调用替换为函数体本身的技术,目的是提高程序的执行效率。

什么情况下使用内联函数?

  • 函数体较小且频繁调用:当函数的代码量较小,并且在程序中被频繁调用时,使用内联函数就可以减少函数调用的开销,提高程序的执行效率。
  • 没有复杂的控制流:内联函数不适合包含复杂的控制流,因为这会增加编译后的代码量。
  • 函数不依赖于大量全局变量:如果函数依赖于大量全局变量,内联可能会导致代码的可读写降低,并且难以维护。

数组不是矩阵,矩阵也不是数组。

Makefile 忽略错误

如果我们在没有编译时执行make clean目标,会收到以下报错。

atguigu@ubuntu:~/helloworld$ make clean
rm main hello.o main.o
rm: 无法删除 'main': 没有那个文件或目录
rm: 无法删除 'hello.o': 没有那个文件或目录
rm: 无法删除 'main.o': 没有那个文件或目录
make: *** [Makefile:20:clean] 错误 1

make命令最终以返回码1退出了。

objects := hello.o\
		   main.o

main: $(objects)
	gcc -o main $(objects)

main.o:hello.h

hello.o:hello.h

.PHONY:clean

clean:
	-rm main $(objects)

rm前面的-告诉make,如果该命令执行失败,不要停止执行剩余的过程,即忽略错误。

atguigu@ubuntu:~/helloworld$ make clean
rm main hello.o main.o
rm: 无法删除 'main': 没有那个文件或目录
rm: 无法删除 'hello.o': 没有那个文件或目录
rm: 无法删除 'main.o': 没有那个文件或目录
make: [Makefile:20:clean] 错误 1 (已忽略)

C标志I/O库函数

#include <stdio.h>
int main(){
	char* filename = "io.txt";
	FILE* ioFile = fopen(filename, "a+");
	if (ioFile == NULL)
    {
        printf("FAILED,a+不能打开不存在的文件\n");
    }
    else
    {
        printf("SUCCESS,a+能打开不存在的文件\n");
    }
}
CC:=gcc
	fopen_test:fopen_test.c
	-$(CC) -o $@ $^
	-./$@
	-rm ./$@

裸机开发与基于操作系统开发的区别

  • 裸机开发:直接对硬件进行编程,不借助任何操作系统,程序员需要自己管理所有的硬件资源,包括内存、中断、定时器等。
  • 操作系统开发:基于操作系统内核,开发应用程序。操作系统提供了一套接口和服务,屏蔽了硬件的复杂性,开发者可以专注于业务逻辑。

可移植性

  • 裸机开发,可移植性较差,程序通常针对特定的硬件平台。
  • 操作系统开发:可移植性较好,基于操作系统的应用程序可以在不同的硬件平台上运行。

系统调用是操作系统内核提供给应用程序,使其可以间接访问硬件资源的接口。

open系统调用:
O_TRUNC:截断文件长度为0。

O_CREATE时,还要传入创建文件的权限,以八进制数字表示,以0开头。(其它用户没有写权限

如果打开文件失败,返回-1,同时设置全局变量errno表示对应的错误,否则返回对应的文件描述符。

long 是long int的缩写

exit和_exit

系统调用_exit(),用于终止一个进程,确保进程立即退出,不执行任何清理操作,_exit()在子进程终止时特别有用,这可以防止子进程的终止影响到父进程(比如,防止子进程以外刷新了父进程未写入的输出缓冲区)。

exit()函数由C标准库提供。
终止当前进程,但是在此之前会执行3种清理操作。

  1. 调用所有通过atexit()注册的终止处理函数(自定义)
  2. 刷新所有标准I/O缓冲区
  3. 关闭所有打开的标准I/O流

exit(status):父进程可接收到的退出状态码0表示成功,非0表示各种不同的错误。

使用场景

  1. 通常在父进程中使用exit(),以确保程序在退出前能执行清理操作,如关闭文件和刷新输出。
  2. 在子进程中,特别是在fork()之后立即调用了一个执行操作如exec,但执行失败时,推荐使用_exit()或_Exit()确保子进程的快速、干净地退出,避免执行标准的清理操作,与父进程发生冲突或不必要的重复。

每个文件描述符都关联到内核一个struct file类型的结构体数据。

struct file{
	f_count;//引用计数,管理文件对象的生命周期
	f_ops_lock;//保护文件位置的互斥锁
	f_pos; //当前文件读写位置
	f_path;//文件路径
	f_inode; //指向与文件相关联的inode对象的指针,该对象用于维护文件元数据,如文件类型、访问权限等
	f_op; //指向文件操作函数表的指针,例如读,写,锁定等。
};

在Linux操作系统中,所有文件都有唯一的inode。

在这里插入图片描述
文件描述符表在底层是通过数组来实现的,文件描述符实际上是这个数组的偏移量。

每个进程都有一个文件描述符表。

int fd = open("text.txt", O_RDONLY);//描述符表会增加一项

当我们执行open()等系统调用时,内核会创建一个新的struct file,这个数据结构记录文件的元数据(文件类型、权限等)、文件路径、支持的操作等,然后分配文件描述符,将struct file维护在文件描述符表中,最后将文件描述符返回给应用程序。

我们可以通过文件描述符对文件执行各种它所支持的函数操作,而这些函数的函数指针都维护在struct file_operations数据结构中。文件描述符实际上是底层数据结构struct file的引用或句柄,为用户提供了操作底层文件的入口。

什么是进程

进程是正在运行的程序,是操作系统进行资源分配的基本单位。
程序是存储在硬盘或内存的一段二进制序列,是静态的,而进程是动态的,进程包括代码、数据以及分配给它的其它系统资源(如文件描述符、网络连接等)。

使用标准库函数创建子进程

int result = system("ping -c 10 www.baidu.com");

在C99标准之前,main函数没有参数的形式被写为int main(),这在某些情况下可能导致与int main(void)行为不完全相同的问题,因为int main()在老式的C语言标准中不明确指出函数是否接受参数。从C99标准开始,推荐使用int main(void)明确指明main函数不接受任何参数,以提高代码的可读性和一致性。

pid实际上就是int类型的数据。

子进程会复制父进程的文件描述符fd,两者指向的是同一个底层文件描述(struct file)。
我们发现,子进程通过close()释放文件描述符之后,父进程对于相同的文件描述符执行write()操作仍然成功了。

因为struct file结构体中有一个属性为引用计数,记录的是 与当前struct file绑定的文件描述符数量。
close()系统调用的作用是将当前进程中的文件描述符和对应的struct file结构体解绑,使得引用计数减一。
如果close()执行之后,引用计数变为0,则会释放struct file相关的所有资源。

system()函数底层逻辑也是fork()+execve()

Linux中父进程除了可以启动子进程,还需要负责回收子进程。
如果子进程结束后,父进程没有正常回收,那么子进程就会变成一个僵尸进程——即程序执行完成,但是进程没有完全结束,其内核中PCB结构体没有释放。

父进程如果在子进程结束前就结束了,那么其子进程的回收工作就交给了父进程的父进程。

Linux的进程是通过父子关系组织起来的,所有进程之间的父子关系共同构成了进程树(Process Tree)。
一个进程的父进程只能有一个,但是 子进程不止一个。

用ps -ef | grep 进程号 可以过滤看自己想要的

执行ll /sbin/init
在这里插入图片描述
实质上,1号进程就是systemd,它由内核创建,是第一个进程,负责初始化系统,启动其它所有用户空间的服务和进程。它是所有进程的祖先。

在ps -ef的输出中,带有[]表示属于内核线程,内核线程在内核空间执行,不占用任何用户空间资源,它们在技术上是线程,而多方面表现得像独立的进程,因此也会被ps命令检索到,第一个内核线程的pid为2,是所有其它内核线程的祖先。

在这里插入图片描述
孤儿进程是指父进程已经结束,但它仍在运行的进程。

孤儿进程会被其祖先自动领养。
此时的子进程因为和终端切断了联系,所以很难适应标准输入让它停止。

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
嵌入式面试基础知识准备包括一些常见问题和答案,以及一些关于嵌入式软件开发的基础知识点。例如,关于Linux进程状态,常见的有3种状态:运行态、就绪态和等待态,而不是6种。在C语言方面,一些基础知识点包括volatile、const、static和指针等。volatile关键字通常用于多线程编程中,用于标识变量可能会被其他线程修改,需要使用该关键字来确保在编译器优化时不对这些变量进行优化。举几个需要使用volatile关键字的例子可以是多线程共享的变量或者与硬件相关的寄存器。除了这些基础知识点外,还可能涉及到其他嵌入式相关的内容,如嵌入式系统架构、设备驱动、嵌入式操作系统等。为了更好地准备嵌入式面试,建议学习这些基础知识点,并且多做一些实际的嵌入式开发项目,以增加实践经验。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [嵌入式软件面试基础知识点](https://blog.csdn.net/m0_56041246/article/details/121481340)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【嵌入式面试嵌入式知识点面经整理](https://blog.csdn.net/weixin_42112090/article/details/128686200)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

饼干饼干圆又圆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值