韦东山:6000字长文告诉你如何学习嵌入式linux

我在100ASK_IMX6ULL售后群里,发现不少初学者只有单片机基础,甚至没有单片机基础。在学习Linux时,对不少概念比较陌生,致使不知道学什么,也不知道学了以后有什么用。因此我趁着五一假期,编写此文。linux

从事嵌入式Linux培训12年来,咱们写过不少《关于如何学习linux》的文章,这是最新的,本文将不断更新。web

第1章 单片机和Linux的区别

1.1 有哪些产品使用单片机或Linux

全部的电子产品,所用技术均可以认为要么是单片机,要么是Linux;GUI方面主要是QT/Android,它们都是运行于Linux之上的。shell

也许你不服!不是还有ucos、vxwork、wince、IOS吗?下面这个图是关于操做系统的占比,是2016年的,我没找到更新的图,可是颇有参考意义:编程

咱们说的单片机不使用操做系统,在上图中没有体现出来。可是使用单片机设备确定远远超过Linux。不少人也是先学习单片机,从单片机进入电子工程师行业网络

平常生活中,有哪些产品使用单片机、Linux呢?下面举一些例子:架构

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

咱们设计一个产品时,是使用单片机仍是Linux,取决于成本:硬件成本、软件成本、维护成本、升级成本。而不该该根据我的偏好来选择:我喜欢单片机,因此就排斥使用Linux;我喜欢Linux,就排斥使用单片机。为了有更多的选择,咱们须要既懂单片机,又懂Linux。框架

1.2 在硬件操做上单片机和Linux是相似的

以点灯为例,
不管是单片机仍是Linux,咱们要作的事情都同样:异步

① 看原理图,肯定引脚是哪个,肯定它输出什么电平才能够svg

② 看芯片手册,肯定要怎么操做寄存器函数

③ 写程序

可是,怎么编写程序,单片机和Linux有很大不一样。

1.3 在单片机中点灯、使用LCD

使用单片机开发程序时,咱们一上来就写一个main函数,下面是一些简化的代码:
在这里插入图片描述

LED程序里面的init_led、led_on、led_off函数是你一我的写的,爱取什么名就取什么名,爱怎么写就怎么写。

LCD程序里的函数也是你写的,彻底是自由发挥。

不少单片机项目不是很复杂,二、3我的从上到下通通搞定,里面的函数大多时间是直接去读写寄存器。

不少单片机项目严重依赖于硬件,换一个芯片后怎么办?重写一套代码呗。

在单片机程序里,没有应用程序、驱动程序的概念,极可能一我的包揽了硬件设计、模块调试(或称之为驱动)、功能开发(或称之为应用)的所有活。

1.4 在Linux中点灯、使用LCD

在Linux中,不容许应用开发人员直接去操做硬件,好比你想点个灯,很差意思,你没法直接访问寄存器;你须要经过驱动程序来访问寄存器。

为何?有几大缘由:

① Linux系统中运行着众多程序,必须保证质量差的程序没法破坏系统:

假设你写的程序比较烂,那我不能让你去随便访问寄存器,把系统搞崩溃了怎么办?你本意是去点灯,可是你看错了寄存器,你把电源关了怎么办?

因此这些操做硬件的活,仍是交给信得过的人来作吧:交给驱动工程师,他既懂硬件又懂软件。

② 保证程序的可移植性:

编写应用程序时,你们都使用统一的函数,之后换一个芯片时,应用程序不用变;只须要根据这个接口提供驱动程序就能够了。

③ 团队协做:

使用Linux系统的项目通常比较大,术业有专攻,一我的不太可能从上到下都所有掌握。好比作人脸识别项目,有擅长作图像处理的,他可无论你要用多少种摄像头,有图像给他就能够。而多种摄像头的硬件操做方法各有不一样,这些交给驱动程序工程师。

因此,在Linux中应用程序和驱动程序是分开的。

以LED、LCD程序为例,简化的代码以下:

在这里插入图片描述

也许你已经大概猜出来了,应用程序怎么调用驱动程序?经过标准的接口:

①open:

打开驱动程序。

② read/write:

读、写数据。

③ ioctl:

传入各类参数,得到各类参数。

④ mmap:

内存映射,好比映射以后,应用程序能够直接读写LCD的显存。

你看!从这些接口里,咱们根本看不到寄存器的操做。底层的程序驱动会根据这些调用,去设置寄存器、操做硬件。

因此,我高大上的应用工程师,干吗苦哈哈地去看原理图、看在片手册、读写寄存器,搞很差还要去调试硬件BUG。这些脏活、累活就交给驱动工程师吧。客户的需求变幻无穷,我996时间都不够用了。

