1.實(shí)驗(yàn)?zāi)康?/p>
通過編寫文件讀寫及上鎖的程序,進(jìn)一步熟悉Linux中文件I/O相關(guān)的應(yīng)用開發(fā),并且熟練掌握open()、read()、write()、fcntl()等函數(shù)的使用。
2.實(shí)驗(yàn)內(nèi)容
在Linux中FIFO是一種進(jìn)程間的管道通信機(jī)制,Linux支持完整的FIFO通信機(jī)制。
本實(shí)驗(yàn)內(nèi)容比較有趣,我們通過使用文件操作,仿真FIFO(先進(jìn)先出)結(jié)構(gòu)及生產(chǎn)者—消費(fèi)者運(yùn)行模型。
本實(shí)驗(yàn)中需要打開兩個(gè)虛擬終端,分別運(yùn)行生產(chǎn)者程序(producer)和消費(fèi)者程序(customer),此時(shí)兩個(gè)進(jìn)程同時(shí)對同一個(gè)文件進(jìn)行讀寫操作。因?yàn)檫@個(gè)文件是臨界資源,所以可以使用文件鎖機(jī)制來保證兩個(gè)進(jìn)程對文件的訪問都是原子操作。
先啟動(dòng)生產(chǎn)者進(jìn)程,它負(fù)責(zé)創(chuàng)建仿真FIFO結(jié)構(gòu)的文件(其實(shí)是一個(gè)普通文件)并投入生產(chǎn),就是按照給定的時(shí)間間隔,向FIFO文件寫入自動(dòng)生成的字符(在程序中用宏定義選擇使用數(shù)字還是使用英文字符),生產(chǎn)周期及要生產(chǎn)的資源數(shù)通過參數(shù)傳遞給進(jìn)程(默認(rèn)生產(chǎn)周期為1s,要生產(chǎn)的資源總數(shù)為10個(gè)字符,顯然默認(rèn)生產(chǎn)總時(shí)間為10s)。
后啟動(dòng)的消費(fèi)者進(jìn)程按照給定的數(shù)目進(jìn)行消費(fèi),首先從文件中讀取相應(yīng)數(shù)目的字符并在屏幕上顯示,然后從文件中刪除剛才消費(fèi)過的數(shù)據(jù)。為了仿真FIFO結(jié)構(gòu),此時(shí)需要使用兩次復(fù)制來實(shí)現(xiàn)文件內(nèi)容的偏移。每次消費(fèi)的資源數(shù)通過參數(shù)傳遞給進(jìn)程,默認(rèn)值為10個(gè)字符。
3.實(shí)驗(yàn)步驟
(1)畫出實(shí)驗(yàn)流程圖。
本實(shí)驗(yàn)的兩個(gè)程序的流程圖如圖2.5所示。
 圖2.5 文件讀寫及上鎖實(shí)驗(yàn)流程圖
