《UNIX网络编程 卷2》读书笔记(五)

简介:

{
    struct flock    lock;

    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;                /* write lock entire file */

    Fcntl(fd, F_SETLKW, &lock);
}

void my_unlock(int fd)
{
    struct flock    lock;

    lock.l_type = F_UNLCK;
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;                /* unlock entire file */

    Fcntl(fd, F_SETLK, &lock);
}


static void rwlock_cancelrdwait(void *arg)
{
    pthread_rwlock_t    *rw;

    rw = arg;
    rw->rw_nwaitreaders--;
    pthread_mutex_unlock(&rw->rw_mutex);
}
/* end rwlock_cancelrdwait */

int pthread_rwlock_rdlock(pthread_rwlock_t *rw)
{
    int        result;

    if (rw->rw_magic != RW_MAGIC)
        return(EINVAL);

    if ( (result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
        return(result);

        /* 4give preference to waiting writers */
    while (rw->rw_refcount < 0 || rw->rw_nwaitwriters > 0) {
        rw->rw_nwaitreaders++;
        pthread_cleanup_push(rwlock_cancelrdwait, (void *) rw);
        result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex);
        pthread_cleanup_pop(0);
        rw->rw_nwaitreaders--;
        if (result != 0)
            break;
    }
    if (result == 0)
        rw->rw_refcount++;        /* another reader has a read lock */

    pthread_mutex_unlock(&rw->rw_mutex);
    return (0);
}


static void rwlock_cancelwrwait(void *arg)
{
    pthread_rwlock_t    *rw;

    rw = arg;
    rw->rw_nwaitwriters--;
    pthread_mutex_unlock(&rw->rw_mutex);
}
/* end rwlock_cancelwrwait */

int pthread_rwlock_wrlock(pthread_rwlock_t *rw)
{
    int        result;

    if (rw->rw_magic != RW_MAGIC)
        return(EINVAL);

    if ( (result = pthread_mutex_lock(&rw->rw_mutex)) == -1)
        return(result);

    while (rw->rw_refcount != 0) {
        rw->rw_nwaitwriters++;
        pthread_cleanup_push(rwlock_cancelwrwait, (void *) rw);
        result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);
        pthread_cleanup_pop(0);
        rw->rw_nwaitwriters--;
        if (result != 0)
            break;
    }
    if (result == 0)
        rw->rw_refcount = -1;

    pthread_mutex_unlock(&rw->rw_mutex);
    return(0);
}


如果想让进程只有一个拷贝运行,可以维护一个文件,在进程运行时先对其记录上写锁,则其他拷贝就无法获取到写锁,也就无法运行其他拷贝。
#include    "unpipc.h"
#define    PATH_PIDFILE    "pidfile"
int main(int argc, char **argv)
{
    int        pidfd;
    char    line[MAXLINE];
        /* 4open the PID file, create if nonexistent */
    pidfd = Open(PATH_PIDFILE, O_RDWR | O_CREAT, FILE_MODE);

        /* 4try to write lock the entire file */
    if (write_lock(pidfd, 0, SEEK_SET, 0) < 0) {
        if (errno == EACCES || errno == EAGAIN)
            err_quit("unable to lock %s, is %s already running?",
                     PATH_PIDFILE, argv[0]);
        else
            err_sys("unable to lock %s", PATH_PIDFILE);
    }
        /* 4write my PID, leave file open to hold the write lock */
    snprintf(line, sizeof(line), "%ld\n", (long) getpid());
    Ftruncate(pidfd, 0);
    Write(pidfd, line, strlen(line));

    /* then do whatever the daemon does  */

    pause();
}

当然一个守护进程防止自身另一个拷贝的启动还有其他方法,比如可以使用信号灯。
      若以O_CREATE和O_EXCL来调用open函数,则若文件存在,就返回一个错误,我们可以用这个技巧来将文件作为锁用。
#include    "unpipc.h"

#define    LOCKFILE    "/tmp/seqno.lock"

