感谢各位的关注和支持,你们的支持是原子哥无限提高的动力。
第一章《运用编程观点》
由于本章内容较多,以是第一章《运用编程观点》将会分为几个部分进行内容的发布,更多精彩原创文章请持续关注正点原子原子哥官方账号。

对付大多数首次打仗Linux运用编程的读者来说,可能对运用编程(也可称为系统编程)这个观点并不太理解,以是在正式学习Linux运用编程之前,笔者有必要向大家先容这些大略基本的观点,从整体上认识到运用编程为何物?与驱动编程、裸机编程有何不同?
理解本章所先容的内容是节制运用编程的先决条件,以是本章紧张内容便是对Linux运用编程进行一个大略地先容,让读者对此有一个基本的认识。
本章将会谈论如下主题内容。
作甚系统调用;
作甚库函数;
运用程序的main()函数;
运用程序开拓环境的先容。
一1.系统调用系统调用(system call)实在是Linux内核供应给运用层的运用编程接口(API),是Linux运用层进入内核的入口。不止Linux系统,所有的操作系统都会向运用层供应系统调用,运用程序通过系统调用来利用操作系统供应的各种做事。
通过系统调用,Linux运用程序可以要求内核以自己的名义实行某些事情,譬如打开磁盘中的文件、读写文件、关闭文件以及掌握其它硬件外设。
通过系统调用API,运用层可以实现与内核的交互,其关系可通过下图大略描述:
图 1.1.1 内核、系统调用与运用程序
内核供应了一系列的做事、资源、支持一系列功能,运用程序通过调用系统调用API函数来利用内核供应的做事、资源以及各种各样的功能,如果大家打仗过其它操作系统编程,想必对此并不陌生,譬如Windows运用编程,操作系统内核一样平常都会向运用程序供应运用编程接口API,否则我们将我无法利用操作系统。
运用编程与裸机编程、驱动编程有什么差异?
在学习运用编程之前,相信大家都有过软件开拓履历,譬如51、STM32等单片机软件开拓、以及嵌入式Linux硬件平台下的驱动开拓等,51、STM32这类单片机的软件开拓常日是裸机程序开拓,并不会涉及到操作系统的观点,那运用编程与裸机编程以及驱动开拓有什么差异呢?
就拿嵌入式Linux硬件平台下的软件开拓来说,我们大可将编程分为三种,分别为裸机编程、Linux驱动编程以及Linux运用编程。首先对付裸机编程这个观点来说很好理解,一样平常把没有操作系统支持的编程环境称为裸机编程环境,譬如单片机上的编程开拓,编写直接在硬件上运行的程序,没有操作系统支持;狭义上Linux驱动编程指的是基于内核驱动框架开拓驱动程序,驱动开拓工程师通过调用Linux内核供应的接口完成设备驱动的注册,驱动程序卖力底层硬件操作干系逻辑,如果学习过Linux驱动开拓的读者,想必对此并不陌生;而Linux运用编程(系统编程)则指的是基于Linux操作系统的运用编程,在运用程序中通过调用系统调用API完成运用程序的功能和逻辑,运用程序运行于操作系统之上。常日在操作系统下有两种不同的状态:内核态和用户态,运用程序运行在用户态、而内核则运行在内核态。
关于运用编程这个观点,以上给大家阐明得很清楚了,笔者以实现点亮一个LED功能为例,给大家大略地解释三者之间的差异,LED裸机程序如下所示:
示例代码 1.1.1 LED裸机程序static void led_on(void){ / 点亮LED硬件操作代码 /} static void led_off(void){ / 熄灭LED硬件操作代码 /} int main(void){ / 用户逻辑 / for ( ; ; ) { led_on(); //点亮LED delay(); //延时 led_off(); //熄灭LED delay(); //延时 }}
可以看到在裸机程序当中,LED硬件操作代码与用户逻辑代码全部都是在同一个源文件(同一个工程)中实现的,硬件操作代码与用户逻辑代码没有隔离,没有操作系统支持,代码编译之后直接在硬件平台运行,俗称“裸跑”。我们再来看一个Linux系统下的LED驱动程序示例代码,如下所示:
示例代码 1.1.2 Linux下LED驱动程序#include <linux/module.h>#include <linux/platform_device.h>#include <linux/of_gpio.h>#include <linux/delay.h>#include <linux/cdev.h>#include <linux/uaccess.h> static void led_on(void){ / 点亮LED硬件操作代码 /} static void led_off(void){ / 熄灭LED硬件操作代码 /} static int led_open(struct inode inode, struct file filp){ / 打开设备时须要做的事情 /} static ssize_t led_write(struct file filp, const char __user buf, size_t size, loff_t offt){ int flag; / 获取运用层write的数据,存放在flag变量 / if (copy_from_user(&flag, buf, size)) return -EFAULT; / 判断用户写入的数据,如果是0则熄灭LED,如果是非0则点亮LED / if (flag) led_on(); else led_off(); return 0;} static int led_release(struct inode inode, struct file filp){ / 关闭设备时须要做的事情 /} static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .write = led_write, .release= led_release,}; static int led_probe(struct platform_device pdev){ / 驱动加载时须要做的事情 /} static int led_remove(struct platform_device pdev){ / 驱动卸载时须要做的事情 /} static const struct of_device_id led_of_match[] = { { .compatible = "alientek,led", }, { / sentinel / },};MODULE_DEVICE_TABLE(of, led_of_match); static struct platform_driver led_driver = { .driver = { .owner = THIS_MODULE, .name = "led", .of_match_table= led_of_match, }, .probe = led_probe, .remove = led_remove,};module_platform_driver(led_driver); MODULE_DESCRIPTION("LED Driver");MODULE_LICENSE("GPL");
以上并不是一个完全的LED驱动代码,如果没有打仗过Linux驱动开拓的读者,看不懂也没有关系,并无大碍,此驱动程序利用了最基本的字符设备驱动框架编写而成,非常大略;led_fops工具中供应了open、write、release方法,当运用程序调用open系统调用打开此LED设备时会实行到led_open函数,当调用close系统调用关闭LED设备时会实行到led_release函数,而调用write系统调用时会实行到led_write函数,此驱动程序的设定是当运用层调用write写入0时熄灭LED,write写入非0时点亮LED。
驱动程序属于内核的一部分,当操作系统启动的时候会加载驱动程序,可以看到LED驱动程序中仅仅实现了点亮/熄灭LED硬件操作干系逻辑代码,运用程序可通过write这个别系调用API函数掌握LED亮灭;接下来我们看看Linux系统下的LED运用程序示例代码,如下所示:
示例代码 1.1.3 Linux下LED运用程序#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h> int main(int argc, char argv){ int fd; int data; fd = open("/dev/led", O_WRONLY);//打开LED设备(假定LED的设备文件为/dev/led) if (0 > fd) return -1; for ( ; ; ) { data = 1; write(fd, &data, sizeof(data)); //写1点亮LED sleep(1); //延时1秒 data = 0; write(fd, &data, sizeof(data)); //写0熄灭LED sleep(1); //延时1秒 } close(fd); return 0;}
此运用程序也非常大略,仅只需实现用户逻辑代码即可,循环点亮、熄灭LED,并不须要实现硬件操作干系,示例代码中调用了open、write、close这三个别系调用API接口,open和close分别用于打开/关闭LED设备,write写入数据传给LED驱动,传入0熄灭LED,传入非0点亮LED。
LED运用程序与LED驱动程序是分隔、分离的,它们单独编译,它们并不是整合在一起的,运用程序运行在操作系统之上,有操作系统支持,运用程序处于用户态,而驱动程序处于内核态,与纯粹的裸机程序存在着质的差异。Linux运用开拓与驱动开拓是两个不同的方向,将来在事情当中也会卖力不同的任务、办理不同的问题。
关于本小节系统调用观点干系的内容就先容到这里了,接下来向大家先容库函数。
2.库函数前面给大家先容了系统调用,系统调用是内核直接向运用层供应的运用编程接口,譬如open、write、read、close等,关于这些系统调用后面会给大家进行详细先容。编写运用程序除了利用系统调用之外,我们还可以利用库函数,本小节来聊一聊库函数。
库函数也便是C措辞库函数,C措辞库是运用层利用的一套函数库,在Linux下,常日以动态(.so)库文件的形式供应,存放在根文件系统/lib目录下,C措辞库函数构建于系统调用之上,也便是说库函数实在是由系统调用封装而来的,当然也不能完备这么说,缘故原由在于有些库函数并不调用任何系统调用,譬如一些字符串处理函数strlen()、strcat()、memcpy()、memset()、strchr()等等;而有些库函数则会利用系统调用来帮它完成实际的操作,譬如库函数fopen内部调用了系统调用open()来帮它打开文件、库函数fread()就利用了系统调用read()来完成读文件操作、fwrite()就利用了系统调用write()来完成写文件操作。
Linux系统内核供应了一系列的系统调用供应用层利用,我们直策应用系统调用就可以了呀,那为何还要设计出库函数呢?事实上,有些系统调用利用起来并不是很方便,于是就涌现了C措辞库,这些C措辞库函数的设计是为了供应比底层系统调用更为方便、更为好用、且更具有可移植性的调用接口。
来看一看它们之间的差异:
库函数是属于运用层,而系统调用是内核供应给运用层的编程接口,属于系统内核的一部分;
库函数运行在用户空间,调用系统调用会由用户空间(用户态)陷入到内核空间(内核态);
库函数常日是有缓存的,而系统调用是无缓存的,以是在性能、效率上,库函数常日要优于系统调用;
l 可移植性:库函数比较于系统调用具有更好的可移植性,常日对付不同的操作系统,其内核向运用层供应的系统调用每每都是不同,譬如系统调用的定义、功能、参数列表、返回值等每每都是不一样的;而对付C措辞库函数来说,由于很多操作系统都实现了C措辞库,C措辞库在不同的操作系统之间其接口定义险些是一样的,以是库函数在不同操作系统之间比较于系统调用具有更好的可移植性。
以上便上它们之间一个大致的差异,从实现者的角度来看,系统调用与库函数之间有根本的差异,但从用户利用角度来看,其差异并不主要,它们都是C措辞函数。在实际运用编程中,库函数和系统调用都会利用到,以是对付我们来说,直接把它们当做是C函数即可,知道你自己调用的函数是系统调用还是库函数即可,不用太过于区分它们之间的差别。
以是运用编程大略点来说便是:开拓Linux运用程序,通过调用内核供应的系统调用或利用C库函数来开拓具有相应功能的运用程序。
3.标准C措辞函数库在Linux系统下,利用的C措辞库为GNU C措辞函数库(也叫作glibc,其网址为http://www.gnu.org/software/libc/),作为Linux下的标准C措辞函数库。
进入到http://www.gnu.org/software/libc/网址,如下所示:
图 1.3.1 glibc官网
点击上面的Sources选项可以查看它的源码实现:
图 1.3.2 获取源码的办法
glibc源码的获取办法很大略,直接直接从git仓库下载,也可以通过ftp下载,如果大家有兴趣、或者想要理解某一个库函数它的详细实现,那么就可以获取到它源码来进行剖析,好了,这里就不再多说了!
确定Linux系统的glibc版本
前面提到过了,C措辞库因此动态库文件的形式供应的,常日存放在/lib目录,它的命名办法常日是libc.so.6,不过这个是一个软链接文件,它会链接到真正的库文件。
进入到Ubuntu系统的/lib目录下,笔者利用的Ubuntu版本为16.04,在我的/lib目录下并没有创造libc.so.6这个文件,实在是在/lib/x86_64-linux-gnu目录下,进入到该目录:
图 1.3.3 libc.so.6文件
可以看到libc.so.6链接到了libc-2.23.so库文件,2.23表示的便是这个glibc库的版本号为2.23。除此之外,我们还可以直接运行该共享库来获取到它的信息,如下所示:
图 1.3.4 确定glibc版本号
从打印信息可以看到,笔者所利用的Ubuntu系统对应的glibc版本号为2.23。
4.main函数对学习过C措辞编程的读者来说,譬如单片机编程、Windows运用编程等,main函数想必大家再熟习不过了,很多编程开拓都因此main函数作为程序的入口函数,同样在Linux运用程序中,main函数也是作为运用程序的入口函数存在,main函数的形参一样平常会有两种写法,如果实行运用程序无需传参,则可以写成如下形式:
示例代码 1.4.1 main函数写法之无传参int main(void){ / 代码 /}如果在实行运用程序的时候须要向运用程序通报参数,则写法如下:示例代码 1.4.2 main函数写法之有传参int main(int argc, char argv){ / 代码 /}
5.本书利用的开拓环境
对付编程开拓,大家可能都比较关心开拓环境的问题,譬如本书将利用什么IDE编写运用程序之类的问题,本小节将对开拓环境的问题进行一个大略先容。
首先,本书分为三篇内容,在序言部分已经向各位读者先容了本书的内容方案,分为入门篇、提高篇以及进阶篇。由于是Linux运用编程,以是本书将会在Linux操作系统环境下编写示例代码,为了同正点原子的其它开拓文档保持同等,选择Ubuntu操作系统,推举大家利用16.04或14.04版本的Ubuntu系统,个人以为这两个版本比较稳定;除了利用Ubuntu系统外,大家还可以选择其它Linux发行版,譬如CentOS、Redhat(红帽)等。
在Linux操作系统下,也有很多比较好用的IDE软件,可以帮助我们更为轻松的进行软件开拓,譬如Eclipse、vscode等,如果你会利用Eclipse,可以在Ubuntu系统下安装Eclipse进行Linux运用开拓,1.5.1小节先容了如何在Ubuntu系统下安装Eclipse;如果你不会利用Eclipse,那就建议你利用vscode,同样本书也是利用vscode,与正点原子的驱动开拓文档保持同等;vscode的安装方法和利用方法本书不再先容,相信大家在自己的Ubuntu系统下已经安装好了,如果没有安装的,直接参考“开拓板光盘资料A-根本资料/【正点原子】I.MX6U嵌入式Linux驱动开拓指南V1.5.pdf”文档第四章的4.5小节内容进行安装即可!
Tips:把稳,本文档并不先容Eclipse IDE的利用方法,笔者也用的不多,只是见告大家可以在Ubuntu利用这个IDE,并且后面给出了安装方法,如果你以前常常利用它开拓软件,你可以考试测验利用它在Ubuntu下开拓Linux运用程序,如果你没用过就不要去碰了。用什么IDE都不主要,哪怕你直策应用vi编写程序都可以,我们的重点是学习运用编程!
而不是学习IDE怎么用。
vscode是一个代码编辑器,供应了很多好用的插件,譬如语法检测、高亮显示、智能补全等,大家可以根据自己的选择安装插件,在驱动开拓指南文档中也都有先容。
入门篇章节内容中,紧张向大家先容了Linux运用编程所涉及到的各个知识点以及各种系统调用和库函数,本部分内容所编写的示例代码,均是在Ubuntu系统下进行测试、验证,也便是在Ubuntu系统下实行测试程序以验证所学知识内容。以是在该部分内容中,本书利用vscode+gcc的办法开拓运用程序,vscode作为代码编辑器、gcc作为编译器,利用vscode编写完代码之后再用gcc编译源码,得到可在PC端Ubuntu系统下运行的可实行文件。gcc的利用方法在驱动开拓指南中有先容,参考“开拓板光盘资料A-根本资料/【正点原子】I.MX6U嵌入式Linux驱动开拓指南V1.5.pdf”文档第三章的内容。
提高篇章节内容中,将会以正点原子阿尔法I.MX6U Linux开拓板为例,向大家先容如何编写运用程序掌握开拓板上的各种硬件外设,以是编写的运用程序是须要在开拓板上运行的,阿尔法I.MX6U开拓板是ARM架构硬件平台,以是源码须要利用交叉编译工具(ARM架构下的gcc编译器)进行编译,得到可在开拓板上运行的可实行文件。以是在第二篇章节内容中,本书利用vscode+ARM gcc(交叉编译工具)的办法开拓运用程序。
入门篇和提高篇章节内容中所编写的示例代码只有单个.c源文件,但是对付一个真正的Linux运用程序项目来说,其原文件的个数肯定不止一个,可能有几十上百个,这个时候可能就须要用到Makefile来组织工程源文件。直接通过Makefile来组织工程,对付一个大型软件工程来说常日是比较困难的,须要程序员对Makefile能够闇练利用,譬如Linux内核源码、U-Boot源码等都是直策应用Makefile进行管理。对付一样平常程序员来说,很难做到闇练利用Makefile。在第三篇(进阶篇)章节内容中,会向大家先容cmake工具,利用cmake来管理我们的源码工程,比较于直策应用Makefile,利用cmake会打打降落难度,提高我们的开拓效率!
进阶篇章节内容中,会给大家先容一些Linux实战小项目,通过学习进一步提升Linux运用编程能力,本部内容将会利用cmake+vscode进行开拓,实在cmake在实际的项目当中用的还是比较多的,譬这样多的开源软件都会选择利用cmake来构建、配置工程源码,学习了之后,相信大家在今后的事情当中也会用到。
关于本小节的内容到这里就结束了,下一章开始将正式进入到运用编程学习之路,大家加油!
本小节先容如何在Ubuntu系统下安装Eclipse,笔者利用的Ubuntu系统版本号为16.04.5,其它版本笔者未进行验证,不过大家试一试,如果涌现了问题,再根据提示找到相应的办理办法!
Eclipse是一个开源的IDE,基于java措辞开拓的可扩展开拓平台,支持很多插件,可用于诸如java、C/C++、PHP、Android等编程措辞的软件开拓,关于更加详细的先容大家自己去理解,本书不再先容!
㈠下载Eclipse
开拓板资料包中供应了Eclipse安装包,路径为:开拓板光盘->3、软件->eclipse-cpp-2020-12-R-linux-gtk-x86_64.tar.gz。
首先我们要下载Eclipse安装包,进入到Eclipse官网:https://www.eclipse.org/,如下所示:
图 1.5.1 Eclipse官网
点击右上角“Download”按钮进入到下载页面:
图 1.5.2 Download页面
点击下边的“Download Package”选项:
图 1.5.3 package列表
当前页面显示的是最新版本的软件包,可以看到Eclipse的最新版本是2021-03,上图右下角列出了其它的版本软件包的链接地址,这里笔者选择2020-12,也便是4.18版本作为演示,其它版本笔者没测试过,理论上都是可以的。
点击4.18版本链接地址,页面跟上图一样,由于是利用Eclipse进行C措辞运用程序开拓,以是选择“Eclipse IDE for C/C++ Developers”软件包,选择Linux系统的安装包,点击下图红框中的x86_64链接:
图 1.5.4 C/C++ IDE
点击之后会进入到下载界面,点击下图中的“Download”即可下载:
图 1.5.5 下载
下载完之后会得到压缩包文件“eclipse-cpp-2020-12-R-linux-gtk-x86_64.tar,gz”:
图 1.5.6 Eclipse压缩包文件
㈡、下载jdk
开拓板资料包中供应了jdk,路径为:开拓板光盘->3、软件->jdk-8u291-linux-x64.tar.gz。
由于Eclipse是基于java措辞开拓的,以是它依赖于java jdk,以是我们要下载,进入到https://www.oracle.com/cn/java/technologies/javase-downloads.html网页,如下所示:
图 1.5.7 jdk下载主页
我们往下翻找到Java SE8,如下所示:
图 1.5.8 Java SE8
点击JDK Download进入到jdk8下载页面,往下翻找到软件包下载列表:
图 1.5.9 jdk8下载页面
可以看到上图列举出了很多不同操作系统以及硬件平台的jdk包,操作系统包括Linux、macOS以及Windows等,还有不同的硬件平台,笔者用的是64位的Ubuntu系统,以是选择“Linux x64 Compressed Archive”,点击后边“jdk-8u291-linux-x64.tar.gz”下载。之后会弹出如下界面:
图 1.5.10 下载
勾选图中所示的选项,之后点击进行下载。下载须要登录Oracle账户,譬如下图所示,填写用户名、密码点击登录之后才能下载;没有账户的读者须要点击下面的“创景账户”进行创建。
图 1.5.11 登录Oracle账户
下载完之后得到一个压缩包文件:
图 1.5.12 jdk压缩包文件
㈢、安装Eclipse
首先把前面下载好的两个压缩包文件拷贝到Ubuntu系统用户家目录下,如下所示:
图 1.5.13 将压缩包文件拷贝到Ubuntu系统
接着将eclipse-cpp-2020-12-R-linux-gtk-x86_64.tar.gz压缩包文件解压到/opt目录下,如下所示:
sudo tar -xzf eclipse-cpp-2020-12-R-linux-gtk-x86_64.tar.gz -C /opt/
图 1.5.14 解压eclipse-cpp-2020-12-R-linux-gtk-x86_64.tar.gz文件
实行解压命令的时候,须要加上sudo,这样才能有权限将压缩文件解压到/opt目录下,解压之后会在/opt目录下天生eclipse文件夹,如下所示:
图 1.5.15 eclipse文件夹
这样Eclipse就安装完成了,我们下载的这个压缩包是一个免安装的,直接解压就可以了,eclipse目录便是软件的安装目录了,如下所示:
图 1.5.16 Eclipse安装目录
接下来创建一个桌面快捷办法,可以通过双击桌面图标来打开Eclipse软件。首先进入到桌面孔录下,如下所示:
图 1.5.17 进入桌面孔录
笔者的Ubuntu系统配置的是中文措辞环境,如果你的是英文环境,那么对应的该当是Desktop目录。在该目录下创建一个名为eclipse.desktop的文件,将以下内容写入到该文件中:
[Desktop Entry]
Encoding=UTF-8
Name=Eclipse
Comment=Eclipse
Exec=/opt/eclipse/eclipse
Icon=/opt/eclipse/icon.xpm
Terminal=false
StartupNotify=true
Type=Application
Categories=Application;Development;
图 1.5.18 eclipse.desktop文件中的内容
个中“Exec=”后面为eclipse安装目录下的eclipse可实行文件的路径,“Icon=”后面为eclipse安装目录下的图标图片的路径,编辑完成之后保存退出。末了为eclipse.desktop文件添加上可实行权限:
chmod u+x eclipse.desktop
完成之后,Ubuntu系统桌面上会涌现Eclipse软件图标快捷办法:
图 1.5.19 桌面图标
未完待续...