當(dāng)前位置:首頁 > 嵌入式 > 嵌入式教程
[導(dǎo)讀]通過編寫文件讀寫及上鎖的程序,進(jìn)一步熟悉Linux中文件I/O相關(guān)的應(yīng)用開發(fā),并且熟練掌握open()、read()、write()、fcntl()等函數(shù)的使用。

6.6實驗內(nèi)容6.6.1文件讀寫及上鎖1.實驗?zāi)康?p>通過編寫文件讀寫及上鎖的程序,進(jìn)一步熟悉Linux中文件I/O相關(guān)的應(yīng)用開發(fā),并且熟練掌握open()、read()、write()、fcntl()等函數(shù)的使用。

2.實驗內(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]='