Web : Server, DB , PHP , JSP , Design etc.
10/08 28
http://forum.ubuntu.org.cn/viewtopic.php?t=20852

首先当然是要安装samba了,呵呵:
代码:
sudo apt-get install samba
sudo apt-get install smbfs


下面我们来共享群组可读写文件夹,假设你要共享的文件夹为: /home/ray/share

首先创建这个文件夹
代码:
mkdir /home/ray/share
chmod 777 /home/ray/share


备份并编辑smb.conf允许网络用户访问
代码:
sudo cp /etc/samba/smb.conf /etc/samba/smb.conf_backup
sudo gedit /etc/samba/smb.conf


搜寻这一行文字
代码:
; security = user


用下面这几行取代
代码:
security = user
username map = /etc/samba/smbusers


将下列几行新增到文件的最后面,假设允许访问的用户为:newsfan。而文件夹的共享名为 Share

代码:
[Share]
comment = Shared Folder with username and password
path = /home/ray/share
public = yes
writable = yes
valid users = newsfan
create mask = 0700
directory mask = 0700
force user = nobody
force group = nogroup
available = yes
browseable = yes


然后顺便把这里改一下,找到[global]把 workgroup = MSHOME 改成
代码:
workgroup = WORKGROUP
display charset = UTF-8
unix charset = UTF-8
dos charset = cp936

后面的三行是为了防止出现中文目录乱码的情况。其中根据你的local,UTF-8 有可能需要改成 cp936。自己看着办吧。

现在要添加newsfan这个网络访问帐户。如果系统中当前没有这个帐户,那么
代码:
sudo useradd newsfan

要注意,上面只是增加了newsfan这个用户,却没有给用户赋予本机登录密码。所以这个用户将只能从远程访问,不能从本机登录。而且samba的登录密码可以和本机登录密码不一样。

现在要新增网络使用者的帐号:
代码:
sudo smbpasswd -a newsfan
sudo gedit /etc/samba/smbusers


在新建立的文件内加入下面这一行并保存
代码:
newsfan = "network username"


如果将来需要更改newsfan的网络访问密码,也用这个命令更改
代码:
sudo smbpasswd -a newsfan


删除网络使用者的帐号的命令把上面的 -a 改成 -x
代码:
sudo testparm
sudo /etc/init.d/samba restart

最后退出重新登录或者重新启动一下机器。
Tags:
10/08 26

svn 常用指令 不指定

jason , 17:52 , 我的收藏 » Other , 评论(0) , 引用(0) , 阅读(46) , Via 本站原创
开发人员常用命令

(1) 导入项目

$ cd ~/project
$ mkdir -p svntest/{trunk,branches,tags}
$ svn import svntest https://localhost/test/svntest --username jasonyu --message "Start project"
...
$ rm -rf svntest

我们新建一个项目svntest,在该项目下新建三个子目录:trunk,开发主干;branches,开发分支;tags,开发阶段性标签。然后导入到版本库test下,然后把svntest拿掉。

(2) 导出项目

$ svn checkout https://localhost/test/svntest/trunk

$ svn co https://localhost/test/svntest/trunk svntest

修订版本号的指定方式是每个开发人员必须了解的,以下是几个参考例子,说明可参考svn推荐书。

$ svn diff --revision PREV:COMMITTED foo.c
# shows the last change committed to foo.c

$ svn log --revision HEAD
# shows log message for the latest repository commit

$ svn diff --revision HEAD
# compares your working file (with local changes) to the latest version
# in the repository

$ svn diff --revision BASE:HEAD foo.c
# compares your “pristine” foo.c (no local changes) with the
# latest version in the repository

$ svn log --revision BASE:HEAD
# shows all commit logs since you last updated

$ svn update --revision PREV foo.c
# rewinds the last change on foo.c
# (foo.c's working revision is decreased)

$ svn checkout --revision 3
# specified with revision number

$ svn checkout --revision {2002-02-17}
$ svn checkout --revision {15:30}
$ svn checkout --revision {15:30:00.200000}
$ svn checkout --revision {"2002-02-17 15:30"}
$ svn checkout --revision {"2002-02-17 15:30 +0230"}
$ svn checkout --revision {2002-02-17T15:30}
$ svn checkout --revision {2002-02-17T15:30Z}
$ svn checkout --revision {2002-02-17T15:30-04:00}
$ svn checkout --revision {20020217T1530}
$ svn checkout --revision {20020217T1530Z}
$ svn checkout --revision {20020217T1530-0500}

(3) 日常指令

$ svn update

$ svn add foo.file
$ svn add foo1.dir
$ svn add foo2.dir --non-recursive
$ svn delete README
$ svn copy foo bar
$ svn move foo1 bar1

$ svn status
$ svn status --verbose
$ svn status --verbose --show-updates
$ svn status stuff/fox.c

$ svn diff
$ svn diff > patchfile

$ svn revert README
$ svn revert

修改冲突发生时,会生成三个文件:.mine, .rOLDREV, .rNEWREV。比如:

$ ls -l
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2

解决修改冲突方式之一:修改冲突的文件sandwich.txt,然后运行命令:

$ svn resolved sandwich.txt

方式之二:用库里的新版本覆盖你的修改:

