当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]通过编写文件读写及上锁的程序,进一步熟悉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()函数实现具有以上功能的代码。

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

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