背景
sysrepo
是基于共享内存的数据库,实际共享内存的外在体现是一个共享的文件。对这个共享文件的访问控制又是通过文件锁的方式来实现的。因为sysrepo
只是一个库而已,会被多个进程链接,共享内存文件的路径都是一致的,在编译的时候确定了的。多进程间的资源互斥这里使用的就是文件锁的方式。
实现
sysrepo
对数据的存储主要分为两块,一个是main shm
,用来保存概要的数据sr_main_shm_t
和模块的信息数组sr_mod_t
(大部分保存模块对应信息的一个偏移)。另一个是ext shm
,用来保存具体的模块对应的数据信息。
其中对main shm
的资源资源控制使用的是一个sr_main
的文件锁来保护。同样的,对ext shm
的资源资源控制使用的是一个sr_ext
的文件锁来保护。
今天主要介绍一下main shm
的文件锁sr_main
的控制。
接口
涉及的锁接口主要有三个:
1 |
|
这里shm_lock
实际上是锁文件的文件描述符fd。
锁文件创建
1 |
|
这里获取到文件的路径,设置创建文件属性的掩码,然后使用open
打开文件,如果文件不存在,创建文件并使用指定的访问属性。
加锁操作
1 |
|
对文件加解锁使用了一个结构体struct flock
,定义一些文件的锁的选项。还有fcntl
这个函数来设置定义的选项。
1 | int fcntl(int filedes, int cmd, ... /* struct flock *flockptr */ ); |
这是一个拥有可变参数的函数声明,filedes自然是要操作的文件描述符,对与记录锁相关的操作,cmd只能是F_GETLK, F_SETLK, 或者 F_SETLKW,而第三个参数则必须是一个指向flock结构体的指针。
1 | struct flock { |
第一个成员是加锁的类型:只读锁,读写锁,或是解锁。l_start
和l_whence
用来指明加锁部分的开始位置,l_len
是加锁的长度,l_pid
是加锁进程的进程id。比如说,我们现在需要把一个文件的前三个字节加读锁,则该结构体的l_type=F_RDLCK
, l_start=0
, l_whence=SEEK_SET
, l_len=3
,l_pid
不需要指定,然后调用fcntl
函数时,cmd
参数使用F_SETLK
.
回到sr_shmmain_createlock
函数, 设置了读写锁类型F_WRLCK
,cmd
为F_SETLKW
,然后就调用fcntl
加锁了。
解锁操作
1 |
|
这里设置cmd
为F_SETLK
, l_type
类型为解锁。
demo编程
我写了一个demo
程序,2个程序分别往一个共享内存写数据,对一个数进行累加。一个步进1, 加10次。 一个步进2,加10次。当两个程序结束后,都延迟10s钟,分别再读取数据,最终的结果应该是我们认为的30。
demo
代码路径:
https://github.com/fishmwei/blog_code/tree/master/sysrepo/filelock
行动,才不会被动!
欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。