$ cp sandwich.txt.r2 sandwich.txt
$ svn resolved sandwich.txt

方式之三:撤销你的修改,这种方式不需要运行resolved子命令:

$ svn revert sandwich.txt
Reverted 'sandwich.txt'
$ ls sandwich.*
sandwich.txt

确保没问题后,就可以提交了。

$ svn commit --message "Correct some fatal problems"
$ svn commit --file logmsg
$ svn commit

(4) 检验版本历史

$ svn log
$ svn log --revision 5:19
$ svn log foo.c
$ svn log -r 8 -v

$ svn diff
$ svn diff --revision 3 rules.txt
$ svn diff --revision 2:3 rules.txt
$ svn diff --revision 4:5 http://svn.red-bean.com/repos/example/trunk/text/rules.txt

$ svn cat --revision 2 rules.txt
$ svn cat --revision 2 rules.txt > rules.txt.v2

$ svn list http://svn.collab.net/repos/svn
$ svn list --verbose http://svn.collab.net/repos/svn

$ svn checkout --revision 1729 # Checks out a new working copy at r1729

$ svn update --revision 1729 # Updates an existing working copy to r1729


(5) 其他有用的命令

svn cleanup

为失败的事务清场。

(6) 分支和合并

建立分支方法一:先checkout然后做拷贝,最后提交拷贝。

$ svn checkout http://svn.example.com/repos/calc bigwc
A  bigwc/trunk/
A  bigwc/trunk/Makefile
A  bigwc/trunk/integer.c
A  bigwc/trunk/button.c
A  bigwc/branches/
Checked out revision 340.

$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch

$ svn commit -m "Creating a private branch of /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.

建立分支方法二:直接远程拷贝。

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m "Creating a private branch of /calc/trunk."

Committed revision 341.

建立分支后,你可以把分支checkout并继续你的开发。

$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch

假设你已经checkout了主干,现在想切换到某个分支开发,可做如下的操作:

$ cd calc
$ svn info | grep URL
URL: http://svn.example.com/repos/calc/trunk
$ svn switch http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile
Updated to revision 341.
$ svn info | grep URL
URL: http://svn.example.com/repos/calc/branches/my-calc-branch

合并文件的命令参考:

$ svn diff -r 343:344 http://svn.example.com/repos/calc/trunk
$ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk
$ svn commit -m "integer.c: ported r344 (spelling fixes) from trunk."
$ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk my-calc-branch
$ svn merge http://svn.example.com/repos/branch1@150 \
            http://svn.example.com/repos/branch2@212 \
            my-working-copy
$ svn merge -r 100:200 http://svn.example.com/repos/trunk my-working-copy
$ svn merge -r 100:200 http://svn.example.com/repos/trunk
$ svn merge --dry-run -r 343:344 http://svn.example.com/repos/calc/trunk

最后一条命令仅仅做合并测试,并不执行合并操作。

建立标签和建立分支没什么区别,不过是拷贝到不同的目录而已。

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/tags/release-1.0 \
      -m "Tagging the 1.0 release of the 'calc' project."

$ ls
my-working-copy/
$ svn copy my-working-copy http://svn.example.com/repos/calc/tags/mytag
Committed revision 352.

后一种方式直接把本地的工作拷贝复制为标签。

此外,你还可以删除某个分支。

$ svn delete http://svn.example.com/repos/calc/branches/my-calc-branch \
  -m "Removing obsolete branch of calc project."

管理人员常用命令

(7) 版本库管理

$ svnadmin help
...
$ svnadmin help create
...
$ svnadmin create --fs-type bdb /usr/local/repository/svn/test
$ chown -R svn.svn /usr/local/repository/svn/test

建立版本库,库类型为bdb(使用Berkeley DB做仓库),库名称为test。
svn版本库有两种存储方式:基于Berkeley DB(bdb)或者基于文件系统(fsfs),通过 --fs-type可指定存储方式。

(8) 查询版本库信息

$ svnlook help
...
$ svnlook help tree
...
$ svnlook tree /usr/local/repository/svn/test --show-ids
...