(2)編寫代碼。
本實(shí)驗(yàn)中的生產(chǎn)者程序的源代碼如下所示,其中用到的lock_set()函數(shù)可參見后面章節(jié)。
/* producer.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "mylock.h"
#define MAXLEN 10 /* 緩沖區(qū)大小大值 */
#define ALPHABET 1 /* 表示使用英文字符 */
#define ALPHABET_START 'a' /* 頭一個(gè)字符,可以用 'A' */
#define COUNT_OF_ALPHABET 26 /* 字母字符的個(gè)數(shù) */
#define DIGIT 2 /* 表示使用數(shù)字字符 */
#define DIGIT_START '0' /* 頭一個(gè)字符 */
#define COUNT_OF_DIGIT 10 /* 數(shù)字字符的個(gè)數(shù) */
#define SIGN_TYPE ALPHABET /* 本實(shí)例選用英文字符 */
const char *fifo_file = "./myfifo";/* 仿真FIFO文件名 */
char buff[MAXLEN]; /* 緩沖區(qū) */
/* 功能:生產(chǎn)一個(gè)字符并寫入到仿真FIFO文件中 */
int product(void)
{
int fd;
unsigned int sign_type, sign_start, sign_count, size;
static unsigned int counter = 0;
/* 打開仿真FIFO文件 */
if ((fd = open(fifo_file, O_CREAT|O_RDWR|O_APPEND, 0644)) < 0)
{
printf("Open fifo file error\n");
exit(1);
}
sign_type = SIGN_TYPE;
switch(sign_type)
{
case ALPHABET:/* 英文字符 */
{
sign_start = ALPHABET_START;
sign_count = COUNT_OF_ALPHABET;
}
break;
case DIGIT:/* 數(shù)字字符 */
{
sign_start = DIGIT_START;
sign_count = COUNT_OF_DIGIT;
}
break;
default:
{
return -1;
}
}/*end of switch*/
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: write error\n");
return -1;
}
lock_set(fd, F_UNLCK); /* 解鎖 */
close(fd);
return 0;
}
int main(int argc ,char *argv[])
{
int time_step = 1; /* 生產(chǎn)周期 */
int time_life = 10; /* 需要生產(chǎn)的資源總數(shù) */
if (argc > 1)
{/* 第一個(gè)參數(shù)表示生產(chǎn)周期 */
sscanf(argv[1], "%d", &time_step);
}
if (argc > 2)
{/* 第二個(gè)參數(shù)表示需要生產(chǎn)的資源數(shù) */
sscanf(argv[2], "%d", &time_life);
}
while (time_life--)
{
if (product() < 0)
{
break;
}
sleep(time_step);
}
exit(EXIT_SUCCESS);
}
本實(shí)驗(yàn)中的消費(fèi)者程序的源代碼如下所示:
/* customer.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAX_FILE_SIZE 100 * 1024 * 1024 /* 100M */
const char *fifo_file = "./myfifo"; /* 仿真FIFO文件名 */
const char *tmp_file = "./tmp"; /* 臨時(shí)文件名 */
/* 資源消費(fèi)函數(shù) */
int customing(const char *myfifo, int need)
{
int fd;
char buff;
int counter = 0;
if ((fd = open(myfifo, O_RDONLY)) < 0)
{
printf("Function customing error\n");
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);
return 0;
}
/* 功能:從sour_file文件的offset偏移處開始,
將count字節(jié)數(shù)據(jù)復(fù)制到dest_file文件 */
int myfilecopy(const char *sour_file,
const char *dest_file, int offset, int count, int copy_mode)
{
int in_file, out_file;
int counter = 0;
char buff_unit;
if ((in_file = open(sour_file, O_RDONLY|O_NONBLOCK)) < 0)
{
printf("Function myfilecopy error in source file\n");
return -1;
}
if ((out_file = open(dest_file, O_CREAT|O_RDWR|O_TRUNC|O_NONBLOCK, 0644)) < 0)
{
printf("Function myfilecopy error in destination file:");
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);
return 0;
}
/* 功能:實(shí)現(xiàn)FIFO消費(fèi)者 */
int custom(int need)
{
int fd;
/* 對資源進(jìn)行消費(fèi),need表示該消費(fèi)的資源數(shù)目 */
customing(fifo_file, need);
if ((fd = open(fifo_file, O_RDWR)) < 0)
{
printf("Function myfilecopy error in source_file:");
return -1;
}
/* 為了模擬FIFO結(jié)構(gòu),對整個(gè)文件內(nèi)容進(jìn)行平行移動(dòng) */
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);
return 0;
}
int main(int argc ,char *argv[])
{
int customer_capacity = 10;
if (argc > 1) /* 第一個(gè)參數(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ī)上編譯該程序,如下所示:
$ make clean; make
(4)在確保沒有編譯錯(cuò)誤后,交叉編譯該程序,此時(shí)需要修改Makefile中的變量。
CC = arm-linux-gcc /* 修改Makefile中的編譯器 */
$ make clean; make
(5)將生成的可執(zhí)行程序下載到目標(biāo)板上運(yùn)行。
4.實(shí)驗(yàn)結(jié)果
此實(shí)驗(yàn)在目標(biāo)板上的運(yùn)行結(jié)果如下所示。實(shí)驗(yàn)結(jié)果會(huì)和這兩個(gè)進(jìn)程運(yùn)行的具體過程相關(guān),希望讀者能具體分析每種情況,下面列出其中一種情況。
終端一:
$ ./producer 1 20 /* 生產(chǎn)周期為1s,需要生產(chǎn)的資源總數(shù)為20個(gè) */
Write lock set by 21867
Release lock by 21867
Write lock set by 21867
Release lock by 21867
…
終端二:
$ ./customer 5 /* 需要消費(fèi)的資源數(shù)為5個(gè) */
Enjoy:abcde /* 對資源進(jìn)行消費(fèi),即打印到屏幕上 */
Write lock set by 21872 /* 為了仿真FIFO結(jié)構(gòu),進(jìn)行兩次復(fù)制 */
Release lock by 21872
在兩個(gè)進(jìn)程結(jié)束后,仿真FIFO文件的內(nèi)容如下:
$ cat myfifo
fghijklmnopqr /* a到e的5個(gè)字符已經(jīng)被消費(fèi),就剩下后面15個(gè)字符 */
本文選自華清遠(yuǎn)見嵌入式培訓(xùn)教材《從實(shí)踐中學(xué)嵌入式Linux應(yīng)用程序開發(fā)》
熱點(diǎn)鏈接:
1、linux 文件鎖的實(shí)現(xiàn)及其應(yīng)用
2、嵌入式Linux串口應(yīng)用編程之串口讀寫
3、Linux文件系統(tǒng)之虛擬文件系統(tǒng)
4、標(biāo)準(zhǔn)I/O操作函數(shù)詳解
5、標(biāo)準(zhǔn)I/O操作的緩沖存儲(chǔ)類型
更多新聞>> |