当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]通过编写文件读写及上锁的程序,进一步熟悉Linux中文件I/O相关的应用开发,并且熟练掌握open()、read()、write()、fcntl()等函数的使用。

6.6实验内容6.6.1文件读写及上锁1.实验目的

通过编写文件读写及上锁的程序,进一步熟悉Linux中文件I/O相关的应用开发,并且熟练掌握open()、read()、write()、fcntl()等函数的使用。

2.实验内容

在Linux中FIFO是一种进程之间的管道通信机制。Linux支持完整的FIFO通信机制。

本实验内容比较有趣,通过使用文件操作,仿真FIFO(先进先出)结构以及生产者-消费者运行模型。

本实验中需要打开两个虚拟终端,分别运行生产者程序(producer)和消费者程序(customer)。此时两个进程同时对同一个文件进行读写操作。因为这个文件是临界资源,所以可以使用文件锁机制来保证两个进程对文件的访问都是原子操作。

先启动生产者进程,它负责创建仿真FIFO结构的文件(其实是一个普通文件)并投入生产,就是按照给定的时间间隔,向FIFO文件写入自动生成的字符(在程序中用宏定义选择使用数字还是使用英文字符),生产周期以及要生产的资源数通过参数传递给进程(默认生产周期为1s,要生产的资源数为10个字符)。

后启动的消费者进程按照给定的数目进行消费,首先从文件中读取相应数目的字符并在屏幕上显示,然后从文件中删除刚才消费过的数据。为了仿真FIFO结构,此时需要使用两次复制来实现文件内容的偏移。每次消费的资源数通过参数传递给进程,默认值为10个字符。

3.实验步骤

(1)画出实验流程图。

本实验的两个程序的流程图如图6.4所示。

图6.4节流程图

(2)编写代码。

本实验中的生产者程序的源代码如下所示,其中用到的lock_set()函数可参见第6.3.2节。

/*producer.c*/

#include<stdio.h>

#include<unistd.h>

#include<stdlib.h>

#include<string.h>

#include<fcntl.h>

#include"mylock.h"

#defineMAXLEN10/*缓冲区大小最大值*/

#defineALPHABET1/*表示使用英文字符*/

#defineALPHABET_START'a'/*头一个字符,可以用'A'*/

#defineCOUNT_OF_ALPHABET26/*字母字符的个数*/

#defineDIGIT2/*表示使用数字字符*/

#defineDIGIT_START'0'/*头一个字符*/

#defineCOUNT_OF_DIGIT10/*数字字符的个数*/

#defineSIGN_TYPEALPHABET/*本实例选用英文字符*/

constchar*fifo_file="./myfifo";/*仿真FIFO文件名*/

charbuff[MAXLEN];/*缓冲区*/

/*功能:生产一个字符并写入仿真FIFO文件中*/

intproduct(void)

{

intfd;

unsignedintsign_type,sign_start,sign_count,size;

staticunsignedintcounter=0;

/*打开仿真FIFO文件*/

if((fd=open(fifo_file,O_CREAT|O_RDWR|O_APPEND,0644))<0)

{

printf("Openfifofileerrorn");

exit(1);

}

sign_type=SIGN_TYPE;

switch(sign_type)

{

caseALPHABET:/*英文字符*/

{

sign_start=ALPHABET_START;

sign_count=COUNT_OF_ALPHABET;

}

break;

caseDIGIT:/*数字字符*/

{

sign_start=DIGIT_START;

sign_count=COUNT_OF_DIGIT;

}

break;

default:

{

return-1;

}

}/*endofswitch*/

sprintf(buff,"%c",(sign_start+counter));

counter=(counter+1)%sign_count;

lock_set(fd,F_WRLCK);/*上写锁*/

if((size=write(fd,buff,strlen(buff)))<0)

{

printf("Producer:writeerrorn");

return-1;

}

lock_set(fd,F_UNLCK);/*解锁*/

close(fd);

return0;

}