更详细的命令,请参考SVN官方推荐的书(http://svnbook.red-bean.com/)。这本书有中译版,而且是免费的。
Tags:
10/08 25
点击在新窗口中浏览此图片
10/08 24
SIGHUP    终止进程    终端线路挂断
SIGINT    终止进程    中断进程
SIGQUIT   建立CORE文件终止进程,并且生成core文件
SIGILL   建立CORE文件      非法指令
SIGTRAP  建立CORE文件      跟踪自陷
SIGBUS   建立CORE文件      总线错误
SIGSEGV  建立CORE文件      段非法错误
SIGFPE   建立CORE文件      浮点异常
SIGIOT   建立CORE文件      执行I/O自陷
SIGKILL  终止进程    杀死进程
SIGPIPE  终止进程    向一个没有读进程的管道写数据
SIGALARM  终止进程    计时器到时
SIGTERM  终止进程    软件终止信号
SIGSTOP  停止进程    非终端来的停止信号
SIGTSTP  停止进程    终端来的停止信号
SIGCONT  忽略信号    继续执行一个停止的进程
SIGURG   忽略信号    I/O紧急信号
SIGIO    忽略信号    描述符上可以进行I/O
SIGCHLD  忽略信号    当子进程停止或退出时通知父进程
SIGTTOU  停止进程    后台进程写终端
SIGTTIN  停止进程    后台进程读终端
SIGXGPU  终止进程    CPU时限超时
SIGXFSZ  终止进程    文件长度过长
SIGWINCH  忽略信号    窗口大小发生变化
SIGPROF  终止进程    统计分布图用计时器到时
SIGUSR1  终止进程    用户定义信号1
SIGUSR2  终止进程    用户定义信号2
SIGVTALRM 终止进程    虚拟计时器到时

1) SIGHUP 本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控
制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端
不再关联.
2) SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出
3) SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-)来控制. 进程在因收到
SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信
号.
4) SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行
数据段. 堆栈溢出时也有可能产生这个信号.
5) SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用.
6) SIGABRT 程序自己发现错误并调用abort时产生.
6) SIGIOT 在PDP-11上由iot指令产生, 在其它机器上和SIGABRT一样.
7) SIGBUS 非法地址, 包括内存地址对齐(alignment)出错. eg: 访问一个四个字长
的整数, 但其地址不是4的倍数.
8) SIGFPE 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢
出及除数为0等其它所有的算术的错误.
9) SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞, 处理和忽略.
10) SIGUSR1 留给用户使用
11) SIGSEGV 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
12) SIGUSR2 留给用户使用
13) SIGPIPE Broken pipe
14) SIGALRM 时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该
信号.
15) SIGTERM 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和
处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这
个信号.
17) SIGCHLD 子进程结束时, 父进程会收到这个信号.
18) SIGCONT 让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用
一个handler来让程序在由stopped状态变为继续执行时完成特定的
工作. 例如, 重新显示提示符
19) SIGSTOP 停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:
该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
20) SIGTSTP 停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时
(通常是Ctrl-Z)发出这个信号
21) SIGTTIN 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN
信号. 缺省时这些进程会停止执行.
22) SIGTTOU 类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
23) SIGURG 有"紧急"数据或out-of-band数据到达socket时产生.
24) SIGXCPU 超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/
改变
25) SIGXFSZ 超过文件大小资源限制.
26) SIGVTALRM 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
27) SIGPROF 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的
时间.
28) SIGWINCH 窗口大小改变时发出.
29) SIGIO 文件描述符准备就绪, 可以开始进行输入/输出操作.
30) SIGPWR Power failure

有两个信号可以停止进程:SIGTERM和SIGKILL。 SIGTERM比较友好,进程能捕捉这个信号,根据您的需要来关闭程序。在关闭程序之前,您可以结束打开的记录文件和完成正在做的任务。在某些情况下,假如进程正在进行作业而且不能中断,那么进程可以忽略这个SIGTERM信号。

对于SIGKILL信号,进程是不能忽略的。这是一个 “我不管您在做什么,立刻停止”的信号。假如您发送SIGKILL信号给进程,Linux就将进程停止在那里。
10/08 24

使用PHP真正的多进程运行模式,适用于数据采集、邮件群发、数据源更新、tcp服务器等环节。

PHP有一组进程控制函数(编译时需要 –enable-pcntl与posix扩展),使得php能在*nix系统中实现跟c一样的创建子进程、使用exec函数执行程序、处理信号等功能。 PCNTL使用ticks来作为信号处理机制(signal handle callback mechanism),可以最小程度地降低处理异步事件时的负载。何谓ticks?Tick 是一个在代码段中解释器每执行 N 条低级语句就会发生的事件,这个代码段需要通过declare来指定。

常用的PCNTL函数

   1. pcntl_alarm ( int $seconds )

      设置一个$seconds秒后发送SIGALRM信号的计数器
   2. pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] )

      为$signo设置一个处理该信号的回调函数。下面是一个隔5秒发送一个SIGALRM信号,并由signal_handler函数获取,然后打印一个“Caught SIGALRM”的例子:
<?php  
          declare(ticks = 1);  
      
          function signal_handler($signal) {  
              print "Caught SIGALRM\n";  
              pcntl_alarm(5);  
          }  
      
          pcntl_signal(SIGALRM, "signal_handler", true);  
          pcntl_alarm(5);  
      
          for(;;) {  
          }  
      
      ?>


   3. pcntl_exec ( string $path [, array $args [, array $envs ]] )

      在当前的进程空间中执行指定程序,类似于c中的exec族函数。所谓当前空间,即载入指定程序的代码覆盖掉当前进程的空间,执行完该程序进程即结束。
<?php  
      $dir = '/home/shankka/';  
      $cmd = 'ls';  
      $option = '-l';  
      $pathtobin = '/bin/ls';  
      
      $arg = array($cmd, $option, $dir);  
      
      pcntl_exec($pathtobin, $arg);  
      echo '123';    //不会执行到该行  
      ?>


   4. pcntl_fork ( void )

      为当前进程创建一个子进程,并且先运行父进程,返回的是子进程的PID,肯定大于零。在父进程的代码中可以用 pcntl_wait(&$status)暂停父进程知道他的子进程有返回值。注意:父进程的阻塞同时会阻塞子进程。但是父进程的结束不影响子进程的运行。

      父进程运行完了会接着运行子进程,这时子进程会从执行pcntl_fork()的那条语句开始执行(包括此函数),但是此时它返回的是零(代表这是一个子进程)。在子进程的代码块中最好有exit语句,即执行完子进程后立即就结束。否则它会又重头开始执行这个脚本的某些部分。

      注意两点:
      1. 子进程最好有一个exit;语句,防止不必要的出错;
      2. pcntl_fork间最好不要有其它语句,例如:
