Linux驱动学习

linux面试_设备树面试问题-优快云博客

1 设备树

        在内核源码中,存在大量对板级细节信息描述的代码。这些代码充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目录,对内核而言这些platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data绝大多数纯属垃圾冗余代码。为了解决这一问题,ARM内核版本3.x之后引入了原先在Power PC等其他体系架构已经使用的Flattened Device Tree。

总结:在arm3.0之后引入了设备树的概念,设备树就是板级细节信息描述的代码,,开源文档:一种描述硬件资源的数据结构。 通过bootload讲硬件资源传输给内核,让内核和硬件资源相对独立

设备树结构:

                根节点、子节点和属性。根节点是整个设备树的入口点,它代表了整个硬件系统。子节点是根节点的子级节点,代表了系统中的各个设备和子系统。每个节点都可以包含多个属性,用于描述设备的配置和特性。

        设备树包含DTC(device tree compiler),DTS(设备树语法)(device tree source和DTB(device tree blob)。

  • DTS(device tree source)

DTS是一种ASCII文本格式的设备树描述,在ARM Linux中,一个dts文件对应一个ARM的设备,该文件一般放在arch/arm/boot/dts/目录中。

  • DTC(device tree compiler)

DTC是将.dts编译为.dtb的工具,相当于gcc。

  • DTB(device tree blob)

dtb文件是.dts 被 DTC 编译后的二进制格式的设备树文件,它由Linux内核解析,也可以被bootloader进行解析。

dts  是一种ASCII文件:

在内核中编程device_node (每个节点都会变成这个)

Dtb展开成Device_node时,内核还是不能直接使用,内核要将Device_node转换成

platform_device才可以开始使用

内存映射

mmap 是一个在 Linux 系统上用于内存映射的函数,它允许程序将一个文件或其他对象映射进内存空间,就像访问内存一样直接操作该对象。mmap 主要用于文件的输入/输出操作,以及共享内存。

操作GPIO管脚的控制多种种基本方式

 1 shell命令:

 echo "out" > /sys/class/gpio/gpio23/direction  # 设置为输出
# 或者
echo "in" > /sys/class/gpio/gpio23/direction   # 设置为输入

2 devmem2

3 系统调用

4 io命令(io -r -4 -寄存器地址)

bootloader

bootloader和uboot的区别?
bootloader是启动装载。这是一段很小的程序,用于在系统上电启动初期运行,
初始化关键接口,如内存,串口,关闭中断,关闭看门狗,引导系统进入内核
的一段初始化的程序。它主要任务就是将内核映像从硬盘读到RAM中,然后跳转
到内核的入口点去运行内核,从而建立系统运行的必要环境。
uboot:是bootloader的一种

编写驱动程序的一般方法

编写驱动程序的套路:

 ① 确定主设备号,也可以让内核分配

定义自己的 file_operations 结构体

 ③ 实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构 体

把 file_operations 结构体告诉内核:register_chrdev

谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这 个入口函数

有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用 unregister_chrdev

其他完善:提供设备信息,自动创建设备节点:class_create, device_create

⚫ 驱动怎么操作硬件?

◼ 通过 ioremap 映射寄存器的物理地址得到虚拟地址,读写虚拟地址。

⚫ 驱动怎么和 APP 传输数据?

◼ 通过 copy_to_user、copy_from_user 这 2 个函数。

IIC驱动框架

设备树中添加总线以及总线下的从设备

I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C设备器

I2C总线:维护驱着两个链表(I2C动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等
I2C驱动:对应的就是I2C设备的驱动程序
I2C设备:是具体硬件设备的一个抽象
I2C设配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象

  • 1、注册I2C驱动
  • 2、将I2C驱动添加到I2C总线的驱动链表中
  • 3、遍历I2C总线上的设备链表,根据i2c_device_match函数进行匹配,如果匹配调用i2c_device_probe函数
  • 4、i2c_device_probe函数会调用I2C驱动probe函数

SPI驱动框架

控制器驱动程序层叫 spi_master ,主要提供transfer函数,进行spi协议的收发。spi_master 也是基于 Platform 模型的,注册 spi_master 时也会扫描一个链表进行注册设备

另一层是设备驱动层,基于 spi_bus_type,相比 i2c 在device中需要提供的信息多一些,需要有名字、片选、最大速率、模式、中断号等等,在driver里则使用spi_read、spi_writer 等函数,最终也会调用到 master->transfer 函数进行发送接收。

 相比 I2C ,SPI驱动的框架是要简单的,因为它少了两种注册device的方式,另外它不需要像I2C一样去发送Start信号和设备地址去探测设备,SPI只需要片选选中就行了。但是它的底层收发的控制,相对I2C要复杂一点,毕竟4根线。

网络编程

在linux网络编程中给TCP和udp是传输层的主要协议

TCP:

TCP是一种面向连接的协议,它通过三次握手建立连接,然后在连接上进行可靠的数据传输。TCP使用序列号和确认应答(ACK)来保证数据的可靠传输,通过滑动窗口和拥塞控制算法进行流量控制和拥塞控制。

UDP的原理


相比于TCP,UDP是一种更简单的协议。UDP是无连接的,它直接在IP协议之上发送数据报,不提供数据的可靠传输、流量控制或拥塞控制。因此,UDP的延迟和开销较小,适用于对实时性要求高的应用,如语音和视频通信。

使用的是Socket通信进行上述网络通信的

TCP通信详解


在TCP通信中,我们首先需要建立一个TCP连接,然后才能在这个连接上进行数据传输。以下是

TCP通信的详细步骤和时序图:

  1. 服务器执行socket()函数,创建一个新的套接字。

  2. 服务器执行bind()函数,将套接字绑定到一个指定的地址(包括IP地址和端口号)。

  3. 服务器执行listen()函数,使套接字进入监听模式,等待客户端的连接请求。

  4. 服务器执行accept()函数,阻塞并等待客户端的连接请求。当一个客户端连接请求到来时,accept()函数返回,并创建一个新的套接字与客户端进行通信。

  5. 客户端执行socket()和connect()函数,向服务器发起连接请求。connect()函数会发送一个SYN(同步)数据包到服务器。

  6. 服务器收到SYN数据包,在accept()函数返回后,回复一个SYN+ACK(确认应答)数据包给客户端。

  7. 客户端收到SYN+ACK数据包,回复一个ACK数据包给服务器,完成TCP连接的建立。

  8. TCP连接建立后,客户端和服务器可以通过read()和write()函数进行数据传输。

与TCP不同,UDP是一种无连接的协议,客户端和服务器不需要建立连接就可以直接发送数据。以下是UDP通信的详细步骤:

  1. 服务器执行socket()函数,创建一个新的套接字。

  2. 服务器执行bind()函数,将套接字绑定到一个指定的地址(包括IP地址和端口号)。

  3. 客户端执行socket()函数,创建一个新的套接字。

  4. 客户端可以直接通过sendto()函数发送数据到服务器。

  5. 服务器通过recvfrom()函数接收客户端发送的数据。

多线程编程

多线程编程是一种利用操作系统的多任务处理机制,以实现程序并发执行的编程模型。

在Linux中,线程是通过pthread库来实现的。线程的创建和管理都是通过pthread库提供的函数完成的。以下是一个简单的线程创建示例:

【Linux】多线程详解,一篇文章彻底搞懂多线程中各个难点!!!_linux多线程-优快云博客

linux内核中是没有线程这个概念的,而是轻量级进程的概念:LWP。一般我们所说的线程概念是C库当中的概念。

1.1线程是怎样描述的?
线程实际上也是一个task_struct,工作线程拷贝主线程的task_struct,然后共用主线程的mm_struct。线程ID是在用task_struct中pid描述的,而task_struct中tgid是线程组ID,表示线程属于该线程组,对于主线程而言,其pid和tgid是相同的,我们一般看到的进程ID就是tgid。

1.2如何查看一个线程的ID

命令:ps -eLf

每一个线程,默认在共享区中占有的空间为8M

1.4.1线程带来的优势
线程会共享内存地址空间。
创建线程花费的时间要少于创建进程花费的时间。
终止线程花费的时间要少于终止进程花费的时间。
线程之间上下文切换的开销, 要小于进程之间的上下文切换。
线程之间数据的共享比进程之间的共享要简单。
充分利用多处理器的可并行数量。(线程会提高运行效率,但当线程多到一定程度后,可能会导致效率下降,因为会有线程调度切换。)

1.4.2线程带来的缺点
  1. 健壮性降低:多个线程之中, 只要有一个线程不够健壮存在bug(如访问了非法地址引发的段错误) , 就会导致进程内的所有线程一起完蛋。
  2. 线程模型作为一种并发的编程模型, 效率并没有想象的那么高, 会出现复杂度高、 易出错、 难以测试和定位的问题。

文件IO操作

解决进程之间并发问题

多核处理器下,会存在多个进程处于内核态的情况,而在内核态下,进程是可以访问所有内核数据的,因此要对共享数据进行保护,即互斥处理;

linux内核锁机制有信号量、互斥锁、自旋锁还有原子操作。

2 原子操作:原子操作是在多线程环境中保证数据一致性和同步的关键技术

原子操作只有2种状态,一种是没做,一种是做完了,看不到正在做的状态

一、信号量(struct semaphore):信号量本质上是一个计数器

是用来解决进程/线程之间的同步和互斥问题的一种通信机制,是用来保证两个或多个关键代码不被并发调用。

也就是说信号量通过PV操作同步解决了进程/线程对临界资源利用的冲突问题;

互斥锁 :

进入临界区,想访问全局变量的时候,要先申请锁,才有资格访问。使用完后释放,如果申请不到,就会导致进程休眠

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值