shell脚本多进程
A. CURL在shell下可以用多线程么
这是多进程吧,连多线程都算不上
php的多并发curl请求(curl_multi_系列函数),我记得没有什么限制吧,能贴一下代码上来吗?
shell下执行多个curl当然是多进程。甚至于像网络蚂蚁那样分块HTTP下载单个文件都未必是多线程,因为每个HTTP请求到头来还是单独的。
追究到底是用多进程还是多线程来做并发,无关紧要。但可以肯定的是:无论是用shell还是用php,同时启动多个/usr/bin/curl进程肯定是没问题的。
楼主应该追究的重点,我估计在于如何多个HTTP请求同时发到服务器上。由于连接服务器的时间前后有所不同,所以如果不认真处理,并发的时间一致性恐怕不好。
这个的解决方式是把连接请求一分为二,把创建连接和发送HTTP请求割裂开来操作。做法是让所有的并发端先连接到服务器,待连接全部就绪之后,再一起发送HTTP请求。
这个我查了一下,似乎不是curl能做得到的,需要写程序。
B. shell flock 何时释放
PHP+shell实现多线程的方法 先写个简单的php代码,这里为了让脚本执行时间更长,方便看效果,sleep一下,呵呵!先看下test.php的代码:ls PHP代码: ?php for ($i=0;$i10;$i++) { echo $i; sleep(10); } ? 在看下shell脚本的代码,非常简单 #!/bin/bash for i in 1 2 3 4 5 6 7 8 9 10 do /usr/bin/php -q /var/www/html/test.php done 注意到在请求php代码的那行有一个符号吗,这个是关键,不加的话是不能进行多线程的,表示讲服务推送到后台执行,因此,在 shell的每次的循环中不必等php的代码全部执行完在请求下一个文件,而是同时进行的,这样就实现了多线程,下面运行下shell看下效果,这里你将 看到10个test.php进程再跑,再利用linux的定时器,定时请求这个shell,在处理一些需要多线程的任务,例如,批量下载时,非常好用! php中用WEB服务器实现多线程 假设我们现在运行的是a.php这个文件. 但是我在程序中又请求WEB服务器运行另一个b.php,那么这两个文件将是同时执行的.(PS: 一个链接请求发送之后, WEB服务器就会执行它, 而不管客户端是否已经退出) 有些时候, 我们想运行的不是另一个文件, 而是本文件中的一部分代码.该怎么办呢? 其实可是通过参数来控制a.php来运行哪一段程序. //a.php,b.php PHP代码:-------------------------------------------------------------------------------- ?php function runThread() { $fp = fsockopen('localhost', 80, $errno, $errmsg); fputs($fp, GET /b.php?act=brnrn); //这里的第二个参数是HTTP协议中规定的请求头 //不明白的请看RFC中的定义 fclose($fp); } function a() { $fp = fopen('result_a.log', 'w'); fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . rn); fclose($fp); } function b() { $fp = fopen('result_b.log', 'w'); fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . rn); fclose($fp); } if(!isset($_GET['act'])) $_GET['act'] = 'a'; if($_GET['act'] == 'a') { runThread(); a(); } else if($_GET['act'] == 'b') b(); ? -------------------------------------------------------------------------------- 打开result_a.log 和 result_b.log 比较一下两个文件的中访问的时间. 大家会发现, 这两个的确是在不同线程中运行的.有些时间完全一样. 上面只是一个简单的例子, 大家可以改进成其它形式. 既然PHP中也能多线程了, 那么问题也来了, 那就是同步的问题. 我们知道 PHP本身是不支持多线程的. 所以更不会有什么像Java 中synchronize的方法了. 那我们该如何做呢. 1. 尽量不访问同一个资源. 以避免冲突. 但是可以同时像数据库操作. 因为数据库是支持并发操作的. 所以在多线程的PHP中不要向同一个文件中写入数据. 如果必须要写的话, 用别的方法进行同步.. 如调用 flock对文件进行加锁等. 或建立临时文件并在另外的线程中等待这个文件的消失 while(file_exits('xxx')); 这样就等于这个临时文件存在时, 表示其实线程正在操作 如果没有了这个文件, 说明其它线程已经释放了这个. 2. 尽量不要从runThread在执行fputs后取这个socket中读取数据. 因为要实现多线程, 需要的用非阻塞模式. 即在像fgets这样的函数时立即返回.. 所以读写数据就会出问题. 如果使用阻塞模式的话, 程序就不算是多线程了. 他要等上面的返回才执行下面的程序. 所以如果需要交换数据最后利用外面文件或数据中完成. 实在想要的话就用socket_set_nonblock($fp) 来实现. 说了这么多, 倒底这个有没有实际的意义呢? 在什么时候需要这种用这种方法呢 ? 答案是肯定的. 大家知道. 在一个不断读取网络资源的应用中, 网络的速度是瓶颈. 如果采多这种形式就可以同时以多个线程对不同的页面进行读取. 本人做的一个能从8848、soaso这些商城网站搜索信息的程序。还有一个从阿里巴巴网站上读取商业信息和公司目录的程序也用到了此技术。 因为这两个程序都是要不断的链接它们的服务器读取信息并保存到数据库。 利用此技术正好消除了在等待响应时的瓶颈。 php模拟实现多线程的三种方法 PHP语言本身是不支持多线程的. 总结了一下网上关于PHP模拟多线程的方法, 总的来说, 都是利用了PHP的好伙伴们本身所具有的多线程能力. PHP的好伙伴指的就是LINUX和APACHE啦, LAMP嘛. 另外, 既然是模拟的, 就不是真正的多线程. 其实只是多进程. 进程和线程是两个不同的概念. 好了, 以下方法都是从网上找来的. 1. 利用LINUX操作系统 ?php for ($i=0;$i10;$i++) { echo $i; sleep(5); } ? 上面存成test.php, 然后写一段SHELL代码 #!/bin/bash for i in 1 2 3 4 5 6 7 8 9 10 do php -q test.php done 2. 利用fork子进程(其实同样是利用LINUX操作系统) ?php declare(ticks=1); $bWaitFlag = FALSE; /// 是否等待进程结束 $intNum = 10; /// 进程总数 $pids = array(); /// 进程PID数组 echo (Startn); for($i = 0; $i $intNum; $i++) { $pids[$i] = pcntl_fork();/// 产生子进程,而且从当前行之下开试运行代码,而且不继承父进程的数据信息 if(!$pids[$i]) { // 子进程进程代码段_Start $str=; sleep(5+$i); for ($j=0;$j$i;$j++) {$str.=*;} echo $i - . time() . $str n; exit();
C. 如何在一个shell脚本里执行多个任务
你是想多进程的同时执行这些任务吗?
你试一下这样能不能达到你想要的效果
#!/bin/bash
{
DATE1=$(date -d "1 day ago" +"%Y%m%d") c=${DATE1} echo $c DATE1=$(date -d "1 day ago" +"%Y%m%d") d=${DATE1} echo $d
}&
就是直接把命令直接放在{}&里面,注意最后的那个符号
D. 刚开机任务管理器里就有好多进程。解决追加5分
很正常啊 一般会有21个左右进程
大约占了CPU的1%-3%
taskmgr.exe : Windows任务管理器。按 CTRL+ALT+ DEL或CTRL+SHIFT+ESC 打开。查看现在是运行在系统上的程序。
wnwb.exe :万能五笔输入法。
Exploter.EXE :Windows Explorer用于控制Windows图形Shell,包括开始菜单、任务栏,桌面和文件管理。这是一个用户的shell,在我们看起来就像任务条,桌面等等。或者说它就是资源管理器,不相信你在运行里执行它看看。它对Windows系统的稳定性还是比较重要的,而红码也就是找它的麻烦,在c和d根下创建explorer.exe。
System Idle Process :Windows页面内存管理进程,该进程拥有0级优先。它作为单线程运行在每个处理器上,并在系统不处理其他线程的时候分派处理器的时间。它的cpu占用率越大表示可供分配的CPU资源越多,数字越小则表示CPU资源紧张。
E. 如何使用gdb调试多进程
GDB 是 linux 系统上常用的 c/c++ 调试工具,功能十分强大。对于较为复杂的系统,比如多进程系统,如何使用 GDB 调试呢?考虑下面这个三进程系统:
进程
进程
Proc2 是 Proc1 的子进程,Proc3 又是 Proc2 的子进程。如何使用 GDB 调试 proc2 或者 proc3 呢?
实际上,GDB 没有对多进程程序调试提供直接支持。例如,使用GDB调试某个进程,如果该进程fork了子进程,GDB会继续调试该进程,子进程会不受干扰地运行下去。如果你事先在子进程代码里设定了断点,子进程会收到SIGTRAP信号并终止。那么该如何调试子进程呢?其实我们可以利用GDB的特点或者其他一些辅助手段来达到目的。此外,GDB 也在较新内核上加入一些多进程调试支持。
接下来我们详细介绍几种方法,分别是 follow-fork-mode 方法,attach 子进程方法和 GDB wrapper 方法。
follow-fork-mode
在2.5.60版Linux内核及以后,GDB对使用fork/vfork创建子进程的程序提供了follow-fork-mode选项来支持多进程调试。
follow-fork-mode的用法为:
set follow-fork-mode [parent|child]
parent: fork之后继续调试父进程,子进程不受影响。
child: fork之后调试子进程,父进程不受影响。
因此如果需要调试子进程,在启动gdb后:
(gdb) set follow-fork-mode child
并在子进程代码设置断点。
此外还有detach-on-fork参数,指示GDB在fork之后是否断开(detach)某个进程的调试,或者都交由GDB控制:
set detach-on-fork [on|off]
on: 断开调试follow-fork-mode指定的进程。
off: gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。
注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。
follow-fork-mode/detach-on-fork的使用还是比较简单的,但由于其系统内核/gdb版本限制,我们只能在符合要求的系统上才能使用。而且,由于follow-fork-mode的调试必然是从父进程开始的,对于fork多次,以至于出现孙进程或曾孙进程的系统,例如上图3进程系统,调试起来并不方便。
Attach子进程
众所周知,GDB有附着(attach)到正在运行的进程的功能,即attach <pid>命令。因此我们可以利用该命令attach到子进程然后进行调试。
例如我们要调试某个进程RIM_Oracle_Agent.9i,首先得到该进程的pid
[root@tivf09 tianq]# ps -ef|grep RIM_Oracle_Agent.9i
nobody 6722 6721 0 05:57 ? 00:00:00 RIM_Oracle_Agent.9i
root 7541 27816 0 06:10 pts/3 00:00:00 grep -i rim_oracle_agent.9i
通过pstree可以看到,这是一个三进程系统,oserv是RIM_Oracle_prog的父进程,RIM_Oracle_prog又是RIM_Oracle_Agent.9i的父进程。
[root@tivf09 root]# pstree -H 6722
通过 pstree 察看进程
通过 pstree 察看进程
启动GDB,attach到该进程
用 GDB 连接进程
用 GDB 连接进程
现在就可以调试了。一个新的问题是,子进程一直在运行,attach上去后都不知道运行到哪里了。有没有办法解决呢?
一个办法是,在要调试的子进程初始代码中,比如main函数开始处,加入一段特殊代码,使子进程在某个条件成立时便循环睡眠等待,attach到进程后在该代码段后设上断点,再把成立的条件取消,使代码可以继续执行下去。
至于这段代码所采用的条件,看你的偏好了。比如我们可以检查一个指定的环境变量的值,或者检查一个特定的文件存不存在。以文件为例,其形式可以如下:
void debug_wait(char *tag_file)
{
while(1)
{
if (tag_file存在)
睡眠一段时间;
else
break;
}
}
当attach到进程后,在该段代码之后设上断点,再把该文件删除就OK了。当然你也可以采用其他的条件或形式,只要这个条件可以设置/检测即可。
Attach进程方法还是很方便的,它能够应付各种各样复杂的进程系统,比如孙子/曾孙进程,比如守护进程(daemon process),唯一需要的就是加入一小段代码。
GDB wrapper
很多时候,父进程 fork 出子进程,子进程会紧接着调用 exec族函数来执行新的代码。对于这种情况,我们也可以使用gdb wrapper 方法。它的优点是不用添加额外代码。
其基本原理是以gdb调用待执行代码作为一个新的整体来被exec函数执行,使得待执行代码始终处于gdb的控制中,这样我们自然能够调试该子进程代码。
还是上面那个例子,RIM_Oracle_prog fork出子进程后将紧接着执行RIM_Oracle_Agent.9i的二进制代码文件。我们将该文件重命名为RIM_Oracle_Agent.9i.binary,并新建一个名为RIM_Oracle_Agent.9i的shell脚本文件,其内容如下:
[root@tivf09 bin]# mv RIM_Oracle_Agent.9i RIM_Oracle_Agent.9i.binary
[root@tivf09 bin]# cat RIM_Oracle_Agent.9i
#!/bin/sh
gdb RIM_Oracle_Agent.binary
当fork的子进程执行名为RIM_Oracle_Agent.9i的文件时,gdb会被首先启动,使得要调试的代码处于gdb控制之下。
新的问题来了。子进程是在gdb的控制下了,但还是不能调试:如何与gdb交互呢?我们必须以某种方式启动gdb,以便能在某个窗口/终端与gdb交互。具体来说,可以使用xterm生成这个窗口。
xterm是X window系统下的模拟终端程序。比如我们在Linux桌面环境GNOME中敲入xterm命令:
xterm
xterm
就会跳出一个终端窗口:
终端
终端
如果你是在一台远程linux服务器上调试,那么可以使用VNC(Virtual Network Computing) viewer从本地机器连接到服务器上使用xterm。在此之前,需要在你的本地机器上安装VNC viewer,在服务器上安装并启动VNC server。大多数linux发行版都预装了vnc-server软件包,所以我们可以直接运行vncserver命令。注意,第一次运行vncserver时会提示输入密码,用作VNC viewer从客户端连接时的密码。可以在VNC server机器上使用vncpasswd命令修改密码。
[root@tivf09 root]# vncserver
New 'tivf09:1 (root)' desktop is tivf09:1
Starting applications specified in /root/.vnc/xstartup
Log file is /root/.vnc/tivf09:1.log
[root@tivf09 root]#
[root@tivf09 root]# ps -ef|grep -i vnc
root 19609 1 0 Jun05 ? 00:08:46 Xvnc :1 -desktop tivf09:1 (root)
-httpd /usr/share/vnc/classes -auth /root/.Xauthority -geometry 1024x768
-depth 16 -rfbwait 30000 -rfbauth /root/.vnc/passwd -rfbport 5901 -pn
root 19627 1 0 Jun05 ? 00:00:00 vncconfig -iconic
root 12714 10599 0 01:23 pts/0 00:00:00 grep -i vnc
[root@tivf09 root]#
Vncserver是一个Perl脚本,用来启动Xvnc(X VNC server)。X client应用,比如xterm,VNC viewer都是和它通信的。如上所示,我们可以使用的DISPLAY值为tivf09:1。现在就可以从本地机器使用VNC viewer连接过去:
VNC viewer:输入服务器
VNC viewer:输入服务器
输入密码:
VNC viewer:输入密码
VNC viewer:输入密码
登录成功,界面和服务器本地桌面上一样:
VNC viewer
VNC viewer
下面我们来修改RIM_Oracle_Agent.9i脚本,使它看起来像下面这样:
#!/bin/sh
export DISPLAY=tivf09:1.0; xterm -e gdb RIM_Oracle_Agent.binary
如果你的程序在exec的时候还传入了参数,可以改成:
#!/bin/sh
export DISPLAY=tivf09:1.0; xterm -e gdb --args RIM_Oracle_Agent.binary $@
最后加上执行权限
[root@tivf09 bin]# chmod 755 RIM_Oracle_Agent.9i
现在就可以调试了。运行启动子进程的程序:
[root@tivf09 root]# wrimtest -l 9i_linux
Resource Type : RIM
Resource Label : 9i_linux
Host Name : tivf09
User Name : mdstatus
Vendor : Oracle
Database : rim
Database Home : /data/oracle9i/920
Server ID : rim
Instance Home :
Instance Name :
Opening Regular Session...
程序停住了。从VNC viewer中可以看到,一个新的gdb xterm窗口在服务器端打开了
gdb xterm 窗口
gdb xterm窗口
[root@tivf09 root]# ps -ef|grep gdb
nobody 24312 24311 0 04:30 ? 00:00:00 xterm -e gdb RIM_Oracle_Agent.binary
nobody 24314 24312 0 04:30 pts/2 00:00:00 gdb RIM_Oracle_Agent.binary
root 24326 10599 0 04:30 pts/0 00:00:00 grep gdb
运行的正是要调试的程序。设置好断点,开始调试吧!
注意,下面的错误一般是权限的问题,使用 xhost 命令来修改权限:
xterm 错误
xterm 错误
[root@tivf09 bin]# export DISPLAY=tivf09:1.0
[root@tivf09 bin]# xhost +
access control disabled, clients can connect from any host
xhost + 禁止了访问控制,从任何机器都可以连接过来。考虑到安全问题,你也可以使用xhost + <你的机器名>。
小结
上述三种方法各有特点和优劣,因此适应于不同的场合和环境:
follow-fork-mode方法:方便易用,对系统内核和GDB版本有限制,适合于较为简单的多进程系统
attach子进程方法:灵活强大,但需要添加额外代码,适合于各种复杂情况,特别是守护进程
GDB wrapper方法:专用于fork+exec模式,不用添加额外代码,但需要X环境支持(xterm/VNC)。
F. 如何运行多进程Docker容器
一般来说,Docker容器比较适合运行单个进程。例如,项目"使用多个Docker容器运行Kubernetes",Kubernetes的各个组件分别运行在各个容器之中,每个容器只运行单个进程。
然而,很多时候我们需要在Docker容器中运行多个进程。例如,项目"使用单个Docker容器运行Kubernetes",kubernetes的各个组件均运行在同一个容器中,该容器中运行了多个进程。那么,如何运行多进程Docker容器?
一种方法是使用Shell脚本,另一种方法是使用进程管理工具Supervisor。kiwenlau/kubernetes-shell和kiwenlau/kubernetes-supervisor分别采用了这两种方法,用于启动多个进程来运行Kubernetes的各个组件,从而实现"使用单个Docker容器运行Kubernetes"。下面我将分别介绍两种不同方法。
使用Shell脚本运行多进程Docker容器
这个方法大家应该会比较熟悉,使用Shell脚本依次启动Kubernetes的各个组件即可。以下为start-kubernetes.sh
!/bin/bashstart docker daemondocker daemon > /var/log/docker.log 2>&1 & start etcdetcd --data-dir=/var/etcd/data > /var/log/etcd.log 2>&1 & wait for ectd to setupsleep 5 start apiserverkube-apiserver --service-cluster-ip-range=10.0.0.1/24 --insecure-bind-address=0.0.0.0 --etcd_servers=http://127.0.0.1:4001 > /var/log/kube-apiserver.log 2>&1 & wait for apiserver to setupsleep 5 start controller manager, sheler, kubelet and proxykube-controller-manager --master=http://0.0.0.0:8080 > /var/log/kube-controller-manager.log 2>&1 & kube-scheler --master=http://0.0.0.0:8080 > /var/log/kube-scheler.log 2>&1 & kubelet --api_servers=http://0.0.0.0:8080 --address=0.0.0.0 --cluster_dns=10.0.0.10 --cluster_domain="kubernetes.local" --pod-infra-container-image="kiwenlau/pause:0.8.0" > /var/log/kubelet.log 2>&1 & kube-proxy --master=http://0.0.0.0:8080 > /var/log/kube-proxy.log 2>&1 & just keep this script runningwhile [[ true ]]; do sleep 1 done
然后在Dockerfile中,将start-kubernetes.sh指定为Docker容器默认执行的命令即可:
CMD ["start-kubernetes.sh"]
需要注意的一点在于,start-kubernetes.sh脚本将作为Docker容器的1号进程运行,必须始终保持运行。因为Docker容器仅在1号进程运行时保持运行,换言之,Docker容器将在1号进程退出后Exited。由于Kubernetes的各个组件都以后台进程方式执行,我在脚本末尾添加了死循环,以保持start-kubernetes.sh脚本始终处于运行状态。
just keep this script runningwhile [[ true ]]; do sleep 1 done
使用supervisor运行多进程Docker容器
Supervisor是进程管理工具。这时,需要编写supervisor的配置文件kubernetes.conf:
[supervisord] nodaemon=true [program:etcd] command=etcd --data-dir=/var/etcd/data autorestart=true stdout_logfile=/var/log/etcd.stdout.log stderr_logfile=/var/log/etcd.stderr.log [program:kube-apiserver] command=kube-apiserver --service-cluster-ip-range=10.0.0.1/24 --insecure-bind-address=0.0.0.0 --etcd_servers=http://127.0.0.1:4001 autorestart=true stdout_logfile=/var/log/kube-apiserver.stdout.log stderr_logfile=/var/log/kube-apiserver.stderr.log [program:kube-controller-manager] command=kube-controller-manager --master=http://0.0.0.0:8080 autorestart=true stdout_logfile=/var/log/controller-manager.stdout.log stderr_logfile=/var/log/controller-manager.stderr.log [program:kube-scheler] command=kube-scheler --master=http://0.0.0.0:8080 autorestart=true stdout_logfile=/var/log/kube-scheler.stdout.log stderr_logfile=/var/log/kube-scheler.stderr.log [program:kubelet] command=kubelet --api_servers=http://0.0.0.0:8080 --address=0.0.0.0 --cluster_dns=10.0.0.10 --cluster_domain="kubernetes.local" --pod-infra-container-image="kiwenlau/pause:0.8.0" autorestart=true stdout_logfile=/var/log/kubelet.stdout.log stderr_logfile=/var/log/kubelet.stderr.log [program:kube-proxy] command=kube-proxy --master=http://0.0.0.0:8080 autorestart=true stdout_logfile=/var/log/kube-proxy.stdout.log stderr_logfile=/var/log/kube-proxy.stderr.log [program:docker] command=docker daemon autorestart=true stdout_logfile=/var/log/docker.stdout.log stderr_logfile=/var/log/docker.stderr.log
可知,将Kubernetes的各个组件的启动命令设为command即可。autorestart参数设为true,意味着supervisor将负责重启意外退出的组件。stdout_logfile和stderr_logfile参数则可以用于设置命令的标准输出文件和标准错误输出文件。
然后在Dockerfile中,将supervisord指定为Docker容器默认执行的命令即可:
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/kubernetes.conf"]
此时, supervisord是Docker容器中的1号进程,也需要始终保持运行状态。nodaemon设为true时,表示supervisor保持前台运行而非在后台运行。若supervisor在后台运行,则Docker容器也会在执行supervisord命令后立即Exited.
[supervisord] nodaemon=true
总结
使用Shell脚本运行多进程Docker容器,优势是大家比较熟悉。由于需要保持Docker容器的1号进程始终运行,这一点比较容易出错。若要实现进程意外退出后自动重启的话,使用shell脚本比较麻烦。
使用supervisor运行多进程Docker容器,非常方便。另外,保持1号进程保持运行,以及进程意外退出后自动重启,实现起来都很简单。
使用多个Docker容器运行Kubernetes
GitHub地址
kiwenlau/single-kubernetes-docker
使用单个Docker容器运行Kubernetes
GitHub地址:
kiwenlau/kubernetes-shell kiwenlau/kubernetes-supervisor
该项目中,我将kubernetes的所有组件:etcd, controller manager, apiserver, scheler, kubelet, proxy以及docker daemon均运行在同一个Docker容器之中。
容器启动时,各个组件由shell脚本或者supervisor启动。
G. linux中,shell的功能和特点
用户可以通过shell与系统打交道,特点是可实现脚本编程。与windows中的cmd窗口类似,比cmd强大。
H. VBA中使用SHELL为什么打不开文件双击能打开
做了一个ACTIVEX.EXE或者是屏蔽BI导致的只能双击打开。shell命令是采用的多进程执行方法,即使shell命令还未执行完成,其下面的代码就已经开始执行了。
I. 关于一个多线程shell脚本的函数调用问题
你这个是不行的 !
首先, shell 没有多线程的说法.
其实, 你这个伪多进程.
然后, 每个 & 产生一个shell子进程,每个子进程都是独立的.
再次, 子进程的变量父进程无法获取到.
所以,你最后 $d , $n 的值都不会变,都是 0 .