<?php
$pid = pcntl_fork();
      //这里最好不要有其他的语句
      if ($pid == -1) {
        die('could not fork');
      } else if ($pid) {
        // we are the parent
        pcntl_wait($status); //Protect against Zombie children
      } else {
        // we are the child
      }


   5. pcntl_wait ( int &$status [, int $options ] )

      阻塞当前进程,只到当前进程的一个子进程退出或者收到一个结束当前进程的信号。使用$status返回子进程的状态码,并可以指定第二个参数来说明是否以阻塞状态调用:
      1. 阻塞方式调用的,函数返回值为子进程的pid,如果没有子进程返回值为-1;
      2. 非阻塞方式调用,函数还可以在有子进程在运行但没有结束的子进程时返回0。
   6. pcntl_waitpid ( int $pid , int &$status [, int $options ] )

      功能同pcntl_wait,区别为waitpid为等待指定pid的子进程。当pid为-1时pcntl_waitpid与pcntl_wait 一样。在pcntl_wait和pcntl_waitpid两个函数中的$status中存了子进程的状态信息,这个参数可以用于 pcntl_wifexited、pcntl_wifstopped、pcntl_wifsignaled、pcntl_wexitstatus、 pcntl_wtermsig、pcntl_wstopsig、pcntl_waitpid这些函数。

      例如:

      
<?php  
      $pid = pcntl_fork();  
      if($pid) {  
         pcntl_wait($status);  
         $id = getmypid();  
         echo "parent process,pid {$id}, child pid {$pid}\n";  
      }else{  
         $id = getmypid();  
         echo "child process,pid {$id}\n";  
         sleep(2);  
      }  
      ?>


      子进程在输出child process等字样之后sleep了2秒才结束,而父进程阻塞着直到子进程退出之后才继续运行。
   7. pcntl_getpriority ([ int $pid [, int $process_identifier ]] )

      取得进程的优先级,即nice值,默认为0,在我的测试环境的linux中(CentOS release 5.2 (Final)),优先级为-20到19,-20为优先级最高,19为最低。(手册中为-20到20)。
   8. pcntl_setpriority ( int $priority [, int $pid [, int $process_identifier ]] )

      设置进程的优先级。
   9. posix_kill

      可以给进程发送信号
  10. pcntl_singal

      用来设置信号的回调函数

当父进程退出时,子进程如何得知父进程的退出
当父进程退出时,子进程一般可以通过下面这两个比较简单的方法得知父进程已经退出这个消息:

   1. 当父进程退出时,会有一个INIT进程来领养这个子进程。这个INIT进程的进程号为1,所以子进程可以通过使用getppid()来取得当前父进程的pid。如果返回的是1,表明父进程已经变为INIT进程,则原进程已经推出。
   2. 使用kill函数,向原有的父进程发送空信号(kill(pid, 0))。使用这个方法对某个进程的存在性进行检查,而不会真的发送信号。所以,如果这个函数返回-1表示父进程已经退出。

除了上面的这两个方法外,还有一些实现上比较复杂的方法,比如建立管道或socket来进行时时的监控等等。

PHP多进程采集数据的例子

<?php
/**
* Project: Signfork: php多线程库
* File:    Signfork.class.php
*/

class Signfork{
  /**
   * 设置子进程通信文件所在目录
   * @var string
   */
  private $tmp_path='/tmp/';

/**
  * Signfork引擎主启动方法
  * 1、判断$arg类型,类型为数组时将值传递给每个子进程;类型为数值型时,代表要创建的进程数.
  * @param object $obj 执行对象
  * @param string&#124;array $arg 用于对象中的__fork方法所执行的参数
  * 如:$arg,自动分解为:$obj->__fork($arg[0])、$obj->__fork($arg[1])...
  * @return array  返回   array(子进程序列=>子进程执行结果);
  */
  public function run($obj,$arg=1){
    if(!method_exists($obj,'__fork')){
      exit("Method '__fork' not found!");
    }

    if(is_array($arg)){
     $i=0;
     foreach($arg as $key=>$val){
       $spawns[$i]=$key;
       $i++;
       $this->spawn($obj,$key,$val);
     }
     $spawns['total']=$i;
    }elseif($spawns=intval($arg)){
      for($i = 0; $i < $spawns; $i++){
        $this->spawn($obj,$i);
      }
    }else{
      exit('Bad argument!');
    }

   if($i>1000) exit('Too many spawns!');
      return $this->request($spawns);
   }

