文件锁flock
在linux 系统中,flock函数是为解决多进程对同一文件的读写冲突的,而flock函数只能锁定整个文件,无法锁定文件的某一区域。且flock可以保证robust,这也是我们选择它最终目的。
int flock(int fd,int operation);
- fd 文件描述
- operation 锁的模式
- LOCK_SH 建立共享锁定。多个进程可同时对同一个文件作共享锁定。
- LOCK_EX 建立互斥锁定。多个进程中一个文件同时只有一个互斥锁定。
- LOCK_UN 解除文件锁定状态。
- LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程。通常与LOCK_SH或LOCK_EX 做组合
- 单一文件无法同时建立共享锁定和互斥锁定,且当使用dup()或fork()时文件描述符也不会继承此种锁定。
- 返回值 返回0表示成功,若有错误则返回-1,错误代码存于errno。
代码实现
void MMIPC::setData(const string &key, const string &value) { // AutoMutex autoMutex(m_mutex_lock); // 上跨进程写锁 flock(m_fd, LOCK_EX); string content = key + ":" + value + ","; // ALOGD("setData content=%s", content.c_str()); size_t numberOfBytes = content.length(); if (m_position + numberOfBytes > m_file_size) { auto msg = "m_position: " + to_string(m_position) + ", numberOfBytes: " + to_string(numberOfBytes) + ", m_file_size: " + to_string(m_file_size); throw out_of_range(msg); } m_position = strlen(m_ptr); memcpy(m_ptr + m_position, (void *) content.c_str(), numberOfBytes); // ALOGD("setData success m_ptr.len=%d", m_position + numberOfBytes); // 解锁 flock(m_fd, LOCK_UN); }
运行效果
2022-07-24 12:54:32.259 10191-10191/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: set data 2022-07-24 12:54:32.521 10221-10221/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: init 2022-07-24 12:54:32.523 10221-10221/com.zzy.mmipc D/MMIPC->: root dir /data/user/0/com.zzy.mmipc/files 2022-07-24 12:54:32.524 10221-10221/com.zzy.mmipc D/MMIPC->: open success for mutex: fd[76], /data/user/0/com.zzy.mmipc/files/default_mutex.ipc 2022-07-24 12:54:32.524 10221-10221/com.zzy.mmipc E/MMIPC->: pid[10221] 2022-07-24 12:54:32.524 10221-10221/com.zzy.mmipc D/MMIPC->: open m_fd[76], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc 2022-07-24 12:54:32.524 10221-10221/com.zzy.mmipc D/MMIPC->: m_fd size[4194304] 2022-07-24 12:54:32.524 10221-10221/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 4194304, default_mmap_size 4194304 2022-07-24 12:54:32.524 10221-10221/com.zzy.mmipc D/MMIPC->: mmap success 2022-07-24 12:54:44.677 10221-10221/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: 2796540 2022-07-24 12:55:52.774 10191-10191/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: 3600000
怎么是36万?不应该是18万么,是的,因为我跑了两遍,一遍不放心不是,万一出问题呢。接着之前的经验,为了避免自己手欠无法正常的配对使用锁,我们封装下文件锁flock,如下
// // Created by 张占永 on 2022/7/24. // #include <fcntl.h> #include <sys/file.h> #include <unistd.h> #include <cerrno> #include "AndroidLog.h" #ifndef MMIPC_FILELOCK_H #define MMIPC_FILELOCK_H enum LockType { SharedLockType, ExclusiveLockType, }; class FileLock { int m_fd; bool isFileLockValid() { return m_fd >= 0; } public: explicit FileLock(int fd); bool lock(LockType lockType); bool try_lock(LockType lockType); bool unlock(); // just forbid it for possibly misuse explicit FileLock(const FileLock &other) = delete; FileLock &operator=(const FileLock &other) = delete; class Autolock { public: inline Autolock(FileLock &flock, LockType lockType) : mFLock(flock), m_lockType(lockType) { mFLock.lock(m_lockType); } inline Autolock(FileLock *flock, LockType lockType) : mFLock(*flock), m_lockType(lockType) { mFLock.lock(m_lockType); } inline ~Autolock() { mFLock.unlock(); } private: FileLock &mFLock; LockType m_lockType; }; }; inline FileLock::FileLock(int fd) : m_fd(fd) { } inline bool FileLock::lock(LockType lockType) { if (!isFileLockValid()) { return false; } int res = flock(m_fd, lockType == SharedLockType ? LOCK_SH : LOCK_EX); if (res == -1) { ALOGD("flock lock fail: fd[%d]", m_fd); } return res == 0; } inline bool FileLock::try_lock(LockType lockType) { if (!isFileLockValid()) { return false; } int res = flock(m_fd, lockType == SharedLockType ? LOCK_SH | LOCK_NB : LOCK_EX | LOCK_NB); if (res == -1) { ALOGD("flock try_lock fail: fd[%d]", m_fd); } return res == 0; } inline bool FileLock::unlock() { if (!isFileLockValid()) { return false; } int res = flock(m_fd, LOCK_UN); if (res == -1) { ALOGD("flock unlock fail: fd[%d]", m_fd); } return res == 0; } typedef FileLock::Autolock AutoFileLock; #endif //MMIPC_FILELOCK_H
然后调用如下
void MMIPC::setData(const string &key, const string &value) { // AutoMutex autoMutex(m_mutex_lock); // 加文件锁 AutoFileLock autoFileLock(m_file_lock, ExclusiveLockType); string content = key + ":" + value + ","; // ALOGD("setData content=%s", content.c_str()); size_t numberOfBytes = content.length(); if (m_position + numberOfBytes > m_file_size) { auto msg = "m_position: " + to_string(m_position) + ", numberOfBytes: " + to_string(numberOfBytes) + ", m_file_size: " + to_string(m_file_size); throw out_of_range(msg); } m_position = strlen(m_ptr); memcpy(m_ptr + m_position, (void *) content.c_str(), numberOfBytes); // ALOGD("setData success m_ptr.len=%d", m_position + numberOfBytes); }
运行一下,看看如何
2022-07-24 13:36:29.715 28670-28670/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: init 2022-07-24 13:36:29.716 28670-28670/com.zzy.mmipc D/MMIPC->: root dir /data/user/0/com.zzy.mmipc/files 2022-07-24 13:36:29.716 28670-28670/com.zzy.mmipc D/MMIPC->: open success for mutex: fd[75], /data/user/0/com.zzy.mmipc/files/default_mutex.ipc 2022-07-24 13:36:29.716 28670-28670/com.zzy.mmipc E/MMIPC->: pid[28670] 2022-07-24 13:36:29.717 28670-28670/com.zzy.mmipc D/MMIPC->: open m_fd[75], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc 2022-07-24 13:36:29.717 28670-28670/com.zzy.mmipc D/MMIPC->: m_fd size[0] 2022-07-24 13:36:29.717 28670-28670/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 0, default_mmap_size 4194304 2022-07-24 13:36:29.717 28670-28670/com.zzy.mmipc D/MMIPC->: mmap success 2022-07-24 13:36:29.929 28670-28670/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: set data 2022-07-24 13:36:30.414 28737-28737/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: init 2022-07-24 13:36:30.415 28737-28737/com.zzy.mmipc D/MMIPC->: root dir /data/user/0/com.zzy.mmipc/files 2022-07-24 13:36:30.415 28737-28737/com.zzy.mmipc D/MMIPC->: open success for mutex: fd[76], /data/user/0/com.zzy.mmipc/files/default_mutex.ipc 2022-07-24 13:36:30.415 28737-28737/com.zzy.mmipc E/MMIPC->: pid[28737] 2022-07-24 13:36:30.415 28737-28737/com.zzy.mmipc D/MMIPC->: open m_fd[76], /data/user/0/com.zzy.mmipc/files/default_mmap.ipc 2022-07-24 13:36:30.415 28737-28737/com.zzy.mmipc D/MMIPC->: m_fd size[4194304] 2022-07-24 13:36:30.415 28737-28737/com.zzy.mmipc D/MMIPC->: getFileSize m_file_size 4194304, default_mmap_size 4194304 2022-07-24 13:36:30.415 28737-28737/com.zzy.mmipc D/MMIPC->: mmap success 2022-07-24 13:36:38.311 28737-28737/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc:childProcess 日志: 1463520 2022-07-24 13:36:51.944 28670-28670/com.zzy.mmipc D/MMIPC->: 进程:com.zzy.mmipc 日志: 1800000
好的,18万,完美。是不是可结束了?其实光有flock是不行的,还需要线程同步,这里又用到了mutex的线程锁,可以往上翻一翻看下,今天就到此结束。
总结
多线程如何保持结果一致性,我们做了两种实现方式:
- Looper 实现
- mutex 互斥锁
多进程如何保持结果一致性,我们做了三种:
- Semaphores 找了大量的示例,没有测试成功,抱歉
- mutext 实现了跨进程的互斥锁,但它无法保证robust,意思是在进程出现异常死掉时,无法释放锁,导致其他进程无法再次获取,从而产生不可预估的问题
- flock 实现了跨进程的文件锁,但需要配合mutex线程锁来解决多线程问题
总之,目前为止,我们搞定了mmap的跨线程以及跨进程同步的问题,接下来就可以考虑如何设计传输的数据结构,下期我们来用pb做载体,体验pb的魅力。