之前在做DB服务器 的时候多线程 读写共享内存 由于某些平台 phtread_rwlock_ 系列是不支持进程间共享的属性, 而 fcntl() 的文件记录锁又不安全用作线程之间互斥,是所以在多进程和多线程混合混合的服务器模型中, 互斥锁一般只能用信号灯或信号量机制, 但这都只有一种状态而没有共享锁(读)和独占锁(写)的区分, 对于多读少写的情况不太舒服. 下面贴的代码是的实测平台为: FreeBSD-6.2, (正是freebsd不支持pthread_rwlockattr_set_pshared(attr, PTHREAD_PROCESS_SHARED ..) flock_*** 系列的说明, 它可以类似 pthread_rwlock_t 那样的使用, 但因为某些平台不支持 PTHREAD_PROCESS_SHARED 的属性, 所以才特意写了我这段代码通过 fcntl 记录锁来协调. 1) 数据类型: flock_t 2) 函数接口: (对于 int 型的函数成功统一返回 1, 失败返回 0) int flock_init(flock_t *fl, const char *fpath); // 初始化 fl 指向的 flock_t 数据结构, fpath 指定要锁定的文件路径, NULL 则由系统自动创建临时文件 int flock_set_thread_safe(fockt_t *fl); // 将 fl 设置为线程安全 (内部初始化类型为 pthread_rwlock_t 的 fl->plock) int flock_wrlock(flock_t *fl); // 取独占锁, 一般用于写 int flock_rdlock(flock_t *fl); // 取共享锁 int flock_unlock(flock_t *fl); // 解锁 void flock_destroy(flock_t *fl); // 释放切毁锁 3) 通用宏(参数均为要加锁或解锁的文件描述符, 这些宏不具备线程安全): FLOCK_WR_NB(fd); // 对 fd取独占锁(无阻塞,成功返回1,失败返回0) FLOCK_RD_NB(fd); // 对 fd取共享锁(无阻塞,成功返回1,失败返回0) FLOCK_WR(fd); // 对 fd取独占锁(若已被占则自动阻塞等待) FLOCK_RD(fd); // 对 fd取共享锁(若已被独占则自动等待) FLOCK_UN(fd); // 对 fd释放锁 1. 代码之 flock.h
#ifndef __FTPHP_FLOCK_20090530_H__ #define __FTPHP_FLOCK_20090530_H__ #ifdef __cplusplus extern "C" { #endif #include <fcntl.h> #include <sys/types.h> #include <pthread.h> typedef struct { int fd; int flag; pthread_rwlock_t plock; } flock_t; int flock_init(flock_t *fl, const char *fpath); // 1->ok, 0->failed int flock_set_thread_safe(flock_t *fl); // set to thread safe, 1->ok, 0->failed int flock_wrlock(flock_t *fl); // 1->ok, 0->failed int flock_rdlock(flock_t *fl); // 1->ok, 0->failed int flock_unlock(flock_t *fl); void flock_destroy(flock_t *fl); // destroy flock // error: 0, succ: 1 int flock_exec(int fd, int type, off_t offset, int whence, off_t len, int nonblock); #define FLOCK_WR_NB(fd) flock_exec(fd, F_WRLCK, 0, SEEK_SET, 0, 1) #define FLOCK_RD_NB(fd) flock_exec(fd, F_RDLCK, 0, SEEK_SET, 0, 1) #define FLOCK_WR(fd) flock_exec(fd, F_WRLCK, 0, SEEK_SET, 0, 0) #define FLOCK_RD(fd) flock_exec(fd, F_RDLCK, 0, SEEK_SET, 0, 0) #define FLOCK_UN(fd) flock_exec(fd, F_UNLCK, 0, SEEK_SET, 0, 0) #ifdef __cplusplus } #endif #endif | 2. 代码之 flock.c
/** flock $Id: $ */ #include "flock.h" #include <errno.h> #include <stdarg.h> #include <unistd.h> // error: 0, succ: 1 int flock_exec(int fd, int type, off_t offset, int whence, off_t len, int nonblock) { int rc; struct flock fl; if (fd < 0) return 0; fl.l_type = type; fl.l_start = offset; fl.l_whence = whence; fl.l_len = len; fl.l_pid = 0; do { rc = fcntl(fd, nonblock ? F_SETLK : F_SETLKW, &fl); } while (rc < 0 && errno == EINTR); return (rc == 0); } // init (fpath = NULL, auto assigment) int flock_init(flock_t *fl, const char *fpath) { // initilized? if (fl->flag & 0x01) flock_destroy(fl); if (fpath == NULL) { char mypath[] = "/tmp/flk.XXXXXX"; fl->fd = mkstemp(mypath); if (fl->fd >= 0) unlink(mypath); } else { fl->fd = open(fpath, O_CREAT | O_RDWR, 0600); } fl->flag = 0x01; return (fl->fd >= 0); } // thread safe int flock_set_thread_safe(flock_t *fl) { if (pthread_rwlock_init(&fl->plock, NULL) == 0) { fl->flag |= 0x02; return 1; } return 0; } // wrlock int flock_wrlock(flock_t *fl) { if (!(fl->flag & 0x02) || pthread_rwlock_wrlock(&fl->plock) == 0) return FLOCK_WR(fl->fd); return 0; } // rdlock int flock_rdlock(flock_t *fl) { if (!(fl->flag & 0x02) || pthread_rwlock_rdlock(&fl->plock) == 0) return FLOCK_RD(fl->fd); return 0; } // unlock int flock_unlock(flock_t *fl) { if (FLOCK_UN(fl->fd) && (!(fl->flag & 0x02) || pthread_rwlock_unlock(&fl->plock) == 0)) return 1; return 0; } // free flock only(not unlink the fl->fpath) void flock_destroy(flock_t *fl) { if (fl->flag & 0x02) pthread_rwlock_destroy(&fl->plock); close(fl->fd); fl->fd = -1; fl->flag = 0; } | 3. 代码之测试 编译方法: 1. 无线程安全: gcc -o test test.c flock.c -lpthread 2. 有线程安全: gcc -o test_ts -DTHR_SAFE test.c flock.c -lpthread 3. 分别执行 ./test 和 ./test_ts 查看区别
#include <stdio.h> #include <unistd.h> #include <sys/mman.h> #include "flock.h" #define THR_NUM 3 flock_t fl; int *shared_int; struct thr_arg { int index; char *ptype; }; // thread func void *thr_fn(void *arg) { struct thr_arg *a = (struct thr_arg *)arg; // get lock flock_wrlock(&fl); *shared_int = *shared_int + 1; sleep(1); printf("In %s-thread[%d]: shared_int=%d\n", a->ptype, a->index, *shared_int); fflush(stdout); flock_unlock(&fl); } int main() { pid_t pid = 0; struct thr_arg args[THR_NUM]; int i; // 匿名共享mmap shared_int = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); *shared_int = 0; // init the flock flock_init(&fl, NULL); #ifdef THR_SAFE flock_set_thread_safe(&fl); #endif //create child process pid = fork(); if (pid > 0) { pid = fork(); if (pid == 0) pid = 1; } // create 3 threads for (i = 0; i < THR_NUM; i++) { pthread_t tid; args[i].ptype = (pid == 0 ? "child" : pid == 1 ? "child2" : "parent"); args[i].index = i; pthread_create(&tid, NULL, thr_fn, &args[i]); pthread_detach(tid); } // 简单睡10秒让主线程最后退出, sleep(10); flock_destroy(&fl); munmap(shared_int, sizeof(int)); } | 执行结果可以看看 ./test 还存在变数, 而 ./test_ts 则完全正确. [hightman@d3 ~/]$ gcc -o test -DTHR_SAFE 2.c flock.c -lpthread [hightman@d3 ~/]$ ./test_ts In parent-thread[0]: shared_int=1 In child-thread[0]: shared_int=2 In parent-thread[1]: shared_int=3 In child-thread[1]: shared_int=4 In child-thread[2]: shared_int=5 In parent-thread[2]: shared_int=6 [hightman@d3 ~/]$ ./test_ts In parent-thread[1]: shared_int=1 In parent-thread[2]: shared_int=2 In child-thread[0]: shared_int=3 In parent-thread[0]: shared_int=4 In child-thread[2]: shared_int=5 In child-thread[1]: shared_int=6 [hightman@d3 ~/]$ gcc -o test 2.c flock.c -lpthread [hightman@d3 ~/]$ ./test In parent-thread[0]: shared_int=3 In parent-thread[2]: shared_int=3 In parent-thread[1]: shared_int=3 In child-thread[1]: shared_int=5 In child-thread[0]: shared_int=5 In child-thread[2]: shared_int=5 [hightman@d3 ~/]$ ./test In parent-thread[2]: shared_int=3 In parent-thread[0]: shared_int=3 In parent-thread[1]: shared_int=3 In child-thread[0]: shared_int=6 In child-thread[2]: shared_int=6 In child-thread[1]: shared_int=6 |