  /**
   * Signfork主进程控制方法
   * 1、$tmpfile 判断子进程文件是否存在,存在则子进程执行完毕,并读取内容
   * 2、$data收集子进程运行结果及数据,并用于最终返回
   * 3、删除子进程文件
   * 4、轮询一次0.03秒,直到所有子进程执行完毕,清理子进程资源
   * @param  string&#124;array $arg 用于对应每个子进程的ID
   * @return array  返回   array([子进程序列]=>[子进程执行结果]);
   */
   private function request($spawns){
     $data=array();
     $i=is_array($spawns)?$spawns['total']:$spawns;
     for($ids = 0; $ids<$i; $ids++){
       while(!($cid=pcntl_waitpid(-1, $status, WNOHANG)))usleep(30000);
       $tmpfile=$this->tmp_path.'sfpid_'.$cid;
       $data[$spawns['total']?$spawns[$ids]:$ids]=file_get_contents($tmpfile);
       unlink($tmpfile);
     }
     return $data;
   }

/**
  * Signfork子进程执行方法
  * 1、pcntl_fork 生成子进程
  * 2、file_put_contents 将'$obj->__fork($val)'的执行结果存入特定序列命名的文本
  * 3、posix_kill杀死当前进程
  * @param object $obj        待执行的对象
  * @param object $i                子进程的序列ID,以便于返回对应每个子进程数据
  * @param object $param 用于输入对象$obj方法'__fork'执行参数
  */
  private function spawn($obj,$i,$param=null){
    if(pcntl_fork()===0){
      $cid=getmypid();
      file_put_contents($this->tmp_path.'sfpid_'.$cid,$obj->__fork($param));
      posix_kill($cid, SIGTERM);
      exit;
    }
  }
}
?>


php在pcntl_fork()后生成的子进程(通常为僵尸进程)必须由pcntl_waitpid()函数进行资源释放。但在 pcntl_waitpid()不一定释放的就是当前运行的进程,也可能是过去生成的僵尸进程(没有释放);也可能是并发时其它访问者的僵尸进程。但可以使用posix_kill($cid, SIGTERM)在子进程结束时杀掉它。

子进程会自动复制父进程空间里的变量。

PHP多进程编程示例2

<?php
//.....
//需要安装pcntl的php扩展,并加载它
if(function_exists("pcntl_fork")){
   //生成子进程
  $pid = pcntl_fork();
  if($pid == -1){
    die('could not fork');
  }else{
    if($pid){
      $status = 0;
      //阻塞父进程,直到子进程结束,不适合需要长时间运行的脚本,可使用pcntl_wait($status, 0)实现非阻塞式
      pcntl_wait($status);
      // parent proc code
      exit;
    }else{
      // child proc code
      //结束当前子进程,以防止生成僵尸进程
      if(function_exists("posix_kill")){
        posix_kill(getmypid(), SIGTERM);
      }else{
        system('kill -9'. getmypid());
      }
      exit;
    }
  }
}else{
   // 不支持多进程处理时的代码在这里
}
//.....
?>


如果不需要阻塞进程,而又想得到子进程的退出状态,则可以注释掉pcntl_wait($status)语句,或写成:

<?php
pcntl_wait($status, 1);
//或
pcntl_wait($status, WNOHANG);

在上面的代码中,如果父进程退出(使用exit函数退出或redirect),则会导致子进程成为僵尸进程(会交给init进程控制),子进程不再执行。

僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程.(zombie)进程。任何进程在退出前(使用exit退出) 都会变成僵尸进程(用于保存进程的状态等信息),然后由init进程接管。如果不及时回收僵尸进程,那么它在系统中就会占用一个进程表项,如果这种僵尸进程过多,最后系统就没有可以用的进程表项,于是也无法再运行其它的程序。

预防僵尸进程有以下几种方法:

   1. 父进程通过wait和waitpid等函数使其等待子进程结束,然后再执行父进程中的代码,这会导致父进程挂起。上面的代码就是使用这种方式实现的,但在WEB环境下,它不适合子进程需要长时间运行的情况(会导致超时)。

      使用wait和waitpid方法使父进程自动回收其僵尸子进程(根据子进程的返回状态),waitpid用于临控指定子进程,wait是对于所有子进程而言。
   2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用wait回收
   3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号,例如:

      pcntl_signal(SIGCHLD, SIG_IGN);
      $pid = pcntl_fork();
      //....code

   4. 还有一个技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程再fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。下面是一个例子:

      #include "apue.h"
      #include <sys/wait.h>
      
      int main(void){
        pid_t    pid;
      
        if ((pid = fork()) < 0){
           err_sys("fork error");
        } else if (pid == 0){     /**//* first child */
          if ((pid = fork()) < 0){
             err_sys("fork error");
          }elseif(pid > 0){
             exit(0);    /**//* parent from second fork == first child */
          }
      
          /**
           * We're the second child; our parent becomes init as soon
           * as our real parent calls exit() in the statement above.
           * Here's where we'd continue executing, knowing that when
           * we're done, init will reap our status.
           */
           sleep(2);
           printf("second child, parent pid = %d ", getppid());
           exit(0);
        }
      
        if (waitpid(pid, NULL, 0) != pid)  /**//* wait for first child */
          err_sys("waitpid error");
      
        /**
         * We're the parent (the original process); we continue executing,
         * knowing that we're not the parent of the second child.
         */
         exit(0);
      }


      在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD信号处理函数调用 waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程,无法正常结束,此时即使是root身份kill-9也不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在),僵尸进程成为”孤儿进程”,过继给1号进程init,init会定期调用wait回收清理这些父进程已退出的僵尸子进程。

      所以,上面的示例可以改成:

      