void my_lock(int fd)
{
    int        tempfd;

    while ( (tempfd = open(LOCKFILE, O_RDWR|O_CREAT|O_EXCL, FILE_MODE)) < 0) {
        if (errno != EEXIST)
            err_sys("open error for lock file");
        /* someone else has the lock, loop around and try again */
    }
    Close(tempfd);            /* opened the file, we have the lock */
}

void my_unlock(int fd)
{
    Unlink(LOCKFILE);        /* release lock by removing file */
}



这种技巧有三个问题:
1,若持有该锁的进程没有释放锁就终止了,则此文件名没有被删除,对于这个问有一些办法,比如检查文件的最近访问时间,若大于某个阀值,则认定它已经被遗忘了。另一个办法是把持有锁的进程ID写入锁文件中,则其他进程可以读出该进程ID,并去检查进程是否还在运行,但这也有问题,因为进程ID在过一段时间后是会重用的。但这些情形对于fcntl记录上锁来说都不成问题,因为进程终止时,它持有的记录锁都自动释放。
2,若已经有另外一个进程打开了锁文件,则当前进程只是在一个无限循环中不断调用open,也就是轮询。一个替代技巧是sleep一下,再尝试open.当然若使用fcntl进行记录上锁,这不成问题,只要想持有锁的进程指定FSETLKW命令,那么内核将把该进程投入睡眠,直到锁可用再唤醒它。
3,调用open和unlink创建和删除额外一个文件涉及文件系统的访问,这比调用fcntl两次(一个获取锁,一次释放锁)所花时间长得多。
互斥锁和信号量的区别是:互斥锁必须总是由锁住它的线程来解锁,但信号量的signal却不必由执行过它的wait操作的同一个线程来执行。




本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2008/05/29/1210311.html,如需转载请自行联系原作者
目录
相关文章
|
7月前
|
存储 网络协议 Linux
《网络是怎么样连接的》读书笔记 - WEB服务端请求和响应(五)
《网络是怎么样连接的》读书笔记 - WEB服务端请求和响应(五)
69 0
|
7月前
|
域名解析 缓存 网络协议
《网络是怎么样连接的》读书笔记 - 认识网络基础概念(一)
《网络是怎么样连接的》读书笔记 - 认识网络基础概念(一)
195 1
|
7月前
|
缓存 负载均衡 网络协议
《网络是怎么样连接的》读书笔记 - 服务器端的局域网中(四)
《网络是怎么样连接的》读书笔记 - 服务器端的局域网中(四)
73 0
|
7月前
|
存储 设计模式 缓存
《网络是怎么样连接的》读书笔记 - 集线器、路由器和路由器(三)
《网络是怎么样连接的》读书笔记 - 集线器、路由器和路由器(三)
74 0
|
7月前
|
缓存
《网络是怎么样连接的》读书笔记 - FTTH
《网络是怎么样连接的》读书笔记 - FTTH
74 0
|
7月前
|
存储 缓存 网络协议
《网络是怎么样连接的》读书笔记 - Tcp/IP连接(二)
《网络是怎么样连接的》读书笔记 - Tcp/IP连接(二)
91 0
|
7月前
|
缓存 网络协议 网络安全
《网络是怎么样连接的》读书笔记 - 汇总篇(二)
《网络是怎么样连接的》读书笔记 - 汇总篇
203 0
《网络是怎么样连接的》读书笔记 - 汇总篇(二)
|
7月前
|
域名解析 缓存 网络协议
《网络是怎么样连接的》读书笔记 - 汇总篇(一)
《网络是怎么样连接的》读书笔记 - 汇总篇
148 0
《网络是怎么样连接的》读书笔记 - 汇总篇(一)
|
7月前
|
存储 网络协议 Linux
《网络是怎么样连接的》读书笔记 - WEB服务端请求和响应(五)
《网络是怎么样连接的》读书笔记 - WEB服务端请求和响应(五)
47 0
《网络是怎么样连接的》读书笔记 - WEB服务端请求和响应(五)
|
7月前
|
缓存 负载均衡 网络协议
《网络是怎么样连接的》读书笔记 - 服务器端的局域网中(四)
《网络是怎么样连接的》读书笔记 - 服务器端的局域网中(四)
64 0
《网络是怎么样连接的》读书笔记 - 服务器端的局域网中(四)