分类: C/C++
2010-12-31 00:22:12
今天实现了一个使用文件锁的程序,使用的函数是lockf。实现过程是:主进程创建十个子进程,每个子进程会读取同一个文件中的第一行数据,并将读取的数据转换成整型然后加一在写回文件的开头部分。首先通过echo 0 > /tmp/count 向文件count写入0,在没有使用文件锁的情况下,程序执行完,cat /tmp/count 的结果会小于10。当加了文件锁后结果为10,并每执行一次就会累加10/。
这里我实现了两个程序,程序一是在子进程中open的这个共同文件,程序二是在创建子进程之前的父进程中open的文件。
程序一:
#include
#include
#include
#include
#include
#define PATH "/tmp/count"
static void* add_fun(void)
{
FILE* fd;//这里使用文件指针是因为在使用fprintf时只能使用文件指针,同时文件指针可以通过fileno函数实现文件描述符的转换。
char buf[256];
fd = fopen(PATH, "r+");
if (fd == NULL){
perror("open()");
pthread_exit(NULL);//为什么使用这个??
}
lockf(fileno(fd), F_LOCK, 0);//在读取文件的数据之前加锁,而不是在打开文件之前加锁。因为文件是在每个子进程中打开的,所以每个子进程都有自己的inode。Open的过程就是创建属于当前进程文件的inode结构体。所以不用考虑inode的互斥问题。理论上互斥的代码段越短程序的性能越好。
fgets(buf, 256, fd);//通过调用fgets函数获取文件的第一行数据。
usleep(3);//这里睡眠是因为可以使得在没有使用文件锁的时候累加结果不准确更容易产生。
lseek(fileno(fd), 0, SEEK_SET);//记得在写入之前要将文件的当前位置回复到文件的开头。
fprintf(fd, "%d\n", atoi(buf) + 1);//注意从文件读取的数据都是字符型的,可以通过atoi函数将其转换成int型。Fprintf函数有时很有用的。
fflush(fd);//注意这里要对文件进行刷新,只有这样才能在下面的解锁之前保证数据真正的写入了磁盘文件中。当然也可以把fcolse放在解锁之前,这时就不用fflush了。
lockf(fileno(fd), F_ULOCK, 0);
fclose(fd);
return NULL;
}
int main(void)
{
int fd;
pid_t pid[10];
int i;
for(i = 0; i < 10; i++){
pid[i] = fork();
if (pid[i] == 0){
add_fun();
return 0;//子进程执行到这就退出了。
}
}
for(i = i; i < 10; i++){
wait(NULL);
}
return 0;
}
程序二:
#include
#include
#include
#include
#include
#define PATH "/tmp/count"
FILE *fd;//将文件指针定义为全局变量。
static void* add_fun(void)
{
char buf[256];
lockf(fileno(fd), F_LOCK, 0);//同样在获取数据之前加锁
fgets(buf, 256, fd);
usleep(1);
lseek(fileno(fd), 0, SEEK_SET);//写入之前使文件指针回复到文件头
fprintf(fd, "%d\n", atoi(buf) + 1);
fflush(fd);
lseek(fileno(fd), 0, SEEK_SET);//注意这里在关闭文件之前比程序一多了一次将文件当前指针回复到文件头的操作,如果没有此操作会出错。因为所有的子进程共用了一个inode,同时文件当前位置的信息就在inode中,所以如果这里不回复的话,其它子进程在读取的时候就不会从文件的头读取了。
lockf(fileno(fd), F_ULOCK, 0);
fclose(fd);//如果没有上面的fflush的话即使将fclose放在解锁之前也不可以。注意这里即使使用的是同一个inode,但要是进行close文件,如果当前close的文件不是最后一个引用的进程则只是将文件的引用计数减一。只有在close最后一个时才会释放inode。
return NULL;
}
int main(void)
{
pid_t pid[10];
int i;
fd = fopen(PATH, "r+");//在创建子进程之前打开的文件。子进程会继承父进程打开的文件。但需要注意的是,在整个程序中,只open了一次,所以在内存中只有一个inode结构体。
if (fd == NULL){
perror("open()");
pthread_exit(NULL);
}
for(i = 0; i < 10; i++){
pid[i] = fork();
if (pid[i] == 0){
add_fun();
return 0;
}
}
for(i = i; i < 10; i++){
wait(NULL);
}
return 0;
}
chinaunix网友2011-01-03 14:37:40
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com