<?php
       //.....
       //需要安装pcntl的php扩展,并加载它
       if(function_exists("pcntl_fork")){
         //生成第一个子进程
        $pid = pcntl_fork();  //$pid即所产生的子进程id
        if($pid == -1){
          //子进程fork失败
          die('could not fork');
        }else{
          if($pid){
            //父进程code
            sleep(5);  //等待5秒
            exit(0); //或$this->_redirect('/');
          }else{
            //第一个子进程code
            //产生孙进程
            if(($gpid = pcntl_fork()) < 0){ ////$gpid即所产生的孙进程id
              //孙进程产生失败
              die('could not fork');
            }elseif($gpid > 0){
              //第一个子进程code,即孙进程的父进程
              $status = 0;
              $status = pcntl_wait($status); //阻塞子进程,并返回孙进程的退出状态,用于检查是否正常退出
              if($status ! = 0) file_put_content('filename', '孙进程异常退出');
              //得到父进程id
              //$ppid =  posix_getppid(); //如果$ppid为1则表示其父进程已变为init进程,原父进程已退出
              //得到子进程id:posix_getpid()或getmypid()或是fork返回的变量$pid
              //kill掉子进程
              //posix_kill(getmypid(), SIGTERM);
              exit(0);
            }else{ //即$gpid == 0
              //孙进程code
              //....
              //结束孙进程(即当前进程),以防止生成僵尸进程
              if(function_exists('posix_kill')){
                 posix_kill(getmypid(), SIGTERM);
              }else{
                 system('kill -9'. getmypid());
              }
              exit(0);
            }
          }
        }
       }else{
         // 不支持多进程处理时的代码在这里
       }
       //.....
      ?>


怎样产生僵尸进程的
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。

任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是”Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。

如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。

另外,还可以写一个php文件,然后在以后台形式来运行它,例如:

<?php
  //Action代码
  public function createAction(){
    //....
    //将args替换成要传给insertLargeData.php的参数,参数间用空格间隔
    system('php -f insertLargeData.php ' . ' args ' . '&');
    $this->redirect('/');
  }
?>


然后在insertLargeData.php文件中做数据库操作。也可以用cronjob + php的方式实现大数据量的处理。

如果是在终端运行php命令,当终端关闭后,刚刚执行的命令也会被强制关闭,如果你想让其不受终端关闭的影响,可以使用nohup命令实现:

<?php
  //Action代码
  public function createAction(){
    //....
    //将args替换成要传给insertLargeData.php的参数,参数间用空格间隔
    system('nohup php -f insertLargeData.php ' . ' args ' . '&');
    $this->redirect('/');
  }
?>


你还可以使用screen命令代替nohup命令。
10/08 24
最近比较PHP跟python, Erlang 的特性,发现PHP有很多人们不常用到的特性。用PHP CLI可以实现很多不错的应用。比如做爬虫, 长期运行的计算脚本, 完全可以取代其他语言来做 服务器的运维。这对于熟悉PHP的人来说如虎添翼。

为什么PHP多进程很好? 网游服务器大部分都使用多线程而不是多进程的原因也在于进程比线程更加稳定。而且多线程适合现在多核服务器的应用场景,更能发挥多核运算的能力。进程的维护可以用很多操作系统级别的工具。Message Queue解决了多大部分线程通信问题。所以PHP多进程很适合做服务器端的计算密集型的应用。

据一家越南IT公司介绍,他们成功的把PHP后台多进程用在法律文件的分发、处理银行账户的金额这样的企业级的应用上。

使用后台PHP 进程可以不影响服务器同时处理网页的请求。这种后台进程一旦发生失败很容易查处原因进行恢复或者补救,所以健壮性更高。不同的进程相互隔离,更加高效,可以统一调度各个服务进程。

PHP 是目前应用最广泛的WEB开发语言,所以用PHP来做服务器端的应用可以降低成本。可以用现有人员、现有配置、甚至做到代码重用。

什么样的场景更适合用PHP后台多进程呢?比如邮件的分发、远程服务的调用、数据的聚合、计划任务、计算结果的缓存这些不需要立即返回的地方。

PHP单进程在某些地方完全可以达到目的,而且更加容易实现,不用考虑进程的同步问题,不用考虑数据的共享问题。

PHP CLI (SAPI SERVER API)命令行接口可以用来做CRON计划任务, 图形界面程序 (使用GTK库)。

PHP CLI 例子:
php -f test.php
php -r “echo time();”
php -R as python style

PHP读取命令行参数:
#!/usr/bin/php -q
echo “Test Arguments:\n”;
echo $_SERVER["argc"].”\n”;
echo $_SERVER["argv"][0].”\n”;
?>

