在Linux中,实现文件上锁的函数有lockf() 和fcntl() ,其中lockf()用于对文件施加建议性锁,而fcntl() 不仅可以施加建议性锁,而且可以施加强制性锁。fcntl()还能对文件的某一记录上锁,也就是记录锁。记录锁又可分读取锁(共享锁)和写入锁(排斥锁),文件的同一部分不能同时建立读取锁和写入锁。
- #include <stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/wait.h>
- #include <sys/types.h>
- #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;
- }
- #include <stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/wait.h>
- #include <sys/types.h>
- #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;
- }