fcntl+pthread_rwlock制作的支持多进程多线程混合的互斥锁

之前在做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 *= (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 (= 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值