iTOP-STM32MP157开发板采用ST推出的双核cortex-A7+单核cortex-M4异构处理器,既可用Linux、又可以用于STM32单片机开发。开发板采用核心板+底板结构,主频650M、1G内存、8G存储,核心板采用工业级板对板连接器,高可靠,牢固耐用,可满足高速信号环境下使用。共240PIN,CPU功能全部引出:底板扩展接口丰富底板板载4G接口(选配)、千兆以太网、WIFI蓝牙模块HDMI、CAN、RS485、LVDS接口、温湿度传感器(选配)光环境传感器、六轴传感器、2路USB OTG、3路串口,CAMERA接口、ADC电位器、SPDIF、SDIO接口等
第二篇 Linux系统编程篇
什么是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-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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表示的是命令行中的参数。
第一部分 Linux下I/O操作
第三十章 文件IO和标准IO
文件 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分别对应着标准输入,标准输出,标准错误。
30.1 文件IO open()
函数定义:
open():通过系统调用,可以打开文件,并返回文件描述符。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数含义:
pathname:路径和文件名
flags:文件打开方式,可用多个标志位按位或设置,常用的标志位参数如下:
O_CREAT 要打开的文件名不存在时自动创建改文件。
O_EXCL 要和O_CREAT一起使用才能生效,如果文件存在则open()调用失败。
O_RDONLY 只读模式打开文件。
O_WRONLY 只写模式打开文件。
O_RDWR 可读可写模式打开文件。
O_APPEND 以追加模式打开文件。
O_NONBLOCK 以非阻塞模式打开。
mode: 权限掩码,对不同用户和组设置可执行,读,写权限,使用八进制数表示,此参数可不写。
返回值:
open()执行成功会返回int型文件描述符,出错时返回-1。
实验代码
编写程序,在同一目录下创建并打开一个可读可写文件a.c。代码在配套资料“iTOP-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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将编译好的文件拷贝到SRM32MP157开发板,如下图所示:
在开发板上运行结果如下图所示:
#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;
}
30.2 文件IO close()
函数定义:
close()可以关闭文件,通过系统调用取消文件描述符到文件的映射。
#include <unistd.h>
int close(int fd);
参数含义:
fd:文件描述符
返回值:成功返回0;错误返回-1.
实验代码:
编写程序,在同一目录下打开并创建一个可读可写文件,获取完属性后关闭。代码在配套资料“iTOP-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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,如下图所示:
30.3 文件IO read()
函数定义:
read():读取文件最常用的函数,每次从fd读取count个字节,保存到buf中。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数定义:
fd: 要读的文件描述符
buf: 缓冲区,存放读到的内容。
count: 每次读取的字节数
返回值:
返回值大于0,表示读取到的字节数;
等于0在阻塞模式下表示到达文件末尾或没有数据可读(EOF),并调用阻塞;
等于-1表示出错,在非阻塞模式下表示没有数据可读。
实验代码
在程序中打开a.c文件,并读取打印文件内容。代码在配套资料“iTOP-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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!
30.3 文件IO write()
函数 | ssize_t write(int fd, const void *buf, size_t count); |
头文件 | #include <unistd.h> |
参数fd | 文件描述符; |
参数buf | 缓存区,存放将要写入的数据 |
参数count | 每次写入的个数 |
功能 | 每次从buf缓存区拿count个字节写入fd文件。 |
返回值 | 大于或等于0表示执行成功,返回写入的字节数; 返回-1代表出错。 |
函数定义
write():常用来写文件,定义如下:
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数含义:
fd: 文件描述符;
buf: 缓存区,存放将要写入的数据;
count: 每次写入的个数。
函数功能:
每次从buf缓存区拿count个字节写入fd文件。
返回值:
大于或等于0表示执行成功,返回写入的字节数;
返回-1代表出错。
实验代码
在程序中使用write函数向标准输出(屏幕)打印hello。代码在配套资料“iTOP-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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。
30.4综合练习(一)
实验要求
在程序中通过命令行操作,把a.c文件里面的内容写到b.c
实验代码
代码在配套资料“iTOP-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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文件。
30.5文件IO lseek()
函数 | 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:相对于文件末尾 |
功能 | 每次从buf缓存区拿count个字节写入fd文件。 |
返回值 | 成功返回当前位移,失败返回-1 |
函数定义
lseek():移动文件的读写位置,定义如下:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数含义:
fd: 文件描述符;
off_t offset: 偏移量,单位是字节的数量,可以正负,如果是负值表示向前移动;如果是正值, 表示向后移动。
whence: 当前位置的基点,可以使用以下三组值。
SEEK_SET:相对于文件开头
SEEK_CUR:相对于当前的文件读写指针位置
SEEK_END:相对于文件末尾
函数功能:
每次从buf缓存区拿count个字节写入fd文件。
返回值:
成功返回当前位移,失败返回-1.
举个例子
- 把文件位置指针设置为100 lseek(fd,100,SEEK_SET);
- 把文件位置设置成文件末尾 lseek(fd,0,SEEK_END);
- 确定当前的文件位置 lseek(fd,0,SEEK_CUR);
实验代码
在程序中使用lseek函数移动文件读写位置,代码在配套资料“iTOP-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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-STM32MP157开发板网盘资料汇总\09_嵌入式Linux开发指南(iTOP-STM32MP157)手册配套资料\系统编程配套程序\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!运行结果如下图所示: