文件I/O編程之: 實驗內(nèi)容
在Linux中FIFO是一種進(jìn)程之間的管道通信機(jī)制。Linux支持完整的FIFO通信機(jī)制。
本實驗內(nèi)容比較有趣,通過使用文件操作,仿真FIFO(先進(jìn)先出)結(jié)構(gòu)以及生產(chǎn)者-消費(fèi)者運(yùn)行模型。
本實驗中需要打開兩個虛擬終端,分別運(yùn)行生產(chǎn)者程序(producer)和消費(fèi)者程序(customer)。此時兩個進(jìn)程同時對同一個文件進(jìn)行讀寫操作。因為這個文件是臨界資源,所以可以使用文件鎖機(jī)制來保證兩個進(jìn)程對文件的訪問都是原子操作。
先啟動生產(chǎn)者進(jìn)程,它負(fù)責(zé)創(chuàng)建仿真FIFO結(jié)構(gòu)的文件(其實是一個普通文件)并投入生產(chǎn),就是按照給定的時間間隔,向FIFO文件寫入自動生成的字符(在程序中用宏定義選擇使用數(shù)字還是使用英文字符),生產(chǎn)周期以及要生產(chǎn)的資源數(shù)通過參數(shù)傳遞給進(jìn)程(默認(rèn)生產(chǎn)周期為1s,要生產(chǎn)的資源數(shù)為10個字符)。
后啟動的消費(fèi)者進(jìn)程按照給定的數(shù)目進(jìn)行消費(fèi),首先從文件中讀取相應(yīng)數(shù)目的字符并在屏幕上顯示,然后從文件中刪除剛才消費(fèi)過的數(shù)據(jù)。為了仿真FIFO結(jié)構(gòu),此時需要使用兩次復(fù)制來實現(xiàn)文件內(nèi)容的偏移。每次消費(fèi)的資源數(shù)通過參數(shù)傳遞給進(jìn)程,默認(rèn)值為10個字符。
3.實驗步驟(1)畫出實驗流程圖。
本實驗的兩個程序的流程圖如圖6.4所示。
圖6.4節(jié)流程圖
(2)編寫代碼。
本實驗中的生產(chǎn)者程序的源代碼如下所示,其中用到的lock_set()函數(shù)可參見第6.3.2節(jié)。
/*producer.c*/
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include"mylock.h"
#defineMAXLEN10/*緩沖區(qū)大小最大值*/
#defineALPHABET1/*表示使用英文字符*/
#defineALPHABET_START'a'/*頭一個字符,可以用'A'*/
#defineCOUNT_OF_ALPHABET26/*字母字符的個數(shù)*/
#defineDIGIT2/*表示使用數(shù)字字符*/
#defineDIGIT_START'0'/*頭一個字符*/
#defineCOUNT_OF_DIGIT10/*數(shù)字字符的個數(shù)*/
#defineSIGN_TYPEALPHABET/*本實例選用英文字符*/
constchar*fifo_file="./myfifo";/*仿真FIFO文件名*/
charbuff[MAXLEN];/*緩沖區(qū)*/
/*功能:生產(chǎn)一個字符并寫入仿真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:/*數(shù)字字符*/
{
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;/*生產(chǎn)周期*/
inttime_life=10;/*需要生產(chǎn)的資源數(shù)*/
if(argc>1)
{/*第一個參數(shù)表示生產(chǎn)周期*/
sscanf(argv[1],"%d",&time_step);
}
if(argc>2)
{/*第二個參數(shù)表示需要生產(chǎn)的資源數(shù)*/
sscanf(argv[2],"%d",&time_life);
}
while(time_life--)
{
if(product()<0)
{
break;
}
sleep(time_step);
}
exit(EXIT_SUCCESS);
}
本實驗中的消費(fèi)者程序的源代碼如下所示。
/*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";/*臨時文件名*/
/*資源消費(fèi)函數(shù)*/
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);/*消費(fèi)就是在屏幕上簡單的顯示*/
counter++;
}
fputs("n",stdout);
close(fd);
return0;
}
/*功能:從sour_file文件的offset偏移處開始
將count個字節(jié)數(shù)據(jù)復(fù)制到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;
}
/*功能:實現(xiàn)FIFO消費(fèi)者*/
intcustom(intneed)
{
intfd;
/*對資源進(jìn)行消費(fèi),need表示該消費(fèi)的資源數(shù)目*/
customing(fifo_file,need);
if((fd=open(fifo_file,O_RDWR))<0)
{
printf("Functionmyfilecopyerrorinsource_file:");
return-1;
}
/*為了模擬FIFO結(jié)構(gòu),對整個文件內(nèi)容進(jìn)行平行移動*/
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)/*第一個參數(shù)指定需要消費(fèi)的資源數(shù)目,默認(rèn)值為10*/
{
sscanf(argv[1],"%d",&customer_capacity);
}
if(customer_capacity>0)
{
custom(customer_capacity);
}
exit(EXIT_SUCCESS);
}
(3)先在宿主機(jī)上編譯該程序,如下所示:
$makeclean;make
(4)在確保沒有編譯錯誤后,交叉編譯該程序,此時需要修改Makefile中的變量
CC=arm-linux-gcc/*修改Makefile中的編譯器*/
$makeclean;make
(5)將生成的可執(zhí)行程序下載到目標(biāo)板上運(yùn)行。
4.實驗結(jié)果此實驗在目標(biāo)板上的運(yùn)行結(jié)果如下所示。實驗結(jié)果會和這兩個進(jìn)程運(yùn)行的具體過程相關(guān),希望讀者能具體分析每種情況。下面列出其中一種情況:
終端一:
$./producer120/*生產(chǎn)周期為1s,需要生產(chǎn)的資源數(shù)為20個*/
Writelocksetby21867
Releaselockby21867
Writelocksetby21867
Releaselockby21867
……
終端二:
$./customer5/*需要消費(fèi)的資源數(shù)為5個*/
Enjoy:abcde/*消費(fèi)資源,即打印到屏幕上*/
Writelocksetby21872/*為了仿真FIFO結(jié)構(gòu),進(jìn)行兩次復(fù)制*/
Releaselockby21872
在兩個進(jìn)程結(jié)束之后,仿真FIFO文件的內(nèi)容如下:
$catmyfifo
fghijklmnopqr/*a~e的5個字符已經(jīng)被消費(fèi),就剩下后面15個字符*/
6.6.2多路復(fù)用式串口操作1.實驗?zāi)康?p>通過編寫多路復(fù)用式串口讀寫,進(jìn)一步理解多路復(fù)用函數(shù)的用法,同時更加熟練掌握Linux設(shè)備文件的讀寫方法。2.實驗內(nèi)容本實驗主要實現(xiàn)兩臺機(jī)器(宿主機(jī)和目標(biāo)板)之間的串口通信,每臺機(jī)器都可以發(fā)送和接收數(shù)據(jù)。除了串口設(shè)備名稱不同(宿主機(jī)上使用串口1:/dev/ttyS0,而在目標(biāo)板上使用串口2:/dev/ttyS1),兩臺機(jī)器上的程序基本相同。
3.實驗步驟(1)畫出流程圖
如圖6.5所示為程序流程圖,兩臺機(jī)器上的程序使用同樣的流程圖。
圖6.5宿主機(jī)/目標(biāo)板程序的流程圖
(2)編寫代碼。
編寫宿主機(jī)和目標(biāo)板上的代碼,在這些程序中用到的open_port()和set_com_config()函數(shù)請參照6.4節(jié)。這里只列出宿主機(jī)上的代碼。
/*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;
/*將從串口讀取的數(shù)據(jù)寫入這個文件中*/
if((recv_fd=open(RECV_FILE_NAME,O_CREAT|O_WRONLY,0644))<0)
{
perror("open");
return1;
}
fds[0]=STDIN_FILENO;/*標(biāo)準(zhǔn)輸入*/
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);
/*讀取標(biāo)準(zhǔn)輸入或者串口設(shè)備文件*/
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]='