PHP命令行接口标准输入输出:
#!/usr/bin/php -q
/* Define STDIN in case if it is not already defined by PHP for some reason */
if(!defined(“STDIN”)) {
define(“STDIN”, fopen(‘php://stdin’,'r’))
}

echo “Hello! What is your name (enter below):\n”;
$strName = fread(STDIN, 80); // Read up to 80 characters or a newline
echo ‘Hello ‘ , $strName , “\n”;
?>

CRONJOB可以定时运行某些任务,但要防止重复运行。开始时创建一个锁文件, 结束时删除。或者用ps命令来处理。

任务队列可以用Mysql来实现,或者KEY/VALUE数据库,或者消息队列来实现。

进程控制相关函数:
Process Control Extensions
pcntl_fork()
posix_setsid()
posix_kill
pcntl_wait
pcntl_signal

o SIGHUP
o SIGTERM; system shutdown, kill
o SIGINT; sent by Ctrl+c
o SIGKILL (uncatchable); unresponsive, kill -9
o SIGCHLD; child status change
o SIGSTP; sent by Ctrl+z
o SIGCONT; resume from stop, fg

PHP不能对某些错误抛出异常,如何提高PHP多进程应用的容错性?
可以监控进程,依赖进程失败后报告
用CRONJOB实现监控进程
将被监控进程PID写成文件
定时检查PID文件是否存在 检查ps -o pid= 或者file_exists(‘/proc/’)
如果线程不存在 重启进程。

回顾以前用JAVA或者Python做的服务器端的服务都可以用PHP来实现。单一语言更容易维护。以往人们对于WEB语言的认识很片面,例如多线程、事 务这些东西都可以改变方式来达到同样的目的。


http://blog.eood.cn/server-side-php-progress-program-best-practice
10/08 24
将PHP程序作为Linux守护进程的方法:
nohup /usr/local/php/bin/php /opt/zhangyan.php 2>&1 > /dev/null &

 (nohup命令可以在用户退出终端后仍然执行程序,“2>&1 > /dev/null”表示不显示标准输出和错误输出,最后的&表示推到后台执行。)

http://blog.s135.com/post/311/
10/08 7
点击在新窗口中浏览此图片

光标控制命令

命令                        光标移动
h或^h                    向左移一个字符
j或^j或^n                向下移一行
k或^p                    向上移一行
l或空格                     向右移一个字符
G                        移到文件的最后一行
nG                       移到文件的第n行
w                        移到下一个字的开头
W                        移到下一个字的开头,忽略标点符号
b                        移到前一个字的开头
B                        移到前一个字的开头,忽略标点符号
L                        移到屏幕的最后一行
M                        移到屏幕的中间一行
H                        移到屏幕的第一行
e                        移到下一个字的结尾
E                        移到下一个字的结尾,忽略标点符号
(                        移到句子的开头
)                        移到句子的结尾
{                        移到段落的开头
}                        移到下一个段落的开头
0或|                     移到当前行的第一列
n|                       移到当前行的第n列
^                        移到当前行的第一个非空字符
$                        移到当前行的最后一个字符
+或return                移到下一行的第一个字符
-                        移到前一行的第一个非空字符

在vi中添加文本

命令                         插入动作
a                        在光标后插入文本
A                        在当前行插入文本
i                        在光标前插入文本
I                        在当前行前插入文本
o                        在当前行的下边插入新行
O                        在当前行的上边插入新行
:r file                  读入文件file内容,并插在当前行后
:nr file                 读入文件file内容,并插在第n行后
escape                   回到命令模式
^v char                  插入时忽略char的指定意义,这是为了插入特殊字符

在vi中删除文本

命令                         删除操作
x                        删除光标处的字符,可以在x前加上需要删除的字符数目
nx                       从当前光标处往后删除n个字符
X                        删除光标前的字符,可以在X前加上需要删除的字符数目
nX                       从当前光标处往前删除n个字符
dw                       删至下一个字的开头
ndw                      从当前光标处往后删除n个字
dG                       删除行,直到文件结束
dd                       删除整行
ndd                      从当前行开始往后删除
db                       删除光标前面的字
ndb                      从当前行开始往前删除n字
:n,md                    从第m行开始往前删除n行
d或d$                    从光标处删除到行尾
dcursor_command          删除至光标命令处,如dG将从当产胆行删除至文件的末尾
^h或backspace            插入时,删除前面的字符
^w                       插入时,删除前面的字

修改vi文本

每个命令前面的数字表示该命令重复的次数
命令                         替换操作
rchar                    用char替换当前字符
R text escape            用text替换当前字符直到换下Esc键
stext escape             用text代替当前字符
S或cctext escape         用text代替整行
cwtext escape            将当前字改为text
Ctext escape             将当前行余下的改为text
cG escape                修改至文件的末尾
ccursor_cmd text escape  从当前位置处到光标命令位置处都改为text

在vi中查找与替换

命令                         查找与替换操作
/text                    在文件中向前查找text
?text                    在文件中向后查找text
n                        在同一方向重复查找
N                        在相反方向重复查找
ftext                    在当前行向前查找text
Ftext                    在当前行向后查找text
ttext                    在当前行向前查找text,并将光标定位在text的第一个字符
Ttext                    在当前行向后查找text,并将光标定位在text的第一个字符
:set ic                  查找时忽略大小写
:set noic                查找时对大小写敏感
:s/oldtext/newtext       用newtext替换oldtext
:m,ns/oldtext/newtext    在m行通过n,用newtext替换oldtext
&                        重复最后的:s命令
:g/text1/s/text2/text3   查找包含text1的行,用text3替换text2
:g/text/command          在所有包含text的行运行command所表示的命令
:v/text/command          在所有不包含text的行运行command所表示的命令

在vi中复制文本

命令                         复制操作
yy                       将当前行的内容放入临时缓冲区
nyy                      将n行的内容放入临时缓冲区
p                        将临时缓冲区中的文本放入光标后
P                        将临时缓冲区中的文本放入光标前
dsfsd "(a-z)nyy          复制n行放入名字为圆括号内的可命名缓冲区,省略n表示当前行
"(a-z)ndd                删除n行放入名字为圆括号内的可命名缓冲区,省略n表示当前行
"(a-z)p                  将名字为圆括号的可命名缓冲区的内容放入当前行后
"(a-z)P                  将名字为圆括号的可命名缓冲区的内容放入当前行前

在vi中撤消与重复

命令                         撤消操作
u                        撤消最后一次修改
U                        撤消当前行的所有修改
.                        重复最后一次修改
,                        以相反的方向重复前面的f、F、t或T查找命令
;                        重复前面的f、F、t或T查找命令
"np                      取回最后第n次的删除(缓冲区中存有一定次数的删除内容,一般为9)
n                        重复前面的/或?查找命令
N                        以相反方向重复前面的/或?命令

保存文本和退出vi

命令                         保存和/或退出操作
:w                       保存文件但不退出vi
:w file                  将修改保存在file中但不退出vi
:wq或ZZ或:x              保存文件并退出vi
:q!                      不保存文件,退出vi
:e!                      放弃所有修改,从上次保存文件开始再编辑

vi中的选项

选项                         作用
:set all                 打印所有选项
:set nooption            关闭option选项
:set nu                  每行前打印行号
:set showmode            显示是输入模式还是替换模式
:set noic                查找时忽略大小写
:set list                显示制表符(^I)和行尾符号
:set ts=8                为文本输入设置tab stops
:set window=n            设置文本窗口显示n行

vi的状态

选项                         作用
:.=                      打印当前行的行号
:=                       打印文件中的行数
^g                       显示文件名、当前的行号、文件的总行数和文件位置的百分比
:l                       使用字母"l"来显示许多的特殊字符,如制表符和换行符

在文本中定位段落和放置标记

选项                         作用
{                        在第一列插入{来定义一个段落
[[                       回到段落的开头处
]]                       向前移到下一个段落的开头处
m(a-z)                   用一个字母来标记当前位置,如用mz表示标记z
'(a-z)                   将光标移动到指定的标记,如用'z表示移动到z

在vi中连接行

选项                         作用
J                        将下一行连接到当前行的末尾
nJ                       连接后面n行

光标放置与屏幕调整

选项                         作用
H                        将光标移动到屏幕的顶行
nH                       将光标移动到屏幕顶行下的第n行
M                        将光标移动到屏幕的中间
L                        将光标移动到屏幕的底行
nL                       将光标移动到屏幕底行上的第n行
^e(ctrl+e)               将屏幕上滚一行
^y                       将屏幕下滚一行
^u                       将屏幕上滚半页
^d                       将屏幕下滚半页
^b                       将屏幕上滚一页
^f                       将屏幕下滚一页
^l                       重绘屏幕
z-return                 将当前行置为屏幕的顶行
nz-return                将当前行下的第n行置为屏幕的顶行
z.                       将当前行置为屏幕的中央
nz.                      将当前行上的第n行置为屏幕的中央
z-                       将当前行置为屏幕的底行
nz-                      将当前行上的第n行置为屏幕的底行

vi中的shell转义命令

选项                         作用
:!command                执行shell的command命令,如:!ls
:!!                      执行前一个shell命令
:r!command               读取command命令的输入并插入,如:r!ls会先执行ls,然后读入内容
:w!command               将当前已编辑文件作为command命令的标准输入并执行command命令,如:w!grep all
:cd directory            将当前工作目录更改为directory所表示的目录
:sh                      将启动一个子shell,使用^d(ctrl+d)返回vi
:so file                 在shell程序file中读入和执行命令

vi中的宏与缩写
(避免使用控制键和符号,不要使用字符K、V、g、q、v、*、=和功能键)

选项                         作用
:map key command_seq     定义一个键来运行command_seq,如:map e ea,无论什么时候都可以e移到一个字的末尾来追加文本
:map                     在状态行显示所有已定义的宏
:umap key                删除该键的宏
:ab string1 string2      定义一个缩写,使得当插入string1时,用string2替换string1。当要插入文本时,键入string1然后按Esc键,系统就插入了string2
:ab                      显示所有缩写
:una string              取消string的缩写

在vi中缩进文本

选项                         作用
^i(ctrl+i)或tab          插入文本时,插入移动的宽度,移动宽度是事先定义好的
:set ai                  打开自动缩进
:set sw=n                将移动宽度设置为n个字符
n<<                      使n行都向左移动一个宽度
n>>                      使n行都向右移动一个宽度,例如3>>就将接下来的三行每行都向右移动一个移动宽度
分页: 1/23 第一页 1 2 3 4 5 6 7 8 9 10 下页 最后页 [ 显示模式: 摘要 | 列表 ]