文件I/O编程之: 实验内容
扫描二维码
随时随地手机看文章
通过编写文件读写及上锁的程序,进一步熟悉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]='