切,我上懂软件、下懂硬件的驱动工程师,确定不能把这么重要的活交给你去作了,把个人系统搞崩溃了怎么办。

开玩笑、开玩笑、开玩笑的,有应用工程师、驱动工程师的优劣之分,你们都是为了作出产品。如今有一个趋势,一个任务从上到下你都须要懂,这就是所谓的全栈工程师。

仍是以LED为例,应用程序和驱动程序的协做以下图所示:

在Linux中,“一切皆文件”,要访问某个硬件,也是要打开文件、读写文件。应用程序要根据标准的文件接口:open/read/write/ioctl/mmap等来访问驱动程序。

既然如此,怎么写驱动程序呢?最简单的方法就是:APP要调用open来打开驱动程序,那驱动‘程序里就提供一个xxx_open函数来初始化硬件;APP要调用write来写数据,驱动程序里就提供一个xxx_write函数来接收数据并操做硬件。

用xxx_open、xxx_write来构成一个驱动程序,这就是驱动框架。

怎么实现这些xxx_open、xxx_write函数?咱们要作的事情跟单片机是相似的,同样要去看电路图、看芯片手册,而后在这些函数里读写寄存器:这称为硬件操做

因此,Linux驱动程序= 驱动框架 + 硬件操做。

有单片机基础的人,对硬件操做比较熟悉了,把重点放在驱动框架上就能够。

高能预警:驱动框架可不简单,对于LED来讲是简单,可是还有更复杂的驱动程序,它要考虑“通用”,这很要命。

第2章 嵌入式Linux快速入门

这几天在群里跟学员聊天,有一位学员的学习方法很好:先观其广,再究其深。有时候要“不求甚解”,不少时候保持疑问先学下去,这些疑问就天然解决了。

好比课程中涉及汇编知识,若是你要完全弄清楚,你须要去学习《ARM架构与编程》;当你学完这本书,你的同窗搞很差已经能够上手工做了。

2.1 短时间的目标是什么

咱们先把学习目标定下来:快速了解嵌入式Linux开发的流程,知道要学什么,具有跟从业者交流的能力。

2.2 一个嵌入式Linux系统的组成

在这里插入图片描述
在这里插入图片描述

下面咱们用类比和逻辑推导出嵌入式Linux系统的组成,没错,“推导”。

从上图能够知道:

① 组成:

嵌入式Linux系统

= bootloader + linux内核 + 根文件系统(里面含有APP)。

② bootloader:

它的目的是启动内核,去哪等读内核?读到哪里?去Flash等外设读内核,存到内存里去。因此须要有Flash里外设的驱动能力,为了调试方便还会有网络功能。

因此,能够认为 booloader = 裸机集合,它就是一个复杂的单片机程序。

③ Linux内核

Linux内核的最主要目的是去启动APP,APP保存在哪里?保存在“根文件系统”里。“根文件系统”又保存在哪里?在Flash、SD卡等设备里,甚至可能在网络上。因此Linux内核要有这些Flash、SD卡里设备的驱动能力。

不只如此,Linux内核还有进程调度能力、内存管理等功能。

因此:Linux内核 = 驱动集合 + 进程调度 + 内存管理等。

2.3 要学习bootloader吗

Bootloader有不少种,经常使用的叫做u-boot。

在实际工做中,对于u-boot基本上是修修改改,甚至不改。可是u-boot自己是很复杂的,好比为了便于调试,它支持网络功能;有些内核是保存在FAT32分区里,因而它要能解析FAT32分区,读FAT32分区的文件。

花那么多精力去学习u-boot,可是工做中基本用不到,这对初学者很不友善。

因此,对于初学者,我建议:理解u-boot的做用、会使用u-boot的命令,这就能够了

若是你的工做就是修改、完善bootloader,那么再去研究它吧。

2.4 要学习Linux内核、要学习驱动程序吗

以前咱们说过Linux内核 = 驱动集合 + 进程调度 + 内存管理等,若是要学习Linux内核,从驱动程序入手是一个好办法。

可是人人都要学习Linux内核、人人都要学习Linux驱动吗?显然不是。

做为初学者,懂几个简单的驱动程序,有利于工做交流;理解中断、进程、线程的概念,不管是对驱动开发、应用程序开发,都是颇有好处的。

因此对于初学者,建议前期只学习这几个驱动:LED、按键、中断。

① LED驱动程序:

这是最简单的驱动程序。

② 按键驱动程序:

它也比较简单,从它引入“中断”

③ 中断:

