Linux编程基础——后台进程 守护进程

来源:互联网 发布:徒手攀岩电影 知乎 编辑:程序博客网 时间:2024/05/06 02:48

Linux编程基础——后台进程

后台启动

默认情况下,进程是在前台运行的,这时就把shell给占据了,我们无法进行其它操作。对于那些没有交互的进程,很多时候,我们希望将其在后台启动,可以在启动参数的时候加一个'&'实现这个目的。

    tianfang > run &
    [1] 11319
    tianfang >

进程切换到后台的时候,我们把它称为job。切换到后台时会输出相关job信息,以前面的输出为[1] 11319例:[1]表示job ID是1,11319表示进程ID是11319。

切换到后台的进程,仍然可以用ps命令查看:

    tianfang > ps
      PID TTY          TIME CMD
     5170 pts/3    00:00:00 bash
    11319 pts/3    00:00:00 run
    11320 pts/3    00:00:00 ps
    tianfang >

可以通过jobs命令只查看所有job(后台进程):

    tianfang > jobs
    [1]+  Done                    run
    tianfang >

前后台间切换

可以通过bg <jobid>(background)和fg<jobid>(foreground)命令将其在前后台间状态切换。例如,对于一个前台执行的进程,可以通过Ctrl+Z命令将其切换到后台,但此时是进程被挂起了:

    tianfang > run
    ^Z
    [1]+  Stopped                 run

此时通过bg就可以让其继续执行:

    tianfang > bg 1
    [1]+ run &
    tianfang >

在后台执行后,又可以通过fg命令将其切换回前台:

    tianfang > fg 1
    run
    ^C
    tianfang >

Shell退出后进程继续执行

当Shell退出后,该Shell下运行的job会收到SIGHUB信号,进而使其退出。然而,很多时候,我们不想让其长期运行,不想受到Shell退出的影响。一种方式就是通过disown -h <psID>命令,让特定job忽略SIGHUB信号

    tianfang > run &
    [1] 24582
    tianfang > disown -h 24582
    tianfang >

由于需要动态的进程号作为参数,要用脚本运行的话,则可以通过%1来获取这个进程号

    tianfang > run &
    [1] 25659
    tianfang > disown -h %1
    tianfang >

退出Shell的继续执行的方法不知这一种,文章让进程在后台可靠运行中就介绍了几种常用的方法,我就喜欢其中所说的带括号的启动方式。

    tianfang >(run &)
    tianfang >

守护进程

如果一个进程永远都是以后台方式启动,并且不能受到Shell退出影响而退出,一个正统的做法是将其创建为守护进程。守护进程值得是系统长期运行的后台进程,类似Windows服务。守护进程信息通过ps –a无法查看到,需要用到–x参数,当使用这条命令的时候,往往还附上-j参数以查看作业控制信息,其中TPGID一栏为-1就是守护进程。

    tianfang > ps xj
     PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
      953  1190  1190  1190 ?           -1 Ss    1000   0:00 /bin/sh /usr/bin/startkde
        1  1490  1482  1482 ?           -1 Sl    1000   0:00 /usr/bin/VBoxClient –seamless
        1  1491  1477  1477 ?           -1 Sl    1000   0:00 /usr/bin/VBoxClient –display

创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。成功调用该函数的结果是:

  • 创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id
  • 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id
  • 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。

一个示例如下:

    #include<stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>

    void daemonize(void)
    {
        pid_t  pid;

        /*
         * Become a session leader to lose controlling TTY.
         */
        if ((pid = fork()) < 0) {
            perror("fork");
            exit(1);
        } else if (pid != 0) /* parent */
            exit(0);
        setsid();

        /*
         * Change the current working directory to the root.
         */
        if (chdir("/") < 0) {
            perror("chdir");
            exit(1);
        }

        /*
         * Attach file descriptors 0, 1, and 2 to /dev/null.
         */
        close(0);
        open("/dev/null", O_RDWR);
        dup2(0, 1);
        dup2(0, 2);
    }

    int main(void)
    {
        daemonize();
        while(1)
            sleep(1);
    }

为了确保调用setsid的进程不是进程组的Leader,首先fork出一个子进程,父进程退出,然后子进程调用setsid创建新的Session,成为守护进程。

按照守护进程的惯例,通常将当前工作目录切换到根目录,将文件描述符0、1、2重定向到/dev/null。Linux也提供了一个库函数daemon(3)实现我们的daemonize函数的功能,它带两个参数指示要不要切换工作目录到根目录,以及要不要把文件描述符0、1、2重定向到/dev/null。

    tianfang > run
    tianfang > ps xj | grep run
        1  2665  1868  1868 ?           -1 Sl    1000   0:05 kdeinit4: krunner [kdeinit]                  
        1 27506 27506 27506 ?           -1 Ss    1000   0:00 run
    25662 27508 27507 25662 pts/2    27507 S+    1000   0:00 grep --color=auto run
    tianfang >

运行这个程序,它变成一个守护进程,不再和当前终端关联。用ps命令看不到,必须运行带x参数的ps命令才能看到。另外还可以看到,用户关闭终端窗口或注销也不会影响守护进程的运行。

最常用的方法是ps -aux,然后再利用一个管道符号导向到grep去查找特定的进程,然后再对特定的进程进行操作
ps -ef是以全格式显示当前所有的进程

Kill后台进程

 

查看状态:

$ jobs -l

[1]+ 1471 Running nohup ./loop.py > loop.dt 2>&1 &

或:

$ ps -ef | grep 1471

user 1471 30101 99 22:12 pts/0 00:02:58 /usr/bin/python ./loop.py

user 1539 30101 0 22:15 pts/0 00:00:00 grep --color=auto 1471

$ ps -ef | grep loop.py

 

$ kill 1471

则Kill该后台进程

 

 

0 0