下面介绍文件锁以及系统调用和库函数之间的关系。
文件锁:
什么是文件锁?
文件锁就是给文件上了一把锁,在进程使用文件的时候加锁,在进程结束的时候解开。
为什么加文件锁?
多个进程在同时访问一个文件的时候,避免文件的错误。
文件锁分为:读锁(建议锁) 写锁(互斥锁)
锁的实现分为:建议锁 强制锁
下面介绍建议锁:
函数fcntl(2)
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
功能:操控文件描述符
参数:fd-文件描述符
cmd:
F_GETLK :测试是否可以加锁,如果可以加锁返回的lock的l_type字段设置为F_UNLCK。如果不能加锁,将hold这锁的进程 的pid 设置在lock的l_pid成员中
F_SETLK :给文件加锁的区域,添加或释放一把锁。如果在文件加锁的区域已经有程hold着一把互斥锁,里就返回-1.errno 被设置为EACCES 和 EAGAIN互斥锁
F_SETLKW:和F_SETLK一样,但是在另外进程hold着资源的时候,阻塞等待另外进程释放文件锁。
struct flock {
...
short l_type; /* Type of lock: F_RDLCK,
F_WRLCK, F_UNLCK */
short l_whence; /* How to interpret l_start:
SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* Starting offset for lock */
off_t l_len; /* Number of bytes to lock */
pid_t l_pid; /* PID of process blocking our lock
(F_GETLK only) */
...
}; 手册里有 man 2 fcntl
返回值:成功返回0 失败返回-1
演示代码如下(两个文件):
locka.c:
2 #include <unistd.h>
3 #include <fcntl.h>
4 int main(int argc,char *argv[]){
5 int fd=open(argv[1],O_RDONLY);
6 if(fd==-1){
7 perror("open");
8 return -1;
9 }
10 struct flock lock;
11 lock.l_type=F_RDLCK;
12 lock.l_whence=SEEK_SET;
13 lock.l_start=0;
14 lock.l_len=10;
15 fcntl(fd,F_SETLK,&lock);
16 getchar();
17 close(fd);
18 }
lockb.c
1 #include<stdio.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 int main(int argc,char *argv[]){
5 int fd=open(argv[1],O_WRONLY);
6 if(fd==-1){
7 perror("open");
8 return -1;
9 }
10 struct flock lock;
11 lock.l_type=F_WRLCK;
12 lock.l_whence=SEEK_SET;
13 lock.l_start=0;
14 lock.l_len=10;
15 fcntl(fd,F_SETLKW,&lock);
16 printf("lock success...\n");
17 close(fd);
18 }
先执行文件locka,运行之后因为getchar()原因会在当前等待下一步操作,这时候我们执行文件lockb.c(一个新窗口或者把locka.c 用Ctrl+z 进入后台),可以看到结果也会一直等待,他在等待我们文件locka.c运行完毕(F_SETLKW),当我们给locka.c运行结果一个任意操作,让他的结束。就会看到lockb.c输出
printf("lock success...\n")。
下面介绍系统调用和库函数之间的关系:
库函数,fopen(3) fclose(3) fputc(3) fgetc(3)
系统调用 open(2) close(2) read(2) write(2)
首先使用fopen的时候会定义一个FILE
typedef struct _IO_FILE FILE;
查看预编译文件可以看出实际上FILE是一个结构体(他的一部分成员如下截图)的变量的别名。 FILE *new 定义了一个结构体指针指向该结构体!
结构体会占用空间系统会分配空间给该结构体 FILE * new=malloc(sizeof(FILE))
缓存的读写区域的地址与结构体相应的成员一一对应。
结构体里的一个参数 new->文件描述符(fileno)=open() 在通过系统调用去打开一个文件
当我们要从文件得到数据的时候,这时候如果数据的缓存区域有数据,就会直接将数据返回。如果没有数据就会调用read()函数从文件中读取数据到缓存,然后在返回。 read(fd->fileno,buf(缓存),1024);
当我们要向文件中写入数据的时候,也会判断缓存区满了没,满了就调用write()函数将数据写入文件,这时候没满也会用fflush()将缓存区的内容刷新,强制调用write()函数,将内容写进文件。
我们要关闭文件的时候会先清理缓存,然后释放缓存,然后调用close()关闭文件,最后释放FILE对象所占的空间。