Chinaunix首页 | 论坛 | 博客
  • 博客访问: 319310
  • 博文数量: 78
  • 博客积分: 2611
  • 博客等级: 少校
  • 技术积分: 740
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-10 18:02
文章分类

全部博文(78)

文章存档

2014年(1)

2012年(1)

2011年(2)

2010年(8)

2009年(4)

2008年(14)

2007年(48)

我的朋友

分类:

2007-01-31 09:16:22

【背景介绍】
CU上曾经有几个帖子讨论到一个实际问题,就是如何限制同一时刻只允许一个脚本实例运行。其中本版新老斑竹和其它网友都参加了讨论,但以faintblue兄的帖子对大家启发最大,下面的背景介绍中许多内容都是来自于他。在此感谢faintblue兄,也感谢斑竹和其它朋友!
woodie总结了一下现有的结果,大体上可以分为两种思路:
一、简单的方法是,用ps一类命令找出已经运行脚本的数量,如果大于等于2(别忘了把自己也算进去^_^),就退出当前脚本,等于1,则运行。这种方法简单是简单,不过有一些问题:
首先,ps取得脚本文件进程数量就有很多陷阱,例如有时无法ps到脚本文件的名称;
即使可以ps到脚本名,如果用到管道的话,由于子shell的原因,在大多数平台下会得到奇怪的结果,有时得到数字a,有时又得到数字b,让人无所适从;
就算计数的问题已经解决了,还有问题,不过不太严重:如果两个脚本实例同时计数,显然数字都应该等于2,于是两个都退出了。于是在这一时间点上没有一个脚本在执行;
二、加锁的方法。就是脚本在执行开始先试图得到一个“锁”,得到则继续执行,反之就退出。
加锁方法也存在一些问题,主要集中在两个方面:
其一,加锁时如何避免竞态条件(race condition)。即如何找到一些“原子”操作,使得加锁的动作一步完成,中间不能被打断。否则就可能出现下面的情况:
脚本1检测到没有锁被占用;
然后脚本2也检测到没有锁被占用;
脚本1加锁,开始执行;
然后脚本2(错误地)加锁,也开始执行;
看到吗,两个脚本在同时执行。:(
可能的一些加锁的“原子”操作有:
1.创建目录,当一个进程创建成功后其它进程都会失败;
2.符号链接:ln -s,一个链接创建后其它进程的ln -s命令会出错;
3.文件首行的竞争,多个进程以append的方式同时写到文件,只有惟一一个进程写到了文件的第一行,因为不可能有两个第一行。^_^
4.其它软件包的加锁工具,通常是c语言二进制程序,自己写的也行。
目前加锁时的问题已经可以解决。
其二,找到一种方法避免出现“死锁”的情况,这里是指:虽然“锁”被占用,但却没有脚本在执行。这通常在脚本意外退出,来不及释放占用的“锁”之后。如收到一些系统信号后退出,机器意外掉电后退出等。
对于前者的情况,可以用trap捕获一些信号,在退出前释放锁;但有些信号是无法捕获的。
对于后者,可以在机器重起后用脚本自动删除锁来解决。不过有点麻烦。
所以比较理想的是脚本自己来检测死锁,然后释放它。不过问题的难点在于如何找到一种“原子”操作,将检测死锁和删除死锁的动作一步完成,否则又会出现与加锁时同样的竞态条件的问题。例如:
进程1检测到死锁;
进程2监测到死锁;
进程1删除死锁;
进程x(也可能是进程1自己)加锁,开始运行;
进程2(错误地)删除死锁;
此时锁没有占用,于是任意进程都可以加锁并投入运行。
这样又出现了两个进程同时运行的情况。:(
可惜的是:在迄今为止的讨论之后,woodie还没有找到一种合适的“原子”操作。:(只是找到了一种稍微好些的办法:就是在删除时用文件的inode作标识,于是其它进程新建的锁(文件名虽然相同,但inode相同的机率比较微小)不容易被意外删除。这个方法已经接近完美了,可惜还是存在误删的微小几率,不能说是100%安全。唉,山重水复疑无路啊!:(

最近又有网友问起这个问题,促使我又再次思考。从我以前的一个想法发展了一下,换了一种思路,便有豁然开朗的感觉。不敢藏私,写出来请大家debug。^_^

基本的想法就是:借鉴多进程编程中临界区的概念,如果各个进程进入我们设立的临界区,只可能一个一个地顺序进入,不就能保证每次只有一个脚本运行了吗?怎样建立这样一种临界区呢?我想到了一种方法,就是用管道,多个进程写到同一个管道,只可能一行一行地进入,相应的,另一端也是一行一行地读出,如此就可以实现并行执行的多个进程进入临界区时的“串行化”。这与faintblue兄以前贴出的append文件的方法也是异曲同工。
我们可以让并行的进程同时向一个管道写一行请求,内容是其进程号,在管道另一端顺序读取这些请求,但只有第一个请求会得到一个“令牌”,被允许开始运行;后续的请求将被忽略,对应的进程没有得到令牌,就自己退出。这样就保证了任意时间只有一个进程运行(严格地说是进入临界区)。说到“令牌”,熟悉网络发展史的朋友可能会联想到IBM的Token Ring架构,每一时刻只能有一个主机得到令牌并发送数据,没有以太网的“碰撞”问题。可惜如同微通道技术一样,IBM的技术是不错,但最终还是被淘汰了。不错,这里令牌的概念就是借用于Token Ring。^_^
当一个进程执行完毕,向管道发送一个终止信号,即交回“令牌”,另一端接受到后,又开始选取下一个进程发放“令牌”。
您可能会问了,那么死锁问题又如何解决呢?别急,我在以前的讨论中曾提出将检测处理死锁的代码单独拿出来,交给一个专门的进程来处理的想法,这里就具体实践这样一种思路。当检测和删除死锁的任务由一个专门的进程来执行时,就没有多个并发进程对同一个锁进行操作,所以竞态条件发生的物质基础也就根本不存在了。^_^
再发展一下这个思路,允许同时执行多个进程如何?当然可以!只要设立一个计数器,达到限制的数字就停止发放“令牌”即可。
下面就是woodie上述思路的一个实现,只是在centos 4.2下简单地测试了一下,可能还有不少错误,请大家帮忙“除虫”。^_^思路上有什么问题也请不吝指教:
脚本1,token.sh,负责令牌管理和死锁检测处理。与下一个脚本一样,为了保持脚本的最大的兼容性,尽量使用Bourne shell的语法,并用printf代替了echo,sed的用法也尽量保持通用性。这里是由一个命名管道接受请求,令牌在一个文件中发出。如果用ksh也许可以用协进程来实现,熟悉ksh的朋友可以试一试。^_^

#!/bin/sh

#name: token.sh
#function: serialized token distribution, at anytime, only a cerntern number of token given out
#usage: token.sh [number] &
#number is set to allow number of scripts to run at same time
#if no number is given, default value is 1
if [ -p /tmp/p-aquire ]; then
  rm -f /tmp/p-aquire
fi
if mkfifo /tmp/p-aquire; then
  printf "pipe file /tmp/p-aquire created\n" >>token.log
else
  printf "cannot create pipe file /tmp/p-aquire\n" >>token.log
  exit 1
fi

loop_times_before_check=100
if [ -n "$1" ];then
  limit=$1
else
  # default concurrence is 1
  limit=1
fi
number_of_running=0
counter=0
while :;do
  #check stale token, which owner is died unexpected
  if [ "$counter" -eq "$loop_times_before_check" ]; then
    counter=0
    for pid in `cat token_file`;do
      pgrep $pid
      if [ $? -ne 0 ]; then
        #remove lock
            printf "s/ $pid//\nwq\n"|ed -s token_file
            number_of_running=`expr $number_of_running - 1`
      fi
    done
  fi
  counter=`expr $counter + 1`

  #
  if [ "$number_of_running" -ge "$limit" ];then
    # token is all given out. bypass all request until a instance to give one back
    pid=`sed -n '/stop/ {s/\([0-9]\+\) \+stop/\1/p;q}' /tmp/p-aquire`
    if [ -n "$pid" ]; then
      # get a token returned
      printf "s/ $pid//\nwq\n"|ed -s token_file
      number_of_running=`expr $number_of_running - 1`
      continue
    fi
  else
    # there is still some token to give out. serve another request
    read pid action < /tmp/p-aquire
        if [ "$action" = stop ]; then
          #  one token is given back.
          printf "s/ $pid//\nwq\n"|ed -s token_file
          number_of_running=`expr $number_of_running - 1`
        else
          # it's a request, give off a token to instance identified by $pid
          printf " $pid" >> token_file
          number_of_running=`expr $number_of_running + 1`
        fi
  fi
done



--------------------------------------------------------------------------------------------
修订记录:
1.修正token.sh的一个BUG,将原来用sed删除失效令牌的命令用ed命令代替。感谢r2007和waker两位指出错误!
--------------------------------------------------------------------------------------------

脚本2:并发执行的脚本 -- my-script。在"your code goes here"一行后插入你自己的代码,现有的是我用来测试的。

#!/bin/sh

# second to wait that the ditributer gives off a token
a_while=1
if [ ! -p /tmp/p-aquire ]; then
  printf "cannot find file /tmp/p-aquire\n" >&2
  exit 1
fi
# try to aquire a token
printf "$$\n" >> /tmp/p-aquire
sleep $a_while
# see if we get one
grep "$$" token_file
if [ $? -ne 0 ]; then
  # bad luck. :(
  printf "no token free now, exitting...\n" >&2
  exit 2
fi
# yeah, got token!
# be sure to return the token before we exit
trap 'printf "$$ stop\n" > /tmp/p-aquire' 0
trap "exit 3" 1 2 3 15

#get to run, your code goes here
printf "$$: running...\n" >&2
sleep 5
printf "$$: exitting...\n" >&2
#end of your code



[ 本帖最后由 woodie 于 2006-10-29 21:36 编辑 ]



  回复于:2006-10-11 23:32:49

直接写回原文件会出问题吧?还没有完全读明白整个意思,难道是有意这么安排的?

sed "s/\ $pid//" token_file > token_file


先占个位置明天再好好研究一下。^_^

新发现:如何防止多个token.sh同时运行?作为Daemon进程在开机时自动加载?

[ 本帖最后由 r2007 于 2006-10-11 23:41 编辑 ]


  回复于:2006-10-12 08:36:27

引用:原帖由 r2007 于 2006-10-11 23:32 发表
直接写回原文件会出问题吧?还没有完全读明白整个意思,难道是有意这么安排的?

sed "s/\ $pid//" token_file > token_file


先占个位置明天再好好研究一下。^_^

新发现:如何防止 ... 


是有意安排的。^_^
因为token_file只有一行,内容是空格分隔的允许运行的进程号。
该行sed先读入只有一行的文件内容,删除一个进程号,回写到原文件。应该没有问题吧?^_^

token.sh是管理模块,设计时就是用来只执行一次的,通常是用&丢到后台去。所以token.sh应该不存在并发的问题。^_^如要防止意外运行两次,可以在该脚本的开始检测一个标志,有则退出就行了,不必考虑竞态的问题。谢谢七兄的意见!

又读了一下上面的代码,发现检测失效令牌的代码段中使用的pgrep命令兼容性不好,非linux平台可能没有这个命令。应该改成用ps+grep来做,但ps命令的语法在不同的平台上也不尽相同。:(怎样写兼容性最好呢?七兄有什么好的建议?有其它UNIX环境的朋友们也请发表意见,如何在您的平台下检测一个进程号对应的进程是否存在?SOLARIS, AIX, HP-UX, IRIS, *BSD, $CO, ...所有平台的意见都欢迎!

[ 本帖最后由 woodie 于 2006-10-12 08:39 编辑 ]


  回复于:2006-10-12 08:45:46

其实和r007的问题是一样的,如何确保token.sh在my-script之前唯一的运行?

另外

pid=`sed -n '/stop/ {s/\([0-9]\+\) \+stop/\1/p;q}' /tmp/p-aquire`

这一句会从管道读出所有等待写入的内容,会不会忽略脚本的请求?


  回复于:2006-10-12 08:48:37

引用:原帖由 woodie 于 2006-10-12 08:36 发表

是有意安排的。^_^
因为token_file只有一行,内容是空格分隔的允许运行的进程号。
该行sed先读入只有一行的文件内容,删除一个进程号,回写到原文件。应该没有问题吧?^_^

token.sh是管理模块,设计时就是 ... 




太大意了了吧

sed .. file >file会清空file

>file在命令运行前就清空了file ,sed 从哪儿读?


  回复于:2006-10-12 09:04:34

引用:原帖由 waker 于 2006-10-12 08:45 发表
其实和r007的问题是一样的,如何确保token.sh在my-script之前唯一的运行?

另外

pid=`sed -n '/stop/ {s/\([0-9]\+\) \+stop/\1/p;q}' /tmp/p-aquire`

这一句会从管道读出所有等待写入的内容,会不会忽略脚本的请求?


token.sh唯一性的问题已经说过了。
waker兄提到的这行sed,实际上也是有意这样做的。注意这行的前提条件是,令牌限额已经用完,此时应该忽略所有的请求,直到有令牌交回为止(即读到含有stop字样的行),然后才能继续发放令牌。这样做是为了提高读取的效率,避免一次循环读一行时,造成高并发情况下队列越排越长的拥塞问题。


  回复于:2006-10-12 09:13:31

引用:原帖由 waker 于 2006-10-12 08:48 发表



太大意了了吧

sed .. file >file会清空file

>file在命令运行前就清空了file ,sed 从哪儿读? 


呵呵,你说的不错!应该改成:
cat token_file | sed "s/\ $pid//" > token_file
或用ed来做。
谢谢二位指正!

------------------------
附注:上面这个cat也是一个UUOC,不过是一个:Useful Usage Of Cat. ^_^一笑!

[ 本帖最后由 woodie 于 2006-10-12 09:27 编辑 ]


  回复于:2006-10-12 09:26:20

问题是即使现在有脚本已经交回了令牌,请求也会被忽略吧?
一个脚本已经交了牌,但你的处理在收回令牌以前已经忽略了其它的请求
可能我的讲解有误

token.sh唯一性的问题已经说过了。
写这个东西的目的不就是避免竞争条件么?如果放宽一点,我们只需要touch一个文件就行了。
现在把my-script的竞争问题避免了,但途径是转移到token.sh中去,好像不完美


  回复于:2006-10-12 09:28:48

cat token_file | sed "s/\ $pid//" > token_file
在bash中仍然有可能会清空file

用ed吧


  回复于:2006-10-12 09:45:54

引用:原帖由 waker 于 2006-10-12 09:26 发表
问题是即使现在有脚本已经交回了令牌,请求也会被忽略吧?
一个脚本已经交了牌,但你的处理在收回令牌以前已经忽略了其它的请求
可能我的讲解有误


本来这个脚本要解决的问题就是高并发情况下可能发生的一些问题,高并发环境下,某些my-script的请求被拒绝是再正常不过的,这并不是世界末日。^_^
在它请求时没有令牌,所以被拒绝是很自然的处理方法。一次读一行的情况下,也会有请求被拒绝,不过几率低一些罢了,不是吗?不过那样的话,很可能发生上文所说的拥塞的情况。
------------------------
补充一下,在还有令牌要发的情况下,脚本走的是另一个分支,那时是用read逐行读取的,不会随意丢弃排队的请求。^_^

引用:原帖由 waker 于 2006-10-12 09:26 发表token.sh唯一性的问题已经说过了。
写这个东西的目的不就是避免竞争条件么?如果放宽一点,我们只需要touch一个文件就行了。
现在把my-script的竞争问题避免了,但途径是转移到token.sh中去,好像不完美


再说一次,token.sh是一个监控的模块,只需要运行一次,不是用来并发执行的,所以考虑它自身的竞态问题是没有必要的。并发执行的任务都放到my-script中去做。不知道我表达得清楚了吗?^_^

[ 本帖最后由 woodie 于 2006-10-12 09:55 编辑 ]


  回复于:2006-10-12 09:47:40

引用:原帖由 waker 于 2006-10-12 09:28 发表
cat token_file | sed "s/\ $pid//" > token_file
在bash中仍然有可能会清空file

用ed吧 


好建议!用ed应该比较保险,兼容性也很好。谢谢!


  回复于:2006-10-12 10:08:04

>本来这个脚本要解决的问题就是高并发情况下可能发生的一些问题,高并发环境下,某些my-script的请求被拒绝是再正常不过的,这并不是世界末日。

这和之前r007的设计是一样的,只保证脚本不会多次运行,不保证脚本会运行,OK,这应该不是一个问题


>再说一次,token.sh是一个监控的模块,只需要运行一次
这点不敢苟同,没看到保证 token.sh正在运行而且唯一的任何操作,靠人脑计数么?


  回复于:2006-10-12 10:20:31

用token.sh来协调的方式是一个不错的方法。但是应该避免waker提到的出现同时运行的情况。
既然token.sh只是扮演一个协调员的角色,那么不必让用户接触到此脚本。
想法如下:
让token.sh只能由root执行,并且设置成开机运行。
普通用户,仅需要执行my-script脚本,两脚本之间通过管道通讯。
PS:就是不清楚不同用户的进程能否通过管道建立通讯。


  回复于:2006-10-12 10:26:05

引用:原帖由 r2007 于 2006-10-12 10:20 发表
PS:就是不清楚不同用户的进程能否通过管道建立通讯。
 


命名管道不就是为这个目的才产生的么?


  回复于:2006-10-12 10:37:06

学习ing。。。。。。。。。。。。。。。虽然还不是很懂


  回复于:2006-10-12 10:51:30

16排,座位我占了


  回复于:2006-10-12 11:47:12

引用:原帖由 waker 于 2006-10-12 10:08 发表
...
>再说一次,token.sh是一个监控的模块,只需要运行一次
这点不敢苟同,没看到保证 token.sh正在运行而且唯一的任何操作,靠人脑计数么?


人脑计数,有何不可?手指头加上脚趾头足够用了。^_^开个玩笑。
呵呵,有不同的看法很正常。That's why the forum for.
我的想法是token.sh的执行时机是用户高度可控的,执行的频率对一次并发任务的执行是惟一的 -- 即只在开始执行一次,因而不需要过多考虑并发的问题。
当然你和七兄的观点也是有道理的,我并没有拒绝这个建议,只是前面对七兄已经简单回答过了,只是没有深入地探讨细节 -- 以前的帖子已经讨论得太多了。^_^
woodie>...如要防止意外运行两次,可以在该脚本的开始检测一个标志,有则退出就行了...


  回复于:2006-10-12 12:09:46

问题不是人脑不够用,而是人脑太多,如何保证用户A运行了token.sh而用户B不去运行token.sh?如何保证B用户不会kill掉token进程?
如果要保证,是不是又回到了问题的起点?

[ 本帖最后由 waker 于 2006-10-12 12:10 编辑 ]


  回复于:2006-10-12 13:15:40

呵. woodie 兄的 idea 不錯!
不過, 從 ethernet 的原理來看, 倒不像是 token ring, 反而像是 CSMA/CA .
也就是 request to send(RTS) 跟 clear to send(CTS) .

還是我理解錯了?


  回复于:2006-10-12 14:44:50

引用:原帖由 waker 于 2006-10-12 12:09 发表
问题不是人脑不够用,而是人脑太多,如何保证用户A运行了token.sh而用户B不去运行token.sh?如何保证B用户不会kill掉token进程?
如果要保证,是不是又回到了问题的起点?

呵呵,说的不是一个问题。我的着眼点是特定的一个密集并发的任务,这时用户显然知道自己要做什么,没有可能傻到启动一大堆token.sh。
waker兄想的更远点,如果有多个不同的并发的任务 -- 不妨把它称为多个并发任务集,其中每一个的内部都面临多个进程并发执行的问题 -- 都要调用token.sh的情况。
OK,这个想法是对的,但考虑的焦点却错了。其实管理多个并发任务集的多个token.sh同时执行 -- 这件事情本身并不会造成问题,关键是它们用到的命名管道和令牌文件必须各不相同,那样的话运行多少个token.sh都没有问题。相互删除的问题如果是由token.sh自己来退出或者用进程号来标识的话也可以避免。
因为我的楼顶贴出的脚本还只是一个初步的原型,有很多需要完善的地方,现在还不能作为一个通用的工具来使用。请大家多提意见!^_^现在的命名管道名和令牌文件名还是硬编码的,如果要做的通用一些,可以考虑用参数或者配置文件的方式来设置。


  回复于:2006-10-12 14:52:28

引用:原帖由 網中人 于 2006-10-12 13:15 发表
呵. woodie 兄的 idea 不錯!
不過, 從 ethernet 的原理來看, 倒不像是 token ring, 反而像是 CSMA/CA .
也就是 request to send(RTS) 跟 clear to send(CTS) .

還是我理解錯了? 


我的意思是每个进程取得令牌后才运行关键代码段,这一点上与令牌环的得到令牌后才能发送数据的情况比较类似。
当然每个进程请求令牌的过程存在相互的竞争,这在一定程度上又有些类似以太网的竞争环境。网兄说的也没错。着眼点不同罢了。^_^
相反的,Token Ring的令牌好像是在环路上自动轮转的,“皇帝轮流做,明年到我家”,不存在竞争的过程。


  回复于:2006-10-12 15:15:56

pid=`sed -n '/stop/ {s/\([0-9]\+\) \+stop/\1/p;q}' /tmp/p-aquire`

这一点似乎还是有问题,会有stop被抛弃掉而没有收回number_of_running么?


  回复于:2006-10-12 15:39:49

引用:原帖由 waker 于 2006-10-12 15:15 发表
pid=`sed -n '/stop/ {s/\([0-9]\+\) \+stop/\1/p;q}' /tmp/p-aquire`

这一点似乎还是有问题,会有stop被抛弃掉而没有收回number_of_running么? 


这样有可能丢令牌,当管道中有多于一个的stop消息时。


  回复于:2006-10-12 15:56:08

引用:原帖由 r2007 于 2006-10-12 15:39 发表

这样有可能丢令牌,当管道中有多于一个的stop消息时。 


会吗?一行不会有多个stop,匹配到第一个stop并处理后sed就用q命令退出了呀。然后收回令牌,运行数量减一,进入下一循环并读取下一行。会丢失stop行吗?


  回复于:2006-10-12 16:24:42

引用:原帖由 woodie 于 2006-10-12 15:56 发表

会吗?一行不会有多个stop,匹配到第一个stop并处理后sed就用q命令退出了呀。然后收回令牌,运行数量减一,进入下一循环并读取下一行。会丢失stop行吗? 


不会

有个简单的方法不知道如何
事先建一个管道,比如 /dev/my-script.token
然后在启动脚本中向其中灌入若干空行,需要并行几个就写几个。
在用户脚本中,先尝试从管道中取一行,如成功则继续运行,否则继续等待,完成后向管道送回一个空行。
大体意思如下:(可参考这里---[url=]能不能用shell做一个队列)
while (1) {
 读管道(可以设定一个timeout值)
如果$?=0, 跳出循环继续用户代码
否则继续循环(可加一个计数,如果超出,则exit $errno)
}


  回复于:2006-10-12 16:56:25

To r2007:
嗯,不错的想法!一直等待的话并不可取,加上timeout的话就完全可行了。
不过进程意外退出的情况应该如何处理呢?似乎可以由每个运行的进程将进程号写入一个文件,再由管理端来检测是否在运行。跟楼顶的做法正好相反,由进程端生成运行进程列表文件,管理端要做的事情就比较少了。如果不检测失效令牌的话,管理端就完全可以省略!如果在每个进程内部检测失效的话,又会出现竞态的问题,所以最好还是管理端来做。
很有启发性,谢谢!


  回复于:2006-10-12 17:28:55

七兄,好像不行啊!首先我机器上的测试显示:read -t只在bash, ksh93, zsh下合法,linux下的bsh,和pdksh不能用。
而且在能用的平台上似乎超时时间结束后并不退出,而是挂在那里不动了。:( 
bash man page上说:
...
 This option has no effect if read is not reading input from the terminal or a pipe.
...
难道这里的pipe不包括fifo?不解...


  回复于:2006-10-12 17:30:26

难道只好自己编码来处理超时?不甘心。:-P


  回复于:2006-10-12 17:39:46

好像是用-t选项读fifo无效,测试过了普通的管道正常。:(


  回复于:2006-10-12 17:51:45

掉到read -t的陷阱里去了!其实有更简单的方法:就是用普通的文件而不用fifo,令牌用append附加到该文件,然后直接读取,不用加超时,read命令会直接返回,然后检查返回状态就可以了。^_^

----------------------------------------------------------------------------------
等等,又发现问题,用普通文件的话读到一行后应该删除一行,否则不能体现一个进程已经领取令牌的事实,但删行......又掉进了竞态的老泥坑里了,晕菜!还是用fifo吧。
那通过真正的管道转发fifo的内容呢?
head -1 /tmp/p-aquire | read -t 3 V
也不行!这回挂在那里的倒不是read,而是head了,read在一边陪绑!
用其它语言写一个会超时的工具去读fifo?但那兼容性岂不是越发地差了吗?
不用fifo,通过socket实现如何?还是算了,编程用到的工具又降低了兼容性。
看来只能用shell自己来处理。:(但会可能用到子shell,变量不能直接回传...如何检测read的返回状态?用exit?
越来越麻烦了。
七兄有什么简单的好办法教我吗?

[ 本帖最后由 woodie 于 2006-10-12 18:56 编辑 ]


  回复于:2006-10-12 22:57:16

这样可以了,命名管道有点鬼,还没研究透。似乎在打开文件时就阻塞了,所以必须用读写模式打开。


exec 9<>namedpipefile
read -t1 <&9



 


  回复于:2006-10-13 07:43:47

to woodie&r007
1.挂在那里的原因是: read -t1命令根本没有被执行,命令一直处于准备 
2.摸拟read -t的脚本实现以前也讨论过,我的思路是写一个read.sh,调用,在后台计时kill它就好了,这个不讨论了吧,没什么有价值的技巧


  回复于:2006-10-13 09:33:29

呵呵,谢谢两位提示!这样做也可以:
read -t1 var <>$FIFO_NAME
不过read -t的兼容性还是差点。
自己处理倒没什么难度,我只是怕麻烦而已!^_^


  回复于:2006-10-13 09:54:08

引用:原帖由 waker 于 2006-10-13 07:43 发表
to woodie&r007
1.挂在那里的原因是: read -t1命令根本没有被执行,命令一直处于准备 ... 


waker兄说的对,实际上如果没有进程以写方式打开FIFO,FIFO对于任何读取操作都是不可用的(Not ready for read)。
像这样的话就没有问题:
sleep 20 > $FIFO_NAME&
read -t1 var < $FIFO_NAME
因为已经有进程以写方式打开了FIFO,虽然它暂时还没有写入任何内容,但read -t已经按我们的愿望运作正常了。


  回复于:2006-10-19 10:26:39

看不太明白,还是来点实际的吧,reallink程序中有一个限制,就是只能在一台电脑中同时运行三个,能不能用你的理论,把它给破了?


  回复于:2006-10-21 01:57:34

学习


  回复于:2006-10-26 15:04:00

想个最简单的, 不知道可不可行:

脚本运行时直接把当前 进程的 PID 号 写到 如 /tmp/myscriptrun 文件里, 用 >> 加行

这样,脚本运行时先取得这个文件的最后几行,检查一下它的PID是否还存在,感觉这样也应该可以.

而且要控制只能运行 两个,或三个, 实例都可以,而且脚本自身改名了也能检测的到


  回复于:2006-10-26 15:40:08

谢谢关注!append文件思路在楼顶加锁方法之3已经提出了。对于只允许一个进程的情况,这个锁机制工作的不错,但失效锁的问题仍然没有解决,即进程意外退出后其它进程得不到机会运行的问题。
对于允许多个进程的场合,有一个很大的问题,进程结束时应该改写或删除关键文件 -- 你的例子中是myscriptrun,但多个进程同时写一个文件的情况如何处理?这里就有潜在的race condition,怎么解决?再加锁?我们似乎掉入了一个怪圈,好像一只小狗不停地追逐自己的尾巴,却永远追不上.......^_^


  回复于:2006-10-29 09:23:42

不妨看看这个程序,专为这个问题而编写


    用的时候才知道多么有用,收藏了,感谢 还有  

阅读(2718) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~