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

本文介绍如何利用文件锁确保进程的唯一性运行,探讨使用fcntl记录锁和信号灯的方法,对比不同技术手段的优缺点。
void my_lock(int fd)
{
    
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_CREATEO_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,调用openunlink创建和删除额外一个文件涉及文件系统的访问,这比调用fcntl两次(一个获取锁,一次释放锁)所花时间长得多。

互斥锁和信号量的区别是:互斥锁必须总是由锁住它的线程来解锁,但信号量的signal却不必由执行过它的wait操作的同一个线程来执行。

目录 封面 -12 封底 -11 扉页 -10 版权 -9 版权声明 -8 前言 -7 目录 -3 第一部分 简介 1 第1章 简介 2 1.1 概述 2 1.2 进程、线程与信息共享 3 1.3 IPC对象的持续性 4 1.4 名字空间 5 1.5 fork、exec和exit对IPC对象的影响 7 1.6 出错处理:包裹函数 8 1.7 Unix标准 9 1.8 书中IPC例子索引表 11 1.9 小结 13 习题 13 第2章 Posix IPC 14 2.1 概述 14 2.2 IPC名字 14 2.3 创建与打开IPC通道 16 2.4 IPC权限 18 2.5 小结 19 习题 19 第3章 System V IPC 20 3.1 概述 20 3.2 key_t键和ftok函数 20 3.3 ipc_perm结构 22 3.4 创建与打开IPC通道 22 3.5 IPC权限 24 3.6 标识符重用 25 3.7 ipcs和ipcrm程序 27 3.8 内核限制 27 3.9 小结 28 习题 29 第二部分 消息传递 31 第4章 管道和FIFO 32 4.1 概述 32 4.2 一个简单的客户-服务器例子 32 4.3 管道 32 4.4 全双工管道 37 4.5 popen和pclose函数 39 4.6 FIFO 40 4.7 管道和FIFO的额外属性 44 4.8 单个服务器,多个客户 46 4.9 对比迭代服务器与并发服务器 50 4.10 字节流与消息 51 4.11 管道和FIFO限制 55 4.12 小结 56 习题 57 第5章 Posix消息队列 58 5.1 概述 58 5.2 mq_open、mq_close和mq_unlink函数 59 5.3 mq_getattr和mq_setattr函数 61 5.4 mq_send和mq_receive函数 64 5.5 消息队列限制 67 5.6 mq_notify函数 68 5.7 Posix实时信号 78 5.8 使用内存映射I/O实现Posix消息队列 85 5.9 小结 101 习题 101 第6章 System V消息队列 103 6.1 概述 103 6.2 msgget函数 104 6.3 msgsnd函数 104 6.4 msgrcv函数 105 6.5 msgctl函数 106 6.6 简单的程序 107 6.7 客户-服务器例子 112 6.8 复用消息 113 6.9 消息队列上使用select和poll 121 6.10 消息队列限制 122 6.11 小结 124 习题 124 第三部分 同步 125 第7章 互斥锁和条件变量 126 7.1 概述 126 7.2 互斥锁:上锁与解锁 126 7.3 生产者-消费者问题 127 7.4 对比上锁与等待 131 7.5 条件变量:等待与信号发送 132 7.6 条件变量:定时等待和广播 136 7.7 互斥锁和条件变量的属性 136 7.8 小结 139 习题 139 第8章 读写锁 140 8.1 概述 140 8.2 获取与释放读写锁 140 8.3 读写锁属性 141 8.4 使用互斥锁和条件变量实现读写锁 142 8.5 线程取消 148 8.6 小结 153 习题 153 第9章 记录上锁 154 9.1 概述 154 9.2 对比记录上锁与文件上锁 157 9.3 Posix fcntl记录上锁 158 9.4 劝告性上锁 162 9.5 强制性上锁 164 9.6 读出者和写入者的优先级 166 9.7 启动一个守护进程的唯一副本 170 9.8 文件作锁用 171 9.9 NFS上锁 173 9.10 小结 173 习题 174 第10章 Posix信号量 175 10.1 概述 175 10.2 sem_open、sem_close和sem_unlink函数 179 10.3 sem_wait和sem_trywait函数 180 10.4 sem_post和sem_getvalue函数 180 10.5 简单的程序 181 10.6 生产者-消费者问题 186 10.7 文件上锁 190 10.8 sem_init和sem_destroy函数 191 10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值