zoukankan      html  css  js  c++  java
  • 每天学五分钟 Liunx 0011 | 服务篇:进程


    1. 进程

    程序放在硬盘中,在运行它的时候加载到内存,在内存里程序以进程的方式运行,进程有唯一的 ID ,叫 PID。
     
    写个简单的 Hellow world 程序,让它产生 PID:
    [root@test]$ cat test.cpp
    #include <stdio.h>
    #include <unistd.h>
     
    int main(void)
    {
        int i = 0;
        for(i==0;i<100;i++)
        {
            sleep(2);
            printf("Hello World
    ");
        }
    }
     
    [root@test]$ gcc test.cpp -o test.out
    [root@test]$ ./test.out &
    [1] 35166
    [root@test]$ Hello World
    Hello World
    ^C
    [root@test]$ Hello World
    ps Hello World
    -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     34824 34817  0  80   0 - 28750 do_wai 22:49 pts/15   00:00:00 -bash
    0 S root     35166 34824  0  80   0 -  1056 hrtime 22:51 pts/15   00:00:00 ./test.out
    0 R root     35174 34824  0  80   0 - 38356 -      22:51 pts/15   00:00:00 ps -lf
    [root@test]$ Hello World
    执行命令 ./test.out ,Hello World 程序被执行,产生了一个 PID 为 35166 的进程。
     
    上面示例中用到了 ps -lf 命令,该命令会查看当前终端的进程信息。各参数的意思是:
    F:进程标志(Process Flags),说明进程权限。常见号码有: 4 表示进程权限为 root,1 表示该进程只可以复制(fork),不能被执行。
    S:进程状态。S 表示 sleep,该进程正在睡眠状态 idle,可以被唤醒(signal)。R 表示 running,即该进程正在运行。T 表示 stop,该进程出于停止状态,可能是后台暂停或者 traced 状态。Z 表示 zombie 僵尸进程,该进程已经终止但是还常驻在内存中。D 表示该进程处于不可被唤醒的睡眠状态,如等待 I/O 的情况。
    UID: 执行该进程的用户身份。
    PID: 进程 ID。
    PPID: 父进程的 ID。
    C:表示 CPU 的使用率,单位为百分比。 
    PRI:Priority,表示该进程执行的优先级,系统动态调整,不可设置。
    NI: Nice,加权的优先级系数,可设置;PRI/NI 数值越小表示该进程越快被 CPU 执行。
    ADDR:内存相关。表示进程在内存的哪个部分,running 的进程一般显示 “-”。
    SZ:内存相关。表示进程用掉多少内存。
    WCHAN:表示目前进程是否正在运行,“-”表示正在运行。
    STIME: 进程的启动时间。
    TTY: 登陆用户的终端机位置,远程登陆则显示 pts/n 。
    TIME: 进程的运行时间。
    CMD: 什么命令触发了该进程。
     
     
    可以看出,进程 35166 的父进程是 34824 即 bash 的进程。
    在远程客户端登陆主机服务端时,主机会提供一个 bash 进程给远端,这样客户端就可以以 tty 终端来访问服务端主机了。在 bash 进程上执行命令时,bash 进程就成了执行命令产生进程的父进程。比如:
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     34824 34817  0  80   0 - 28750 do_wai 22:49 pts/15   00:00:00 -bash
    0 R root     38815 34824  0  80   0 - 38356 -      23:30 pts/15   00:00:00 ps -lf
    [root@test]$ bash
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     34824 34817  0  80   0 - 28750 do_wai 22:49 pts/15   00:00:00 -bash
    4 S root     38838 34824  0  80   0 - 28715 do_wai 23:30 pts/15   00:00:00 bash
    0 R root     38882 38838  0  80   0 - 38356 -      23:30 pts/15   00:00:00 ps -lf
    [root@test]$ exit
    exit
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     34824 34817  0  80   0 - 28750 do_wai 22:49 pts/15   00:00:00 -bash
    0 R root     38900 34824  0  80   0 - 38356 -      23:30 pts/15   00:00:00 ps -lf
    在 bash 进程 34824 上执行 bash 命令产生一个新的 bash 进程 38838,它的父进程正是 34824,并且父进程是处于 sleep 状态的。
     
     
    执行 Hello World 程序使用的是 ./test.out & 命令,在命令后加 & 号会将当前工作转到后台执行,同时终端界面会打印 [1] 35166,[1] 表示该工作的 ID,35166 表示产生的进程 PID。
    不过,即使工作转到后台了,程序执行的输出还是会打印到终端上。可以加个输入流,将执行的正确信息流到 std.out 中,错误信息流到 std.err 中:
    [root@test]$ ./test.out 1> std.out 2> std.err &
    [1] 41371
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     34824 34817  0  80   0 - 28750 do_wai Apr21 pts/15   00:00:00 -bash
    0 S root     41371 34824  0  80   0 -  1056 hrtime 00:00 pts/15   00:00:00 ./test.out
    0 R root     41379 34824  0  80   0 - 38356 -      00:00 pts/15   00:00:00 ps -lf
    [root@test]$ cat std.out
    [root@test]$ 
     
    数据流到 std.out 中,但是 cat std.out 却没有记录。将 Hello World 更改下重新执行:
    [root@test]$ cat test.cpp
    #include <stdio.h>
    #include <unistd.h>
     
    int main(void)
    {
        int i = 0;
        for(i==0;i<10;i++)
        {
            sleep(2);
            printf("Hello World
    ");
        }
    }
    [root@test]$ gcc test.cpp -o test.out
    [root@test]$ cat std.out
    [root@test]$ ./test.out 1> std.out 2> std.err &
    [1] 41775
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     34824 34817  0  80   0 - 28750 do_wai Apr21 pts/15   00:00:00 -bash
    0 S root     41775 34824  0  80   0 -  1056 hrtime 00:04 pts/15   00:00:00 ./test.out
    0 R root     41779 34824  0  80   0 - 38356 -      00:04 pts/15   00:00:00 ps -lf
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     34824 34817  0  80   0 - 28750 do_wai Apr21 pts/15   00:00:00 -bash
    0 R root     41870 34824  0  80   0 - 38356 -      00:05 pts/15   00:00:00 ps -lf
    [1]+  Done                    ./test.out > std.out 2> std.err
    [root@test]$ cat std.out
    Hello World
    Hello World
    Hello World
    ...
    [root@test]$
    这次 std.out 就有输出了,对比上面两个例子可以发现,当进程执行完之后才会把数据写到 std.out 中,而不是执行一行写一行,系统会有个数据缓冲区,进程在执行的时候会将数据写到缓冲区,在进程结束之后在取出缓冲区中的数据,写到文件 std.out 中。
     
     
    当然,除了 ps -lf 命令可以查看进程信息,还有诸如 ps aux 和 ps -lA 命令也可以查看进程信息。还有不少 ps 加各种选项参数的命令也也可以查看进程信息,但是最常用的还是 ps aux , ps - lA 和 ps -lf 这几个:
    [root@test:/home/robot]
    # ps aux
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.4  0.0 171620 11604 ?        Ss   14:16   0:26 /usr/lib/systemd/systemd --switched-root --system --deserialize 17
    systemd+   591  0.0  0.0  19152  6400 ?        Ss   14:17   0:00 /usr/lib/systemd/systemd-networkd
    avahi      761  0.0  0.0   9656  6244 ?        Ss   14:17   0:04 avahi-daemon: running [test
    root       766  0.0  0.0   6028  2152 ttyS0    Ss+  14:17   0:00 /sbin/agetty -o -p -- u --keep-baud 115200,38400,9600 ttyS0 vt220
    root       767  0.0  0.0   6388  1604 tty1     Ss+  14:17   0:00 /sbin/agetty -o -p -- u --noclear tty1 linux
    root       777  0.0  0.0 614464 18100 ?        Ssl  14:17   0:04 python3 -u /opt/nokia/libexec/StorageUtils/script/lsa/local_storage_agent.py
    avahi      778  0.0  0.0   8944   432 ?        S    14:17   0:00 avahi-daemon: chroot helper
    _test+   779  0.0  0.0 164384  5784 ?        Ssl  14:17   0:01 /opt/nokia/bin/eventdaemon --global
    root       784  0.0  0.0      0     0 ?        Z    14:17   0:00 [cat] <defunct>
    root       785  0.0  0.0   7672  1196 ?        S    14:17   0:00 avahi-publish -a test.local
    _test+   787  0.0  0.0  26736 16288 ?        Ss   14:17   0:00 /opt/nokia/test-acl/bin/ipsetmanager
    ...
     
    [root@test:/home/robot]
    # ps -lA
    F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
    4 S     0     1     0  0  80   0 - 42905 SyS_ep ?        00:00:26 systemd
    0 S     0   531   525  0  80   0 -  1455 hrtime ?        00:00:00 schedmon
    4 S     0   534     1  0  80   0 -  1879 hrtime ?        00:00:00 hypertracerolcd
    4 S     0   550     1  0  80   0 - 205661 core_s ?       00:00:15 python3
    4 S   192   591     1  0  80   0 -  4788 SyS_ep ?        00:00:00 systemd-network
    ...
     
    ps aux 和 ps -lA 都可以查看系统运行的所有进程,不同的是输出信息不同,ps -lA 的输出信息和 ps -lf 一致,而 ps aux 的输出信息是 %CPU / %MEM / VSZ / RSS / STAT 等等。
    各参数的意思分别是:
    %CPU:进程使用 CPU 的资源百分比;
    %MEM:进程占用物理内存百分比;
    VSZ:进程使用的虚拟内存量(KB);
    RSS:进程占用的固定内存量(KB);
    STAT:进程目前的状态。有的进程会同时有多个字符,它们代表的意思是:
        D    不可中断,Uninterruptible sleep (usually IO)
        R    正在运行,或在队列中的进程
        S    处于休眠状态
        T    停止或被追踪
        Z    僵尸进程
        X    死掉的进程
        <    高优先级
        N    低优先级
        L    有些页被锁进内存
        s    包含子进程
        +    位于后台的进程组
        l    多线程,克隆线程 multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
     
     
    除了 ps 命令查看进程信息的命令,还有 pstree 命令可查看各进程的依赖关系:
    [root@test]$ pstree -p
    systemd(1)─┬─abrt-dbus(45043)─┬─{abrt-dbus}(45044)
               │                  └─{abrt-dbus}(45046)
               ├─agetty(2037)
               ├─agetty(2039)
               ├─atd(1989)
               ├─auditd(604)─┬─audispd(19274)─┬─sedispatch(19275)
               │             │                └─{audispd}(19276)
               │             └─{auditd}(605)
               ├─automount(2029)─┬─{automount}(2030)
               │                 ├─{automount}(2031)
               │                 └─{automount}(2056)
    ...
    其中 -p 选项可查看各进程的 PID,可以看到有个 PID 为 1 的 systemd 进程,它是 Liunx 的启动的第一个进程是 systemd 进程,是所有进程的进程之王,它下面管理着很多子进程,这些子进程又会生出多个子子进程。如 PID 为 2029 的 automount 进程,它的父进程是 systemd ,它也是 2030/2031/2056 的父进程。2030/2031/2056 它们几个是兄弟进程。
     
     
    ps 和 pstree 执行的结果是静态的,而使用 top 命令可以动态查看进程的变化情况:
    [root@test]$ top -H
    top - 20:49:51 up 264 days,  3:07, 14 users,  load average: 0.13, 0.08, 0.02
    Threads: 567 total,   1 running, 565 sleeping,   0 stopped,   1 zombie
    %Cpu(s):  0.2 us,  0.1 sy,  0.0 ni, 99.6 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem : 37077464 total, 12919928 free,  2649664 used, 21507872 buff/cache
    KiB Swap:     1020 total,        0 free,     1020 used. 33678848 avail Mem
     
      PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    44644 root      20   0   12.7g 335660  21284 S  0.7  0.9   0:04.60 java
    43794 root      20   0   12.7g 323332  21332 S  0.7  0.9   0:06.44 java
    1959 fluentd   20   0 1382452  28436   4896 S  0.3  0.1  42:04.92 fluentd
     
    top 的输出信息有好几行,从上到下分别表示:
    第一行: 当前时间;系统开机到现在过了多长时间(看看 264 天没关机,这是 windows 能比的了哒);当前登陆系统的用户人数;系统在 1/5/15 分钟的平均工作负载,分别是 0.13/0.08/0.02,越小表示系统越闲置,若高于 1 表示系统 load 偏高。
    第二行: 当前进程的总量,以及进程的总体运行状态。
    第三行: CPU 的整体负载。us 表示 CPU 消耗在用户空间(user space)的时间百分比;sy 表示 CPU 消耗在内核空间(kernel space)的时间百分比;<关于用户空间和内核空间的文章可看这里>ni(niceness) 表示 CPU 消耗在 nice 进程(低优先级)的时间百分比;id(idle) 表示 CPU 消耗在闲置进程的时间百分比,值越低表示 CPU 越忙,这里 id 是 99.6 ,看来 CPU 不忙啊。wa(wait) CPU 等待外部 I/O 的时间百分比,在等待 I/O 的时间里 CPU 不能干其它事情;hi(hardware interrupt) CPU 响应硬件中断请求的时间百分比;si(software interrupt) CPU 响应软件中断请求的时间百分比;
    第四行:物理内存 Mem 使用情况;
    第五行:虚拟内存 swap 使用情况;
    第六行: PR(Priority) 进程的优先级执行顺序,越小表示越早被执行,该值由系统指定,用户不能更改;NI(nice)与优先级相关,用户可更改。
     
     

    2. 工作管理

    将程序/命令转到后台执行的形式都属于工作管理的范畴,顾名思义,工作管理就是管理 Liunx 系统下各个工作的。使用 & 将命令转到后台执行,搭配流输入将数据转到文件中,从而避免在终端界面显示,前面已经演示过了。
     
    & 命令将工作转到后台,工作是出于运行状态的。而直接使用快捷键 Ctrl + Z 会将工作转到后台并处于暂停状态:
    [root@test test]# vi log.txt
     
    [1]+  Stopped                 vi log.txt
    [root@test test]# ps -lf
    F S UID          PID    PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    0 S root      107543  107542  0  80   0 -  6374 -      15:58 pts/0    00:00:00 -bash
    0 T root      108848  107543  0  80   0 -  9126 -      15:58 pts/0    00:00:00 vi log.txt
    0 R root      109559  107543  0  80   0 - 14894 -      15:58 pts/0    00:00:00 ps -lf
    [root@test test]#
    命令 vi log.txt 产生的进程被转到后台,并且它的状态是 T stopped。
     
    jobs 命令可以查看当前工作的情况,fg 命令可将当前工作从后台转到前台,并且该工作是出于运行状态:
    [root@test test]# jobs
    [1]+  Stopped                 vi log.txt
    [root@test test]# fg %1
    vi log.txt
     
    [root@test test]# jobs
    [1]-  Stopped                 vi log.txt
    [2]+  Stopped                 vi abc
    [root@test test]# fg
    vi abc
    注意,fg 后加 job 编号即表示将该 job 从后台转到前台,前面要加 % 号。jobs 的输出 job 编号后有 + 和 - 号,+ 表示默认调用的工作,不加编号的 fg 命令会调用 + 所在的那个 job。
     
    fg 命令是将工作从后台转到前台。bg 命令会将后台出于暂停状态的工作转为运行状态,同样的,bg 后需加 % 和工作编号:
    [root@test]$ ./test.out
    Hello World
    ^Z
    [1]+  Stopped                 ./test.out
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     49612 49605  0  80   0 - 28750 do_wai 00:13 pts/15   00:00:00 -bash
    0 T root     49854 49612  0  80   0 -  1056 do_sig 00:14 pts/15   00:00:00 ./test.out
    0 R root     49880 49612  0  80   0 - 38356 -      00:14 pts/15   00:00:00 ps -lf
    [root@test]$ bg %1
    [1]+ ./test.out &
    Hello World
    [root@test]$ Hello World
    Hello World
    ^C
    [root@test]$ Hello World
    Hello World
    Hello World
     
    上面的例子中,程序转到后台执行时会循环打印 Hello World,使用 Ctrl + C 也没办法将终止程序,还是会继续打印。此时就要用到 kill 命令杀掉执行该程序的进程或者杀掉该工作。
    kill -l 命令可查看 kill 能够使用的信号:
    [root@test]$ kill -l
    1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
    6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
    11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
    16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
    21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
    26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
    31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
    38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
    43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
    48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
    53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
    58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
    63) SIGRTMAX-1  64) SIGRTMAX
     
    通常使用的最多的信号就是 1/9/15。1 SIGHUP 表示重新读取参数的配置文件,9 SIGKILL 强制删除工作/进程,15 SIGTERM 以正常方式结束一个工作/进程,如果一个工作/进程不能以正常方式结束掉,还是要使用 9 强制结束的。
    不加信号的 kill 默认使用的是 15 SIGTERM 这个信号。如果要 kill 工作的话,需要在工作编号前加 % 号,默认 kill 的是进程的 PID,下面用两种方式 kill Hello World 程序产生的进程:
    # kill -9 %n
    [root@test]$ ./test.out
    Hello World
    Hello World
    ^Z
    [1]+  Stopped                 ./test.out
    [root@test]$ kill -9 %1
    [1]+  Killed                  ./test.out
     
    # kill -9 PID
    [root@test]$ ./test.out
    Hello World
    ^Z
    [1]+  Stopped                 ./test.out
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     49612 49605  0  80   0 - 28750 do_wai 00:13 pts/15   00:00:00 -bash
    0 T root     50944 49612  0  80   0 -  1056 do_sig 00:28 pts/15   00:00:00 ./test.out
    0 R root     50950 49612  0  80   0 - 38356 -      00:28 pts/15   00:00:00 ps -lf
    [root@test]$ bg %1
    [1]+ ./test.out &
    Hello World
    [root@test]$ Hello World
    Hello World
    [1]+  Killed                  ./test.out
    [root@tu-qiaolin-683ci-1 ~]$ kill -9 50944   # exec at another tty
     
    使用 & 和 Ctrl + Z 将工作放到后台运行时,如果当前终端关闭了那么相应的工作也会被停掉(当前终端 bash 是 & 和 Ctrl + Z 的父进程,父进程关掉了,相应的子进程也会随之结束),使得工作不能继续。Liunx 提供了 nohup 命令可以将工作放到系统后台运行,即使当前终端关闭了,工作还是可以继续运行(当前终端关闭了,nohup 会将 systemd 作为自己的父进程继续执行),举例:
    [root@test]$ cat test.cpp
    #include <stdio.h>
    #include <unistd.h>
     
    int main(void)
    {
        int i = 0;
        for(i==0;i<20;i++)
        {
            sleep(2);
            printf("Hello World
    ");
        }
    }
    [root@test]$ gcc test.cpp -o test.out
    [root@test]$ nohup ./test.out &
    [1] 50772
    [root@test]$ nohup: ignoring input and appending output to ‘nohup.out’
    [root@test]$ ps -lf
    F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
    4 S root     45024 45018  0  80   0 - 28752 do_wai 20:39 pts/15   00:00:00 -bash
    0 S root     50772 45024  0  80   0 -  1056 hrtime 21:41 pts/15   00:00:00 ./test.out
    0 R root     50777 45024  0  80   0 - 38356 -      21:41 pts/15   00:00:00 ps -lf
    [root@test]$ exit
    logout
    ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    Session stopped
     
    Last login: Thu Apr 23 23:48:09 2020 from 10.140.119.177
    [root@tu-qiaolin-683ci-1 ~]$ ps -lA | grep 50772
    0 S     0 50772     1  0  80   0 -  1056 hrtime ?        00:00:00 test.out
    [root@tu-qiaolin-683ci-1 ~]$ cat test/nohup.out
    Hello World
    Hello World
    Hello World
    ...
    [root@tu-qiaolin-683ci-1 ~]$
    退出了当前终端,工作还是继续在系统后台运行,且 nohup 产生的进程被 systemd 进程给接管了。
     
     
     
    (完)
     
  • 相关阅读:
    IOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry) 转载
    iOS8.0 使用Photos.framework对相册的常用操作
    iOS 通用button 上图下字
    navigationItem的设置和titleView的设置
    iOS 设置导航栏 返回按钮文字隐藏
    iOS uitableivewCell 下划线顶格
    gitlab创建项目代码:
    iOS block用作属性封装代码
    iOS实录:GCD使用小结(一)
    代码处理 iOS 的横竖屏旋转
  • 原文地址:https://www.cnblogs.com/xingzheanan/p/12776204.html
Copyright © 2011-2022 走看看