intmain(intargc,char*argv[])

{

inttime_step=1;/*生产周期*/

inttime_life=10;/*需要生产的资源数*/

if(argc>1)

{/*第一个参数表示生产周期*/

sscanf(argv[1],"%d",&time_step);

}

if(argc>2)

{/*第二个参数表示需要生产的资源数*/

sscanf(argv[2],"%d",&time_life);

}

while(time_life--)

{

if(product()<0)

{

break;

}

sleep(time_step);

}

exit(EXIT_SUCCESS);

}

本实验中的消费者程序的源代码如下所示。

/*customer.c*/

#include<stdio.h>

#include<unistd.h>

#include<stdlib.h>

#include<fcntl.h>

#defineMAX_FILE_SIZE100*1024*1024/*100M*/

constchar*fifo_file="./myfifo";/*仿真FIFO文件名*/

constchar*tmp_file="./tmp";/*临时文件名*/

/*资源消费函数*/

intcustoming(constchar*myfifo,intneed)

{

intfd;

charbuff;

intcounter=0;

if((fd=open(myfifo,O_RDONLY))<0)

{

printf("Functioncustomingerrorn");

return-1;

}

printf("Enjoy:");

lseek(fd,SEEK_SET,0);

while(counter<need)

{

while((read(fd,&buff,1)==1)&&(counter<need))

{

fputc(buff,stdout);/*消费就是在屏幕上简单的显示*/

counter++;

}

fputs("n",stdout);

close(fd);

return0;

}

/*功能:从sour_file文件的offset偏移处开始

将count个字节数据复制到dest_file文件*/

intmyfilecopy(constchar*sour_file,

constchar*dest_file,intoffset,intcount,intcopy_mode)

{

intin_file,out_file;

intcounter=0;

charbuff_unit;

if((in_file=open(sour_file,O_RDONLY|O_NONBLOCK))<0)

{

printf("Functionmyfilecopyerrorinsourcefilen");

return-1;

}

if((out_file=open(dest_file,

O_CREAT|O_RDWR|O_TRUNC|O_NONBLOCK,0644))<0)

{

printf("Functionmyfilecopyerrorindestinationfile:");

return-1;

}

lseek(in_file,offset,SEEK_SET);

while((read(in_file,&buff_unit,1)==1)&&(counter<count))

{

write(out_file,&buff_unit,1);

counter++;

}

close(in_file);

close(out_file);

return0;

}

/*功能:实现FIFO消费者*/

intcustom(intneed)

{

intfd;

/*对资源进行消费,need表示该消费的资源数目*/

customing(fifo_file,need);

if((fd=open(fifo_file,O_RDWR))<0)

{

printf("Functionmyfilecopyerrorinsource_file:");

return-1;

}

/*为了模拟FIFO结构,对整个文件内容进行平行移动*/

lock_set(fd,F_WRLCK);

myfilecopy(fifo_file,tmp_file,need,MAX_FILE_SIZE,0);

myfilecopy(tmp_file,fifo_file,0,MAX_FILE_SIZE,0);

lock_set(fd,F_UNLCK);

unlink(tmp_file);

close(fd);

return0;

}

intmain(intargc,char*argv[])

{

intcustomer_capacity=10;

if(argc>1)/*第一个参数指定需要消费的资源数目,默认值为10*/

{

sscanf(argv[1],"%d",&customer_capacity);

}

if(customer_capacity>0)

{

custom(customer_capacity);

}

exit(EXIT_SUCCESS);

}

(3)先在宿主机上编译该程序,如下所示:

$makeclean;make

(4)在确保没有编译错误后,交叉编译该程序,此时需要修改Makefile中的变量

CC=arm-linux-gcc/*修改Makefile中的编译器*/

$makeclean;make

(5)将生成的可执行程序下载到目标板上运行。

4.实验结果

此实验在目标板上的运行结果如下所示。实验结果会和这两个进程运行的具体过程相关,希望读者能具体分析每种情况。下面列出其中一种情况:

终端一:

$./producer120/*生产周期为1s,需要生产的资源数为20个*/

Writelocksetby21867

Releaselockby21867

Writelocksetby21867

Releaselockby21867

……

终端二:

$./customer5/*需要消费的资源数为5个*/

Enjoy:abcde/*消费资源,即打印到屏幕上*/

Writelocksetby21872/*为了仿真FIFO结构,进行两次复制*/

Releaselockby21872

在两个进程结束之后,仿真FIFO文件的内容如下:

$catmyfifo

fghijklmnopqr/*a~e的5个字符已经被消费,就剩下后面15个字符*/

6.6.2多路复用式串口操作1.实验目的

通过编写多路复用式串口读写,进一步理解多路复用函数的用法,同时更加熟练掌握Linux设备文件的读写方法。

2.实验内容

本实验主要实现两台机器(宿主机和目标板)之间的串口通信,每台机器都可以发送和接收数据。除了串口设备名称不同(宿主机上使用串口1:/dev/ttyS0,而在目标板上使用串口2:/dev/ttyS1),两台机器上的程序基本相同。

3.实验步骤

(1)画出流程图

如图6.5所示为程序流程图,两台机器上的程序使用同样的流程图。

图6.5宿主机/目标板程序的流程图

(2)编写代码。

编写宿主机和目标板上的代码,在这些程序中用到的open_port()和set_com_config()函数请参照6.4节。这里只列出宿主机上的代码。

/*com_host.c*/

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<fcntl.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<errno.h>

#include"uart_api.h"

intmain(void)

{

intfds[SEL_FILE_NUM],recv_fd,maxfd;

charbuff[BUFFER_SIZE];

fd_setinset,tmp_inset;

structtimevaltv;

unsignedloop=1;

intres,real_read,i;

/*将从串口读取的数据写入这个文件中*/

if((recv_fd=open(RECV_FILE_NAME,O_CREAT|O_WRONLY,0644))<0)

{

perror("open");

return1;

}

fds[0]=STDIN_FILENO;/*标准输入*/

if((fds[1]=open_port(HOST_COM_PORT))<0)/*打开串口*/

{

perror("open_port");

return1;

}

if(set_com_config(fds[1],115200,8,'N',1)<0)/*配置串口*/

{

perror("set_com_config");

return1;

}

FD_ZERO(&inset);

FD_SET(fds[0],&inset);

FD_SET(fds[1],&inset);

maxfd=(fds[0]>fds[1])?fds[0]:fds[1];

tv.tv_sec=TIME_DELAY;

tv.tv_usec=0;

printf("Inputsomewords(enter'quit'toexit):n");

while(loop&&(FD_ISSET(fds[0],&inset)||FD_ISSET(fds[1],&inset)))

{

tmp_inset=inset;

res=select(maxfd+1,&tmp_inset,NULL,NULL,&tv);

switch(res)

{

case-1:

{

perror("select");

loop=0;

}

break;

case0:/*Timeout*/

{

perror("selecttimeout");

loop=0;

}

break;

default:

{

for(i=0;i<SEL_FILE_NUM;i++)

{

if(FD_ISSET(fds[i],&tmp_inset))

{

memset(buff,0,BUFFER_SIZE);

/*读取标准输入或者串口设备文件*/

real_read=read(fds[i],buff,BUFFER_SIZE);

if((real_read<0)&&(errno!=EAGAIN))

{

loop=0;

}

elseif(!real_read)

{

close(fds[i]);

FD_CLR(fds[i],&inset);

}

else

{

buff[real_read]='';

if(i==0)

{/*将从终端读取的数据写入串口*/

write(fds[1],buff,strlen(buff));

printf("Inputsomewords

(enter'quit'toexit):n");

}

elseif(i==1)

{/*将从串口读取的数据写入普通文件中*/

write(recv_fd,buff,real_read);

}

if(strncmp(buff,"quit",4)==0)

{/*如果读取为‘quit’则退出*/

loop=0;

}

}

}/*endofifFD_ISSET*/

}/*fori*/

}

}/*endofswitch*/

}/*endofwhile*/

close(recv_fd);

return0;

}

(3)接下来,将目标板的串口程序交叉编译,再将宿主机的串口程序在PC机上编译。

(4)连接PC的串口1和开发板的串口2。然后将目标板串口程序下载到开发板上,分别在两台机器上运行串口程序。

4.实验结果

宿主机上的运行结果如下所示:

$./com_host

Inputsomewords(enter'quit'toexit):

Hello,Target!

Inputsomewords(enter'quit'toexit):

I'mhostprogram!

Inputsomewords(enter'quit'toexit):

Byebye!

Inputsomewords(enter'quit'toexit):

quit/*这个输入使双方的程序都结束*/

从串口读取的数据(即目标板中发送过来的数据)写入同目录下的recv.dat文件中。

$catrecv.dat

Hello,Host!

I'mtargetprogram!

Byebye!

目标板上的运行结果如下所示:

$./com_target

Inputsomewords(enter'quit'toexit):

Hello,Host!

Inputsomewords(enter'quit'toexit):

I'mtargetprogram!

Inputsomewords(enter'quit'toexit):

Byebye!

与宿主机上的代码相同,从串口读取的数据(即目标板中发送过来的数据)写入同目录下的recv.dat文件中。

$catrecv.dat

Hello,Target!

I'mhostprogram!

Byebye!

Quit

请读者用poll()函数实现具有以上功能的代码。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

为了满足日益增长的数据处理需求,铁威马NAS推出了全新的性能巅峰2024年旗舰之作F4-424 Pro,并搭载了最新的操作系统--TOS 6。这款高效办公神器的问世,无疑将为企业和专业人士带来前所未有的便捷与效率。

关键字: 存储 Linux 服务器

双系统将是下述内容的主要介绍对象,通过这篇文章,小编希望大家可以对双系统的相关情况以及信息有所认识和了解,详细内容如下。

关键字: 双系统 Windows Linux

安装Linux操作系统并不复杂,下面是一个大致的步骤指南,以帮助您完成安装。1. 下载Linux发行版:首先,您需要从Linux发行版官方网站下载最新的ISO镜像文件。

关键字: Linux 操作系统 ISO镜像

计算机是由一堆硬件组成的,为了有限的控制这些硬件资源,于是就有了操作系统的产生,操作系统是软件子系统的一部分,是硬件基础上的第一层软件。

关键字: Linux 操作系统 计算机

Linux操作系统是一套免费使用和自由传播的类Unix操作系统,通常被称为GNU/Linux。它是由林纳斯·托瓦兹在1991年首次发布的,并基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。Lin...

关键字: Linux 操作系统

所谓进程间通信就是在不同进程之间传播或交换信息,它是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息;还可以让一个程序能够在同一时间里处理许多用户的需求。

关键字: Linux 进程通信 编程接口

串口通信作为一种最传统的通信方式,在工业自动化、通讯、控制等领域得到广泛使用。

关键字: Linux 串口通信 通讯

2023年11月16日: MikroElektronika(MIKROE) ,作为一家通过提供基于成熟标准的创新式硬软件产品来大幅缩短开发时间的嵌入式解决方案公司,今天宣布推出一款基于单线设备的软硬件开源解决方案Cli...

关键字: 嵌入式 Linux 操作系统

Linux是一种免费使用和自由传播的类Unix操作系统,其内核由林纳斯·本纳第克特·托瓦兹于1991年10月5日首次发布。它主要受到Minix和Unix思想的启发,是一个基于POSIX的多用户、多任务、支持多线程和多CP...

关键字: Linux 操作系统

本文中,小编将对嵌入式予以介绍,如果你想对它的详细情况有所认识,或者想要增进对它的了解程度,不妨请看以下内容哦。

关键字: 嵌入式 Linux
关闭
关闭