从“中断”它能够引入:休眠-唤醒、进程/线程、POLL机制、异步通知等概念。这些概念不管是对驱动开发,仍是对应用开发,都很重要。

因此,对于初学者,我建议必须学习这几个驱动:LED、按键、中断。

入门以后,若是你想从事内核开发、驱动开发,那么能够去钻研几个驱动程序(输入系统、I2C总线、SPI总线等),掌握若干个大型驱动程序后,你对内核的套路就有所了解了,再去研究其余部分(好比进程管理、文件系统)时你会发现套路是如此通用。

摄像头(VL42)、声卡ALSA驱动是Linux中比较复杂的2类驱动,它们是很难的,若是工做与此相关再去研究。

2.5,要学习Linux应用程序吗?先学一些基础技能

要学,即便之后你只想研究内核,一些基本的应用开发编写能力也是须要的:

① 基本设备的访问,好比LCD、输入设备

② 进程、线程、进程通讯、线程同步与互斥

③ 休眠-唤醒、POLL机制、信号

④ 网络编程

①②③部分的知识,跟驱动有密切的关系,它们是相辅相承的。

掌握了基本驱动开发能力、基本应用开发能力以后,在工做中你就能够跟别人友好沟通了,不至于一脸懵逼。

2.6,应用程序是怎么启动的?要了解一下根文件系统

你辛辛苦苦写出了应用程序,怎么把它放到板子上,让它开机就自动启动?

你写的程序,它依赖于哪些库,这些库放到板子上哪一个目录?

怎么作一个可升级的系统?即便升级中途断电了,也要保证程序至少还能够运行老的版本?

这些都须要咱们了解一下根文件系统。

先了解一下init进程:它要读取配置文件,根据配置文件启动各个APP。

了解了init进程,你就了解了根文件系统的组成,就能够为所欲为裁剪系统,为你的项目制做出最精简的系统。

第3章 学习方法

3.1,先不要打破砂锅问到底

嵌入式涉及的东西太多太杂了,若是内心没有主线,碰到什么都要去研究个透彻,最终反而忘记本身要学什么了。

嵌入式涉及硬件知识、软件知识,软件里涉及汇编、ARM架构、C语言、Makefile、Shell;又分为bootloader、内核、驱动、基本的APP、GUI。

好比咱们会用到Makefile,了解它的基本规则,会用咱们提供的Makefile就能够。

不须要深刻研究那些make函数,由于在工做中都有现成的Makefile给你使用,不须要本身去编写一套Makefile。何须花上好几天去深刻研究它呢?

好比咱们会用到bootloader,难道又要花上几个月来深刻研究u-boot吗?工做中基本不须要改u-boot,会用那几个命令就能够。

甚至有些学员先去买本shell的书来学习shell命令,何须?咱们在视频中用到什么命令,你不懂时再去百度一下这些命令就能够了。

不要脱离初学者的主线:应用基础、驱动基础。有了这2个基础后,你想深刻研究某部分时,再去花时间吧。

3.2,思路要清晰,不怕抄代码

视频里的代码,请你必定要本身去写一次、写屡次。为何我如今写驱动那么熟?我2009年在华清远见上课时,

每次上课我都要给学生写一次那些驱动,十几回下来闭着眼睛都知道内核的套路了。

记不住那些函数?我也记不住,我都是去参考同类的驱动程序,这又不是闭卷考试。

可是要理清楚思路,你写这个程序要完成什么功能、怎么实现这些功能?这个要弄清楚。

有了思路后再写代码,不知道怎么写?不要紧,看看视频,看看示例,而后关闭视频看看可否本身写出来。

3.3,对本身的方向很了解,我只能带你到这里了

个人专长是操做系统,是快速地带领你们掌握一些项目开发的基础知识。

若是你决定深刻研究某方面时,我并不能带你多久。你要去看源码,去看这方面的专业书籍。

好比想深刻钻研内核的内存管理时,它有页表映射(你须要阅读ARM架构的手册)、SLAB分配器、vmalloc/malloc实现、mmap实现、缺页中断、父进程子进程之间的页面管理等等,内容很是多。有时候连书籍都没有,你须要直接啃代码。

当你想从事某个行业时,就须要深刻研究行业相关的知识。

好比CAN总线,它能够写成一本书:CAN协议、CAN报文、Socket CAN、车身网络拓扑结构,CAN应用报文,CAN网络管理报文,CAN诊断报文。

想作物联网网关,须要深刻研究MQTT,MQTT协议相对简单,可是MQTT英文原版协议有130多页,中文版有近100页,是一本小书了。

每一个行业都有本身的业务逻辑,在掌握基本的编程能力之一,你须要结合具体的业务去深刻学习。