i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、
【公众号】迅为电子
【粉丝群】258811263(加群获取驱动文档+例程)
第二篇 Linux系统编程篇
本章内容对应视频讲解链接(在线观看):
什么是linux系统编程 → https://www.bilibili.com/video/BV1zV411e7Cy?share_source=copy_web
Linux系统编程基本程序框架 → Linux系统编程基本程序框架_哔哩哔哩_bilibili
什么是Linux系统编程呢?Linux系统编程也叫Linux下的高级编程。是介于应用层和驱动层之间的。内核向用户提供的接口。本章讲述编写Linux系统应用层软件常用的一些技术,包括文件IO,标准IO,进程线程操作。这些运行在系统应用层的程序直接与内核和系统核心库进行交互,只能在Linux上运行,不能跨平台,也就是不能运行在其他操作系统上(比如windows)。Linux根据UNIX发展而来,属于类UNIX操作系统,拥有UNIX特点,但是Linux作为开源软件更专注实用功能,支持更多的系统调用,从而拥有更多的新特性。
学习系统编程可以使用man手册查看API,查找用到的头文件,如“man 2 open”,使用“top”等命令查看进程状态。本文档主要通过实验例程来说明各系统调用API和各种机制的用法。
在开始系统编程前首先要搭建环境,大家可以参考本手册第二十四章安装 Samba,首先我们来了解下Linux系统编程的基本程序框架。Samba搭建好之后,我们在samba文件夹下新建linux文件夹,如下图所示:
首先来编写下Linux系统编程的基本程序框架,在linux文件夹下新建01文件夹,01文件夹里面新建test.c文件,内容如下所示。代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\01”目录下。
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
//argc:表示的是命令行中参数的个数。
//argv:表示的是命令行中的参数
int i;
printf("argc is %d\n",argc);
for(i=0;i<argc;i++){
printf("argv[%d] is %s\n",i,argv[i]);
};
return 0;
}
我们在Ubuntu的Samba目录下输入以下编译test.c,如下图所示编译生成了a.out。
gcc test.c
ls
此时main()函数是没有参数的,但是我们在学习Linux系统编程的时候,大多数main函数都是带参数的,因为我们要配合命令行来给我们的程序传参数。大部分情况下,main函数的参数为int argc,char *argv[]。argc表示的命令行中参数的个数。argv表示的是命令行中的参数。
第三十一章 文件IO和标准IO
本章内容对应视频讲解链接(在线观看):
标准IO和文件IO → https://www.bilibili.com/video/BV1zV411e7Cy?p=3
文件 IO是Linux系统提供的接口,针对文件和磁盘进行操作,不带缓存机制;标准IO是C语言函数库里的标准I/O模型,在stdio.h中定义,通过缓冲区操作文件,带缓存机制。Linux系统中一切皆文件,包括普通文件,目录,设备文件(不包含网络设备),管道,fifio队列,socket套接字等,在终端输入“ls -l”可查看文件类型和权限。
标准IO和文件IO常用API如下:
标准IO | 文件IO | |
打开/创建 | fopen | open |
读 | getc,fgetc,getchar,fgets,gets, fread | read |
写 | putc,fputc,putc,fputs,puts, fwrite | write |
关闭 | fclose | close |
标准IO和文件IO的区别如下图所示:
文件IO是直接调用内核提供的系统调用函数,头文件是unistd.h,标准IO是间接调用系统调用函数,头文件是stdio.h,文件IO是依赖于Linux操作系统的,标准IO是不依赖操作系统的,所以在任何的操作系统下,使用标准IO,也就是C库函数操作文件的方法都是相同的。
对于文件IO来说,一切都是围绕文件操作符来进行的。在Linux系统中,所有打开的文件都有一个对应的文件描述符。文件描述符的本质是一个非负整数,当我们打开一个文件时,系统会给我们分配一个文件描述符。当我们对一个文件做读写操作的时候,我们使用open函数返回的这个文件描述符会标识该文件,并将其作为参数传递给read或者write函数。在posix.1应用程序里面,文件描述符0,1,2分别对应着标准输入,标准输出,标准错误。
31.1 文件IO open()
本章内容对应视频讲解链接(在线观看):
文件I0之open函数 → https://www.bilibili.com/video/BV1zV411e7Cy?p=4
函数 | int open(const char *pathname, int flags) int open(const char *pathname, int flags, mode_t mode) |
头文件 | #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> |
参数pathname | 路径和文件名 |
参数flags | 文件打开方式,可用多个标志位按位或设置 |
参数mode | 权限掩码,对不同用户和组设置可执行,读,写权限,使用八进制数表示,此参数可不写 |
返回值 | open()执行成功会返回int型文件描述符,出错时返回-1。 |
作用 | 通过系统调用,可以打开文件,并返回文件描述符 |
参数flags可选标志:
O_CREAT 要打开的文件名不存在时自动创建改文件。
O_EXCL 要和O_CREAT一起使用才能生效,如果文件存在则open()调用失败。
O_RDONLY 只读模式打开文件。
O_WRONLY 只写模式打开文件。
O_RDWR 可读可写模式打开文件。
O_APPEND 以追加模式打开文件。
O_NONBLOCK 以非阻塞模式打开。
实验代码
编写程序,在同一目录下创建并打开一个可读可写文件a.c。代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\02”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd;
fd=open("a.c",O_CREAT|O_RDWR,0666);
if(fd<0)
{
printf("open is error\n");
}
printf("fd is %d\n",fd);
return 0;
}
编译运行:
在Ubuntu上编译open.c,并运行a.out,如下图所示:
在程序中打开的文件描述符为3,这是因为在一个进程中至少包含三个文件描述符,即0表示标准输入stdin,1表示标准输出stdout,2表示标准错误stderr。
交叉编译open.c,通过NFS将编译好的文件拷贝到iTOP-iMX8MM开发板,如下图所示:
31.2 文件IO close()
本章内容对应视频讲解链接(在线观看):
文件I0之close函数→https://www.bilibili.com/video/BV1zV411e7Cy?p=5
函数 | int close(int fd) |
头文件 | #include <unistd.h> |
参数fd | 文件描述符 |
返回值 | 成功返回0;错误返回-1 |
实验代码:
编写程序,在同一目录下打开并创建一个可读可写文件,获取完属性后关闭。代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\03”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
fd=open("a.c",O_CREAT|O_RDWR,0666);
if(fd<0)
{
printf("open is error\n");
}
printf("fd is %d\n",fd);
close(fd);
return 0;
}
编译运行:
在Ubuntu上编译open.c,如下图所示:
31.3 文件IO read()
本章内容对应视频讲解链接(在线观看):
文件I0之read函数 → https://www.bilibili.com/video/BV1zV411e7Cy?p=6
函数 | ssize_t read(int fd, void *buf, size_t count) |
头文件 | #include <unistd.h> |
参数fd | 要读的文件描述符 |
参数buf | 缓冲区,存放读到的内容 |
参数count | 每次读取的字节数 |
返回值 | 返回值大于0,表示读取到的字节数; 等于0在阻塞模式下表示到达文件末尾或没有数据可读(EOF),并调用阻塞; 等于-1表示出错,在非阻塞模式下表示没有数据可读。 |
实验代码
在程序中打开a.c文件,并读取打印文件内容。代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\04”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("a.c", O_CREAT | O_RDWR, 0666);
if (fd < 0)
{
printf("open is error\n");
return -1;
}
printf("fd is %d\n", fd);
ret = read(fd, buf, 32);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}
编译运行:
在Ubuntu上编译open.c,运行a.out,如下图所示:
编辑a.c文件,内容如下图所示:
运行a.out,如下图所示,成功读取到a.c文件中的hello world!
31.4 文件IO write()
本章内容对应视频讲解链接(在线观看):
文件I0之write函数 → https://www.bilibili.com/video/BV1zV411e7Cy?p=7
函数 | ssize_t write(int fd, const void *buf, size_t count); |
头文件 | #include <unistd.h> |
参数fd | 文件描述符; |
参数buf | 缓存区,存放将要写入的数据 |
参数count | 每次写入的个数 |
功能 | 每次从buf缓存区拿count个字节写入fd文件。 |
返回值 | 大于或等于0表示执行成功,返回写入的字节数; 返回-1代表出错。 |
实验代码
在程序中使用write函数向标准输出(屏幕)打印hello。代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\05”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
char buf[32]={0};
ssize_t ret;
fd=open("a.c",O_CREAT|O_RDWR,0666);
if(fd<0)
{
printf("open is error\n");
return -1;
}
write(1,"hello\n",6);
close(fd);
return 0;
}
编译程序并运行a.out ,如下图所示,向标准输出(屏幕)写入hello。
实验代码
在程序中使用write函数向文件写入hello。代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\05”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
char buf[32]={0};
ssize_t ret;
fd=open("a.c",O_CREAT|O_RDWR,0666);
if(fd<0)
{
printf("open is error\n");
return -1;
}
write(fd,"hello\n",6);
close(fd);
return 0;
}
运行测试
编译运行打开a.out,并且查看写入的文件a.c,如下图所示,a.c文件成功写入hello。
31.5 综合练习(一)
本章内容对应视频讲解链接(在线观看):
综合练习(一) → https://www.bilibili.com/video/BV1zV411e7Cy?p=8
实验要求
在程序中通过命令行操作,把a.c文件里面的内容写到b.c
实验代码
代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\06”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
//步骤一:判断命令行的参数
if(argc != 3)
{
printf("Usage:%s <src file> <obj file>\n", argv[0]);
}
// 步骤二:定义变量
int fd_src;
int fd_obj;
char buf[32] = {0};
ssize_t ret;
// 步骤三:打开文件获得文件描述符
fd_src = open(argv[1], O_RDWR);
if (fd_src < 0)
{
printf("open is error\n");
return -1;
}
fd_obj = open(argv[2], O_CREAT | O_RDWR, 0666);
if (fd_obj < 0)
{
printf("open is error\n");
return -2;
}
// 步骤四:读写操作
while((ret=read(fd_src,buf,32))!=0)
{
write(fd_obj,buf,ret);
}
// 步骤五:关闭文件描述符
close(fd_src);
close(fd_obj);
return 0;
}
编译测试
在Ubuntu上编译open.c,首先查看a.c的内容,然后运行程序,再查看b.c的内容,如下图所示,成功将a.c文件的内容拷贝到b.c文件。
31.6 文件IO lseek()
本章内容对应视频讲解链接(在线观看):
文件IO之Iseek函数 → https://www.bilibili.com/video/BV1zV411e7Cy?p=9
所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为 cfo。cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。文件被打开时,cfo 会被初始化为 0,除非使用了O_APPEND 。 使用 lseek 函数可以改变文件的 cfo 。
函数定义 | off_t lseek(int fd, off_t offset, int whence); |
头文件 | #include <sys/types.h> #include <unistd.h> |
参数fd | 文件描述符 |
参数off_t offset | 偏移量,单位是字节的数量,可以正负,如果是负值表示向前移动;如果是正值,表示向后移动。 |
参数whence | 当前位置的基点,可以使用以下三组值。 SEEK_SET:相对于文件开头 SEEK_CUR:相对于当前的文件读写指针位置 SEEK_END:相对于文件末尾 |
功能 | 移动文件读写指针;获取文件长度;拓展文件空间。 |
返回值 | 成功返回当前位移,失败返回-1 |
举个例子
- 把文件位置指针设置为100 lseek(fd,100,SEEK_SET);
- 把文件位置设置成文件末尾 lseek(fd,0,SEEK_END);
- 确定当前的文件位置 lseek(fd,0,SEEK_CUR);
实验代码
在程序中使用lseek函数移动文件读写位置,代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\07”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char *argv[])
{int fd;char buf[32] = {0};ssize_t ret;fd = open("a.c", O_CREAT | O_RDWR, 0666);if (fd < 0){printf("open is error\n");return -1;}// printf("fd is %d\n", fd);ret = read(fd, buf, 32);if (ret < 0){printf("read is error\n");return -2;}printf("buf is %s\n", buf);printf("ret is %ld\n", ret);lseek(fd,0,SEEK_SET);ret = read(fd, buf, 32);printf("ret is %ld\n", ret);close(fd);return 0;
}
运行测试
编译运行打开a.out ,a.c里面有hello world!,当使用lseek函数移动读写位置时,再次读a.c文件的内容,依旧可以读到a.c的内容,如下图所示:
实验代码
在程序中读a.c文件的2字节,使用lseek函数确定文件的当前位置。代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\07”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char *argv[])
{int fd;char buf[32] = {0};ssize_t ret;fd = open("a.c", O_CREAT | O_RDWR, 0666);if (fd < 0){printf("open is error\n");return -1;}// printf("fd is %d\n", fd);ret = read(fd, buf, 2);if (ret < 0){printf("read is error\n");return -2;}printf("buf is %s\n", buf);printf("ret is %ld\n", ret);ret=lseek(fd,0,SEEK_CUR);// ret = read(fd, buf, 32);printf("ret is %ld\n", ret);close(fd);return 0;
}
运行测试
编译运行打开a.out ,a.c里面有hello world!运行结果如下图所示:
实验代码
在程序中读a.c文件,使用lseek函数确定文件的长度。代码在配套资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\1.系统编程例程\系统编程配套程序\linux\07”目录下。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("a.c", O_CREAT | O_RDWR, 0666);
if (fd < 0)
{
printf("open is error\n");
return -1;
}
// printf("fd is %d\n", fd);
ret = read(fd, buf, 2);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
ret=lseek(fd,0,SEEK_END);
// ret = read(fd, buf, 32);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}
运行测试
编译运行打开a.out ,a.c里面有hello world!运行结果如下图所示: