其实本质上就是需要对文件加锁,最好是读写锁。读的时候加读锁,写的时候加写锁。
用完了就释放锁,嗯,基本操作了。
头文件 + 函数如下:
1 2 |
#include <sys/file.h> int flock(int fd, int operation); |
参数说明:
- LOCK_SH 建立共享锁定。多个进程可同时对同一个文件作共享锁定。
- LOCK_EX 建立互斥锁定。一个文件同时只有一个互斥锁定。
- LOCK_UN 解除文件锁定状态。
- LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程。通常与LOCK_SH或LOCK_EX 做OR(|)组合。
单一文件无法同时建立共享锁定和互斥锁定,而当使用dup()或fork()时文件描述词不会继承此种锁定。
返回值说明:
- 返回0表示成功
- 返回-1表示错误
- 错误代码存于errno.
flock()会依参数operation所指定的方式对参数fd所指的文件做各种锁定或解除锁定的动作。此函数只能锁定整个文件,无法锁定文件的某一区域。
另外需要注意的是,我们经常会用fopen打开函数,此时获得的是一个File*, 需要用fileno函数转换为fd.
这里实现了判断一个路径是文件、读取文件、写入文件方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 判断路径是文件 // [in] filePath, 文件路径 // [ret] 返回true标识是文件, 返回false标识不是或者文件不存在 bool IsFile(const std::string& filePath); // 快速读取文件内容 // [in] filePath, 文件路径 // [out] fileData, 文件内容 // [in, param] lock, 读取文件时加共享锁, 加锁失败返回失败 // [ret] 返回成功or失败 bool FastReadFile(const std::string &filePath, std::string &fileData, bool lock); // 写入文件内容 // [in] filePath, 文件路径 // [in] fileData, 写入内容 // [in, param] lock, 写入文件时加互斥锁, 加锁失败返回失败 // [ret] 返回写入成功or失败 bool WriteFile(const std::string &filePath, const std::string &fileData, bool lock); |
C++11代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
bool IsFile(const std::string &filePath) { // 判断一个路径是文件 struct stat st; if (0 == stat(filePath.c_str(), &st)) { if (st.st_mode & S_IFDIR) { return false; // 目录 } else if (st.st_mode & S_IFREG) { return true; // 文件 } } return false; } bool FastReadFile(const std::string &filePath, std::string &fileData, bool lock) { // buf的大小推荐4k或者1k都行, 一次性全读虽然快, 但是浪费内存 static constexpr long bufSize = 4096; // 判断给定的路径是一个文件 if (!IsFile(filePath)) { return false; } // 采用C方式读取文件信息, 比CPP的readline方式快N倍. FILE *pFile; if ((pFile = fopen(filePath.c_str(), "r")) == NULL) { return false; } // 共享锁/不阻塞 if (lock && flock(fileno(pFile), LOCK_SH | LOCK_NB) != 0) { fclose(pFile); return false; } // 计算文件大小 fseek(pFile, 0, SEEK_SET); long begin = ftell(pFile); fseek(pFile, 0, SEEK_END); long end = ftell(pFile); long fileSize = end - begin; fseek(pFile, 0, SEEK_SET); //重新指向文件头 // 预分配内存空间 fileData.reserve(fileSize + 1); // 读取文件内容 char readBuf[bufSize + 1]; long readSize = 0; while (readSize < fileSize) { long minRead = std::min(fileSize - readSize, bufSize); long len = fread(readBuf, 1, minRead, pFile); readSize += len; fileData.append(readBuf, len); } // 解锁 if (lock && flock(fileno(pFile), LOCK_UN) != 0) { fclose(pFile); return false; } fclose(pFile); return true; } bool WriteFile(const std::string &filePath, const std::string &fileData, bool lock) { FILE *pFile; if ((pFile = fopen(filePath.c_str(), "w")) == NULL) { return false; } // 互斥锁/不阻塞 if (lock && flock(fileno(pFile), LOCK_EX | LOCK_NB) != 0) { fclose(pFile); return false; } fwrite(fileData.c_str(), 1, fileData.length(), pFile); // 解锁 if (lock && flock(fileno(pFile), LOCK_UN) != 0) { fclose(pFile); return false; } fclose(pFile); return true; } |
以上是工程中用到的读写文件方法。
【C/C++】多进程同时读写一个文件