Shell简介
熟悉Linux系统的都知道SHELL(壳),这是一种可以实现用户与kernel交互式的语言,通常在操作系统中,我们称为命令解释语言,如windows系统下dos,再如Linux下的bash,当然Linux下的shell还有很多,我们可以通过查看/etc/shells来查看这台linux系统支持那些shell,当然bash是绝大多数linux系统默认安装shell,因为它来自GNU计划下编写的shell,现在Mac OS系统下默认也是bash,所以可见bash的强大之处。
bash并发式编程
bash是属于面向对象的编程,属于从头到位描述执行解决问题的思路的编程。
废话不多说,直接上码:
我们的目的是计算从0开始,执行100次循环,每次加1,执行100次并发,那结果应该是100。
1 | #!/bin/bash |
2 | countfile=/tmp/count |
3 | if ! [ -f $countfile ] |
4 | then |
5 | echo 0 > $countfile |
6 | fi |
7 | do_count() { |
8 | read count < $countfile |
9 | echo $[++count] > $countfile |
10 | } |
11 | for i in `seq 1 100` |
12 | do |
13 | do_count & |
14 | done |
15 | wait |
16 | cat $countfile |
17 | rm $countfile |
然而执行结果却每次都不一样…
1 | [root@node1 ~]# bash flock.sh |
2 | 1 |
3 | [root@node1 ~]# bash flock.sh |
4 | 1 |
5 | [root@node1 ~]# bash flock.sh |
6 | 1 |
7 | [root@node1 ~]# bash flock.sh |
8 | 3 |
9 | [root@node1 ~]# bash flock.sh |
10 | 9 |
11 | [root@node1 ~]# bash flock.sh |
100次并发,我们都是通过将上一次执行结果写入到一个文件来存储,然而100次并发,都是通过读取这个文件里的数字来进行加1操作,可能得到的结果就是,上一次的操作结果还未写入文件,下一次并发已经开始,他读取的还是n次之前的并发留下结果,并发能力的快慢取决于电脑cpu的快慢,如果按照这个运算结果每次都是1,那说明cpu对于处理100次并发搓搓有余。
然而这不是方法,我们需要引入一种机制,执行100次并发,但必须要等上次操作完成,下次并发才能进行,这时最容易就是对文件加锁,等上一次的锁放开下次的并发的锁加上,这样就可以完成这个流程有序的控制。
flock文件锁
下面介绍一个一个linux命令:
1 | flock - Manage locks from shell scripts |
2 | flock [-sxon] [-w timeout] lockfile [-c] command... |
3 | flock [-sxon] [-w timeout] lockdir [-c] command... |
4 | flock [-sxun] [-w timeout] fd |
5 | -s, --shared: 获得一个共享锁,在定向为某文件的FD上设置共享锁而未释放锁的时间内,其他进程试图在定向为此文件的FD上设置独占锁的请求失败,而其他进程试图在定向为此文件的FD上设置共享锁的请求会成功。 |
6 | -x, --exclusive: 获得一个独占锁,在定向为某文件的FD上设置独占锁而未释放锁的时间内,其他进程试图在定向为此文件的FD上设置共享锁或独占锁都会失败。只要未设置-s参数,此参数默认被设置。 |
7 | -u, --unlock: 移除一个锁,通常是不需要的,脚本执行完会自动丢弃锁。 |
8 | -n, --nonblock: 如果没有立即获得锁,直接失败而不是等待 |
9 | -w, --timeout: 如果没有立即获得锁,等待指定时间 |
10 | -o, --close: 在运行命令前关闭文件的描述符号。用于如果命令产生子进程时会不受锁的管控 |
11 | -c, --command: 在shell中运行一个单独的命令 |
- 共享锁演示:
1 | 我们在node1上设置共享锁,这个锁得等5分钟才解开 |
2 | [root@node1 ~]# flock -s /tmp/lock -c "sleep 300" |
3 | 我们在另一个终端上设置同个共享锁,后面的命令可以直接执行。 |
4 | [root@node1 ~]# flock -s /tmp/lock -c "cat /etc/fstab" |
5 | # |
6 | # /etc/fstab |
7 | # Created by anaconda on Thu Jan 26 08:57:47 2017 |
8 | # |
9 | # Accessible filesystems, by reference, are maintained under '/dev/disk' |
10 | # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info |
11 | # |
12 | /dev/mapper/vg_node1-lv_root / ext4 defaults 1 1 |
13 | UUID=b41284cf-58ea-42af-a9dc-6d4d3b39e6bd /boot ext4 defaults 1 2 |
14 | /dev/mapper/vg_node1-lv_home /home ext4 defaults 1 2 |
15 | /dev/mapper/vg_node1-lv_swap swap swap defaults 0 0 |
16 | tmpfs /dev/shm tmpfs defaults 0 0 |
17 | devpts /dev/pts devpts gid=5,mode=620 0 0 |
18 | sysfs /sys sysfs defaults 0 0 |
19 | proc /proc proc defaults 0 0 |
20 | /dev/myvg1/mylv1 /users ext4 defaults,acl 0 0 |
21 | 我们在同个锁文件上加独占锁,这个将一直等待共享锁解开才可以执行。 |
22 | [root@node1 ~]# flock -x /tmp/lock -c "cat /etc/fstab" |
23 | 我们可以看见在/tmp/下创建了一个lock文件 |
24 | [root@node1 ~]# ll /tmp/lock |
25 | -rw-r--r--. 1 root root 0 Feb 20 07:20 /tmp/lock |
- 独占锁演示:
1 | 默认加的就是独占锁,也可-x 指定独占锁 |
2 | [root@node1 ~]# flock /tmp/lock -c "sleep 300" |
3 | 加另一个独占锁,等待锁解开 |
4 | [root@node1 ~]# flock -x /tmp/lock -c "cat /etc/fstab" |
5 | 加另一个共享锁,等待锁解开 |
6 | [root@node1 ~]# flock -s /tmp/lock -c "cat /etc/fstab" |
7 | 但是一直等待不是好办法,设置-n选项,不堵塞,如果获取不到锁,就直接退出,返回1 |
8 | [root@node1 ~]# flock -xn /tmp/lock -c "cat /etc/fstab" |
9 | [root@node1 ~]# echo $? |
10 | 1 |
11 | 我们还可以设置等待几秒后在退出,等待10秒,如果还是获取不到锁,就退出 |
12 | [root@node1 ~]# flock -xw 10 /tmp/lock -c "cat /etc/fstab" |
- 针对文件描述符的演示
1 | #!/bin/bash |
2 | countfile=/tmp/count |
3 | if ! [ -f $countfile ] |
4 | then |
5 | echo 0 > $countfile |
6 | fi |
7 | do_count() { |
8 | #以只读的方式打开文件,文件描述符为3 |
9 | exec 3< $countfile |
10 | #对3号文件描述符添加独占锁 |
11 | flock -x 3 |
12 | 读取3号文件描述符内容,并赋值给count变量 |
13 | read -u 3 count |
14 | echo $[++count] > $countfile |
15 | #解开文件3号文件描述符 |
16 | flock -u 3 |
17 | #取消文件描述符 |
18 | exec 3>&- |
19 | } |
20 | for i in `seq 1 100` |
21 | do |
22 | do_count & |
23 | done |
24 | wait |
25 | cat $countfile |
26 | rm $countfile |
我们继续上面脚本问题,运行这个脚本,我们就可以等到我们想要的结果,这样就为整个脚本添加了锁的机制,并为并发控制了流程。
- flock crontab上的应用
其实flock在crontab上应用的最多,计划任务运行脚本,往往会未等上一个脚本运行完成就开始了下一次的运行,这样不仅耗系统资源,而且可能会造成进程卡死或接口瘫痪的结果,使用flock可以很好的避免这样的情况出现。
1 | * * * * * flock -xn /tmp/.lock -c "/usr/local/php/bin/php testphp" |
但是凡是锁,就会出现死锁的形象,进程不释放锁,导致下次的脚本运行无法进行,我们还可以引入超时机制.
1 | * * * * * timeout 500 -s SIGINT flock -xn /tmp/.lock -c "/usr/local/php/bin/php testphp" |
如果超过500秒这个进程还是占着这个锁,我们就发送sigint信号,相当于ctrl+c,好了有了锁的机制,还有超时机制,crontab计划任务运行的应该可靠的很多。