进入Linux世界
Linux系统编程
前言
裸机的误区:。
一、Linux系统编程
linux的核心思想:linux中一切皆文件。
1 Linux基础知识
对linux系统编程之前,需要熟悉linux编程的一些常规操作,以下内容主要针对基础小白,补充linux的一些基础知识点。
1.1 Linux用户
linux中有个超级用户,为root,可操作和访问所有的文件。
linux其他用户的创建和删除操作:创建用户,采用useradd ×××;查看用户,采用 cat /etc/passwd;删除用户,采用userdel ×××。linux也和Windows相类似,也有相应的用户组,关于用户组的操作,与用户操作相类似:添加用户组: groupadd ×××;查看用户组 :cat /etc/passwd;删除用户组:groupdel ×××。
1.2 Linux权限
关于linux下文件权限的描述:定义所有者、所属组以及其他人对文件的读、写和执行的权限。linux权限表的含义如下图所示:
修改linux文件权限的命令:chmod,在修改文件权限的时候,一般是将各类所有者的权限值(8进制值)累加进行赋值,如chmod 777 表示所有者、所属组以及所有的用户都有读、写、可执行的权限。
1.3 Linux路径
在linux下查找文件,需要cd到文件所属的目录路径下,linux提供了两种路径,一种是绝对路径,一种是相对路径。
绝对路径:绝对路径是从“/”目录开始的,“/”也称为根目录。
相对路径:相对路径一般是从“.”开始的,或者“../../”开头的路径,或者路径前没有“/”。表示的是相对于用户“当前路径”下的目录位置。因此,退出目录的时候,可用命令 :cd ../或 cd ../../,这种方法比较高效。
查看绝对路径命令:pwd
2 Linux文件 I/O操作
I/O操作即一般的输入输出操作,为直接IO操作,包含了打开、创建、读、写操作等内容。注:打开的文件描述符一定要记得关闭。
2.1 open操作
open函数的两个原型
int open(const char* Path, int oflags);
int open(const char* Path, int oflags, mode_t mode);
其中,第1个参数Path表示:路径名或文件名,路径名为绝对路径;第2个参数 oflags 表示:打开文件所采取的的动作。第3个参数mode表示:设置创建文件的权限。
打开文件的动作有:
- O_RDONLY 文件只读
- O_WRONLY 文件只写
- O_RDWR 文件可读可写 (以上三个参数中必须选择其中一个)(以下是可选的)
- O_APPEND 每次写操作都写入文件的末尾
- O_CREAT 若指定文件不存在,则创建该文件
- O_EXCL 若创建的文件已存在,则返回-1,并修改errno值
- O_TRUNC 若文件存在,且以只写/读写方式打开,则清空文件全部内容
- O_NOCTTY 若路径名指向终端设备,不把把该设备作为此进程的控制终端
- O_NDELAY 非阻塞方式操作文件
- O_NONBLOCK 若路径名指向FIFO/块文件、字符文件。则把文件的打开和打开成功后的I/O操作,都设置为非阻塞模式。
使用open函数会返回一个文件句柄,文件句柄是文件的唯一识别符ID,对文件的操作必须从读取句柄开始。
打开文件的例子:open.c
//标准输入输出头文件
#include <stdio.h>
//文件操作函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main(){
int fd; //句柄
char *leds = "/dev/leds"; led灯的设备节点
char *test1 = "/bin/test1";
char *test2 = "/bin/test2";
if((fd = open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0){ 以可读写、不把该程序作为控制终端、无延时模式打开
printf("open %s failed!\n",leds);
}
printf("\n%s fd is %d\n",leds,fd);
if((fd = open(test1,O_RDWR,0777))<0){
printf("open %s failed!\n",test1);
}
printf("%s fd is %d\n",test1,fd);
if((fd = open(test2,O_RDWR|O_CREAT,0777))<0){
printf("open %s failed!\n",test2);
}
printf("%s fd is %d\n",test2,fd);
}
2.2 creat操作
creat函数的原型
int creat(const char* Pathname, mode_t mode);
其中,第1个参数Path表示:路径名或文件名,路径名为绝对路径;第2个参数mode表示:设置创建文件的权限。
创建文件的例子:creat.c
//标准输入输出头文件
#include <stdio.h>
//文件操作函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main()
{
int fd;
//开发板中已经存在/dev/leds文件
char *leds = "/dev/leds";
//开发板中不存在的文件/bin/test1
char *test1 = "/bin/test1";
//开发板中不存在的文件/bin/test2
char *test2 = "/bin/test2";
//需要新建的文件/bin/test3
char *test3 = "/bin/test3";
//使用open函数打开文件
if((fd = open(leds, O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s failed\n",leds);
}
printf("%s fd is %d\n",leds,fd);
//使用open函数打开不存在的文件,不添加O_CREAT标识符,会报错
if((fd = open(test1, O_RDWR))<0){
printf("open %s failed\n",test1);
}
//打开文件创建文件,添加标志位O_CREAT表示不存在这个文件则创建文件
if((fd = open(test2, O_RDWR|O_CREAT,0777))<0){
printf("open %s failed\n",test2);
}
printf("%s fd is %d\n",test2,fd);
fd = creat(test3,0777);
if(fd = -1){
printf("%s fd is %d\n",test3,fd);
}
else{
printf("create %s is succeed\n",test3);
}
}
2.3 write操作
write函数的原型
ssize_t write(int fd, const void* buf, size_t count);
其中,第1个参数fd表示:使用open函数打开文件,返回的句柄;第2个参数 *buf表示:写入的数据;第三个参数count表示:最多写入字节数。
该函数的返回值:当返回值为-1时,表示出错;当返回值为其它值时,表示实际写入的字节数。
写操作的例子:write.c
//标准输入输出头文件
#include <stdio.h>
//文件操作函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
main()
{
int fd;
char *testwrite = "/bin/testwrite";
ssize_t length_w;
char buffer_write[] = "Hello Write Function!";
if((fd = open(testwrite, O_RDWR|O_CREAT,0777))<0){
printf("open %s failed\n",testwrite);
}
//将buffer写入fd文件
length_w = write(fd,buffer_write,strlen(buffer_write));
if(length_w == -1)
{
perror("write");
}
else{
printf("Write Function OK!\n");
}
close(fd);
}
2.4 read操作
read函数的原型
ssize_t read(int fd, const void* buf, size_t len);
其中,第1个参数fd表示:使用open函数打开文件,返回的句柄;第2个参数 *buf表示:读出的数据保存的位置;第三个参数count表示:每次最多读len个字节数。
该函数的返回值:出错返回-1;执行成功后返回实际读取值。
读操作的例子:read.c
//标准输入输出头文件
#include <stdio.h>
//文件操作函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define MAX_SIZE 1000
main(){
int fd;
ssize_t length_w,length_r = MAX_SIZE,ret;
char *testwrite = "/bin/testwrite";
char buffer_write[] = "Hello Write Function!";
char buffer_read[MAX_SIZE];
if((fd = open(testwrite,O_RDWR|O_CREAT,0777))<0){
printf("open %s failed!\n",testwrite);
}
length_w = write(fd,buffer_write,strlen(buffer_write));
if(length_w == -1){
perror("write");
}
else{
printf("Write Function OK!\n");
}
close(fd); //先写再读
if((fd = open(testwrite,O_RDWR|O_CREAT,0777))<0){
printf("open %s failed!\n",testwrite);
}
if(ret = read(fd,buffer_read,length_r)){
perror("read");
}
printf("Files Content is %s \n",buffer_read);
close(fd);
}
3 字符设备控制
首要需要明确一点,什么是字符设备。字符设备指的是只能按一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据按先后设备。常见的字符设备有鼠标、键盘、串口、led等,在此处涉及的字符设备主要为led灯、蜂鸣器、数模转换灯设备。
每一个字符设备在/dev目录下都有一个设备节点,可通过设备节点来使用驱动程序操作字符设备。
3.1 main函数传参
main函数作为应用程序的入口,完整定义为:
int main (int argc, char **argv)
其中,argc表示参数的个数,**argv表示存储输入字符的数组。argv[0] 表示程序名称,argv[1] - argv[n]表示输入的参数。
当不传入参数的时候,main函数定义为:
int main(void)
main函数传参的例子:argvc.c
#include<stdio.h>
#include<string.h>
//argument count变元计数
//argument value变元值
int main(int argc,char *argv[])
{
int i,j;
i = atoi(argv[1]);
j = atoi(argv[2]);
printf("the Program name is %s\n",argv[0]);
printf("The command line has %d argument:\n",argc-1);
printf("%d,%d\n",i,j);
return 0;
}
3.2 led灯控制
控制led之前,需要补充一些知识点:led灯的设备节点在/dev目录下。以及LED灯的硬件原理:
KP_C0L0和VDD50_EN网络高电平,三极管导通,led小灯就亮,低电平,则断路,小灯就灭。
对于硬件IO口的操作,linux专门设计了高效的ioctl函数,ioctl函数是设备驱动程序中对设备的 I/O 通道进行管理,即对设备的一些特性进行控制。函数原型为:
int ioctl(int fd, int request, int cmd)
fd含义同前;request表示led对应的IO口;cmd表示对IO口进行了什么样的操作。该函数的返回值为0时,表示成功;返回值为-1时,表示出错。
测试小灯实验:leds.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LED_NUM 2
#define LED_C 2
//cmd为0,则灭,为1,则亮;
//io为0则是靠近蜂鸣器的小灯,为1则靠近独立按键的小灯
int main(int argc,char *argv[])
{
int fd,led_num,led_c;
char *leds = "/dev/leds";
led_num = LED_NUM;
led_c = LED_C;
printf("argv1 is cmd;argv2 is io \n");
//对传入的参数进行判断,超出范围直接退出
if (atoi(argv[1]) >= led_c) { 字符串转换成整型数
printf("argv1 is 0 or 1)");
exit(1);
}
if (atoi(argv[2]) >= led_num) {
printf("argv2 is 0 or 1)");
exit(1);
}
//使用ioctl函数将参数传入内核
if((fd = open(leds, O_RDWR|O_NOCTTY|O_NDELAY))<0)
printf("open %s failed\n",leds);
else{
ioctl(fd,atoi(argv[1]),atoi(argv[2]));
printf("ioctl %s success\n",leds);
}
close(fd);
return(1);
}
3.3 蜂鸣器控制
蜂鸣器的设备节点也在/dev目录下。以及蜂鸣器BeeP的硬件原理:
若网络MOTOR_PWM为高电平,则导通,蜂鸣器响。
测试蜂鸣器实验:buzzertest.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define BUZZER_C 2
int main(int argc,char *argv[]){
char *buzzer_ctl = "/dev/buzzer_ctl";
int fd,ret,buzzer_c;
buzzer_c = BUZZER_C;
if(atoi(argv[1]) >= buzzer_c ){
printf("argv[1] is 0 or 1\n");
exit(1);
}
if((fd = open(buzzer_ctl,O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s failed\n",buzzer_ctl);
exit(1);
}
ret = ioctl(fd,atoi(argv[1]),atoi(argv[2]));
close(fd);
return 0;
}
3.4 ADC数模转换控制
数模的设备节点也在/dev目录下。数模转换器的原理其实是华东变阻器,滑动变阻器最大阻值为R13,1和2之间的电阻R12,电压Vadc=R12*VDD1V8_EXT/R13,1.8V对应的是10k欧姆,对应的寄存器数值为0xffff,0V 对应的是0 欧姆,对应的寄存器数值为0x0。
若网络MOTOR_PWM为高电平,则导通,蜂鸣器响。
测试ADC实验:ADC.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdint.h>
#include <termios.h>
//#include <android/log.h>
//#include <sys/ioctl.h>
int main(void)
{
int fd;
char *adc = "/dev/adc";
char buffer[512];
int len=0, r=0;
memset(buffer,0,sizeof(buffer)); buffer初始化
printf("adc ready!\n");
if((fd = open(adc, O_RDWR|O_NOCTTY|O_NDELAY))<0)
printf("open adc err!\n");
else{
printf("open adc success!\n");
len=read(fd,buffer,10);
if(len == 0)
printf("return null\n");
else{
r = atoi(buffer);
r = (int)(r*10000/4095); //Datas transition to Res
printf("res value is %d\n",r);
}
}
}
4 串口编程
4.1 串口编程的概念
串口通信是指一次只传一个数据位,虽然通信时串口有8位或9位,但在物理层面传输,仍然是以单个bit方式传输。串口一般指的是标准的RS232接口。关于RS232接口,有A,B,C三种,其中最广泛使用的是RS-232C。RS232的针脚分为25针和8针。
关于流控的概念:分为软件流控制和硬件流控制。其中软件流控制。
linux串口编程的流程为:开始 → 打开串口 → 初始化串口 → 发送和接收数据 → 关闭。
(1)打开串口,一般使用open函数,打开之后返回句柄;
(2)初始化串口,配置波特率,数据位,校验位等;
(3)发送和接收数据,使用read和write函数;
(4)关闭,close函数。
4.2 开机启动(Hello world)
打开vi编辑器,使用命令vi 、etc/init.d/rcS 打开文件系统的启动文件,在文件的最后一行,添加“/bin/hello world &” 。
4.3 打开串口
在4412开发板中,设备节点使用的是ttySAC*系列,即ttySAC 0, ttySAC 1,ttySAC 2,ttySAC 3。精英版靠近耳机和麦克风的串口对应的设备节点为“ttySAC3”
打开、关闭串口的例程:uartopen.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void main(){
int fd;
char *uart3 = "/dev/ttySAC3";
if((fd = open(uart3,O_RDWR|O_CREAT,0777))<0){
printf("open %s failed!\n",uart3);
}
else{
printf("open %s is success!\n",uart3);
}
close(fd);
}
4.4 串口初始化
使用source insight打开内核源码,串口的初始化最终要将参数传递到内核中,搜索termios.h文件。打开“arch\arm\include\asm”目录下“termioss.h”头文件。
termio结构体的定义:
tcflag_t c_iflag:输入模式标志
tcflag_t c_oflag:输出模式标志
tcflag_t c_cflag:控制模式标志
tcflag_t c_lflag:本地模式标志
cc_tc_line:line discipline
1.串口初始化函数
(1)tcgetattr:
用来读取当前串口参考值,一般用于先确认该串口是够能够配置,做检测用。
头文件:# include <termios.h> 和 # include <unistd.h>
函数原型:
int tcgetattr(int fd, struct termios * termios_p)
参数1表示文件句柄,参数2 termios_p是结构体。
(2)波特率相关的函数
函数cfsetispeed和cfsetospeed用于修改串口的波特率。函数cfgetispeed和cfgetospeed可用于获取当前的波特率。头文件处于 # include <termios.h> 和 # include <unistd.h>中。
- cfsetispeed函数原型:
int cfsetispeed (struct termios *termios_p, speed_t speed);
参数1 *termios_p是结构体,参数2:speed 波特率,常用B2400,B4800,B9600,B115200,B460800。运行成功返回0,运行失败则返回-1。
- cfsetospeed函数原型:
int cfsetospeed (struct termios *termios_p, speed_t speed);
同上。
获取波特率的函数:
- cfgetispeed函数原型:
speed_t cfgetispeed(const struct termios *termios_p)
该函数用于读取当前串口输入的波特率,返回值为speed_t。
- cfgetospeed函数原型:
speed_t cfgetospeed(const struct termios *termios_p)
该函数用于读取当前串口输出的波特率,返回值为speed_t。
(3)tcflush:
用于清空串口中没有完成的输入或输出数据。在接收或发送数据时,串口寄存器会缓存数据,这个函数用于清除这些数据。
函数原型:
int tcflush(int fd, int queue _selector)
参数1:fd是open返回的文件句柄;
参数2:控制tcflush的操作。
控制tcflush的操作:
- TCIFUSH :清除正收到的数据,且不会读取出来;
- TCOFLUSH :清除正写入的数据,且不会发送至终端;
- TCIOFLUSH :清除所有正在发生的 I/O数据
(4)tcsetattr:
设置参数的函数
函数原型:
int tcsetattr (int fd, int optional_actions, const struct termios *termios_p);
参数1:fd,句柄
参数2:optinal_actions 是参数生效的时间;
参数3:*termios_p在旧的参数基础上修改后的参数,(一般在初始化最后使用这个函数)。
optional_actions操作种类:
- TCSANOW :不等数据传输完毕就立即改变属性;
- TCSADRAID :等待所有数据传输结束才改变属性;
- TCSAFLUSH :清空输入输出缓冲区才能改变属性。
2.串口初始化流程
串口初始化代码:
//定义一个初始化函数
int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop);
//定义结构体 newtio 和 oldtio
struct termios newtio, oldtio;
//判断串口是否能够配置
if(tcgetattr(fd,&oldtio)!=0)
{
perror("Setup Serial 1"); //保存原先串口设置
return -1; //调试成功,返回0,失败,返回-1
}
//将newtio清零,设置标志位c_cflag
bzero(&newtio, sizeof(newtio));
newtio.c_cflag 1 = CLLCAL | CREAD ; 本地连接|接收使能
newtio.c_cflag & = ~ CSIZE; 数据位的位掩码,通过位掩码的方式激活这两个选项
//设置停止位
switch(nBits)
{
case 7:
newtio.c_cflag 1 = CS7; (7个数据位)
break;
case 8:
newtio.c_cflag 1 = CS8; (8个数据位)
break;
}
//设置奇偶校验位
switch(nEvent)
{
case 'O': (使能校验)
newtio.c_cflag 1 = PARENB; 校验位使能
newtio.c_cflag 1 = PAROOD; 使用奇校验而不使用偶校验
newtio.c_iflag 1 = (INPCK | ISTRIP) 奇偶校验使能|除去奇偶校验位
break;
case 'E': (使能偶校验)
newtio.c_iflag 1 = (INPCK | ISTRIP);
newtio.c_cflag 1 = PARENB;
newtio.c_cflag &= ~PARODD; 先取反再做与运算
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
//设置波特率(cfsetispeed 和 cfsetospeed)
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400); 输入速度
cfsetospeed(&newtio, B2400); 输出速度
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
//设置停止位
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
//设置最少字符和等待时间
newtio.c_cc[VTIME] = 0; 指定了读取每个字符的等待时间
newtio.c_cc[VMIN] = 0; 指定最少读取的字符数
//刷清输入缓存
tcflush(fd, TCIFLUSH);
//激活配置
if((tcsetattr(fd, TCSANON, &, newtio))! 0) 改变的配置立即生效
{
perror("com set error");
return -1;
}
4.5 串口发送
使用write函数即可实现,三个参数分别是句柄,传输的buffer以及传输的长度。
串口发送例程代码:uartwrite.c
#include <stdio.h> //标准输入输出定义
#include <string.h> //字符串函数定义
#include <unistd.h> //标准函数unix定义
#include <sys/types.h>
#include <sys/stat.h>
#include <fcnt1.h> //文件控制定义
#include <termios> //PPSIX终端控制定义
#include <errno.h> //错误号定义
//实现串口打印hello world十次
//采用靠近声卡旁的串口
//初始化函数
int set_opt(int, int, int , char, int) //试用前先声明
void main()
{
int fd, wr_static, i = 10;
char *uart 3 = "/dev/ttySAC3"; ttySAC代表开发板上的串口
char * buffer = "hello world !\n";
printf("\r\n itop4412 uart3 writetest start \r\n");
//打开串口,可读写,不将该设备作为作为此进程的控制终端,非阻塞操作
if((fd = open(uart3, O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s is failed",uart3);
}
else{
printf("open %s is success\n",uart3);
set_opt(fd, 115200, 8, 'N', 1); 设置串口的参数
while(i--)
{
wr_static = write(fd,buffer, strlen(buffer));
if(wr_static<0)
printf("write failed\n");
else{
printf("wr_static is %d\n",wr_static);
}
sleep(1);
}
}
close(fd);
}
编译例程的操作步骤:
- 编译成可执行文件
- 开发板接两个串口,一个是控制台的超级终端,一个是靠近声卡的串口
- 电脑端开启串口助手,设置与代码中相一致的参数
- 开机,挂载U盘,运行可执行文件,在串口助手终端可看到打印信息,10次hello world,说明串口输出成功。
若电脑端只有1个串口,编译例程的操作步骤又如下:
- 在超级终端下,将uartwrite拷贝到/bin目录下,然后修改权限 chmod 777
- 修改开机启动的脚本文件,添加“/bin/uartwrite &”,保存退出,关掉开发板
- 将串口线接到耳机旁的串口座,打开串口助手
4.6 串口接收
使用read函数即可实现,在之前的 I/O 操作已有介绍。
串口接收例程代码:uartread.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int set_opt(int,int,int,char,int);
//"/dev/ttySAC3"是con2,靠近耳机接口的串口
void main()
{
int fd,nByte;
char *uart3 = "/dev/ttySAC3";
char buffer[512]; 定义一个字符串数组,接收数据
char *uart_out = "please input\r\n";
memset(buffer, 0, sizeof(buffer)); buffer初始化
if((fd = open(uart3, O_RDWR|O_NOCTTY))<0)
printf("open %s is failed",uart3);
else{
set_opt(fd, 115200, 8, 'N', 1); 初始化串口
write(fd,uart_out, strlen(uart_out));
//循环接收串口发送过来的数据,并发送出去
while(1){
while((nByte = read(fd, buffer, 512))>0){
buffer[nByte+1] = '\0'; 表示字符串结束
write(fd,buffer,strlen(buffer)); 计算buffer中字符个数
memset(buffer, 0, strlen(buffer)); 清空buffer中数据
nByte = 0;
}
}
}
}
编译例程的操作步骤:
- 编译成可执行文件
- 只用一个串口,所有设置,保存方法同写文件相同,只是文件的名字不同
- 电脑端开启串口助手,设置与代码中相一致的参数
- 开机,挂载U盘,运行可执行文件,在串口助手终端可看到“please input”,输入“hello world”,点击发送,返回信息“helloread”,说明串口接收成功。
5 网络编程
5.1 TFTP服务器搭建和测试
为实现上位机与开发板在线传输文件,在Ubuntu上搭建一个服务器,通过网络传输文件到开发板。
(1)Ubuntu 服务端搭建
TFTP(在线传输协议),专为小文件传输。首先 ping baidu.com,利用 control + c 暂停命令,可得 ping 的结果,然后下载安装:
sudo apt-get install xinetd
sudo apt-get install tftp tftpd
然后建立TFTP的配置文件,使用命令: vi /etc/xinetd.d/tftp 建立文件:
service tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin /in.tftpd
server_args = -s/var/tftpboot/
disable = no
per_source = 11
cps = 100 2
flags = IPv4
}
其中server_args设置的/var/tftpboot目录是tftp服务器的目录,TFTP客户端就是从这个目录获取服务器上的文件,使用 mkdir /var/tftpboot 建立tftp服务器的目录。然后设置方位权限为777,输入重启命令:
sudo /etc/init.d/xinetd restart
(2)服务器测试
- 在/var/tftpboot目录下新建文件test,输入…
- 启动另一个终端,输入tftp 127.0.0.1
- 输入get test,获取到test 文件
- 输入q退出tftp
- ls查询该文件是否存在当前目录下
5.2 TFTP服务器与开发板的文件传输
(1)设置同一网段IP地址
首先需要补充一个知识点,关于同一网段。两个网络IP是否在同一网段的计算方法:
两个IP分别和相应的子网掩码做 “与” 运算,得到的结果相同,则表明在同一网段。 |
注:进行与运算需要先转换为二进制。比如如下计算过程:
Ubuntu 系统:
IP :11010011 01000111 00001100 10011001
MASK :11111111 11111111 11111111 11111110
Result :11010011 01000111 00001100 10011000
Hex : 211 71 12 152
开发板:
IP :11000000 10101000 00000001 11100110
MASK :11111111 11111111 11111111 00000000
Result :11000000 10101000 00000001 00000000
Hex : 192 168 1 0
根据计算可知,两个IP不在同一网段,因此开发板需要重新修改IP地址。编辑IP地址的脚本文件在文件系统的 etc/eth0-setting文件下,
(2)Ping通TFTP服务器
Ubuntu上服务器的IP地址是 211.71.12.153
(3)从服务器获取文件
tftp -g -l test -r test 211.71.12.153
其中test是文件名,可以是文件包,也可以是编译后的可执行文件。
5.3 NFS网络文件系统
NFS的全称为 Network File System,即网络文件系统,主要应用于linux系统之间资源的共享。
本质思想是:linux A路径下,share文件夹,linux B可利用挂载机制,将linux A系统的share文件夹挂到linux B中,从而实现资源的共享。
设置的原则是:PC上Ubuntu的ip和iTop4412上的ip在同一网段。
搭建NFS服务器的步骤:
(1) 选择Ubuntu作为主机;
(2)Ubuntu上安装NFS服务器:
apt-get install nfs-kernel-server
(3)配置:
vi /etc/exports 添加:
/home/ topeet/linux/*(rw,sync,no_root_squash)
其中:
*:表示允许所有的网络端访问;
rw:表示可读写权限;
sync:表示资料同步写入内存和硬盘;
no_root_squash:是Ubuntu nfs客户端分享目录使用者的权限,若客户端使用的是root用户,那么对于该目录共享而言,该客户端就具有root权限。
(4)重启portmap服务器:
/etc/init.d/portmap restart
(5)重启nfs:
/etc/init.d/nfs_kernel_server restart
NFS共享目录的使用步骤:
(1)查看开发板ip:192.168.1.103
(2)查看NFS服务器ip:192.168.1.104
(3)ping通网络:ping 192.168.1.104 -c 3
(4)在开发板/mnt 目录下,新建 “mkdir nfs”,作为开发板NFS同步目录:
(5)“mount -t nfs -o nolock 192.168.1.104 : /home/minilinux/minilinux /mnt/nfs ” 挂载nfs服务器
(6)在虚拟机系统上, /home/minilinux/minilinux (NFS服务器目录)使用命令 “vim hello” 新建文档
(7)在开发板上查看到相应的信息
(8)开发板和虚拟机完成同步
注:在经开发板内烧写内核镜像时,USB_fastboot_tool中不能放两个内核镜像,每次都要执行所有的烧写命令,以防开发板不能启动。