进程管理 - d09

第一课   编程基础
第二课   内存管理
第三课   文件系统
第四课   进程管理
第五课   信号处理
第六课   进程通信
第七课   网络通信
第八课   线程管理
第九课   线程同步
第十课   综合案例
------------------

一、基本概念

1.进程与程序
    1)进程就是运行中的程序。一个运行着的程序,
        可能有多个进程。进程在操作系统中完成特定的任务。
        进程是一个内存的概念,就是内存中的一个数据块,其中包括代码和数据。
    2)程序是存储在磁盘上,
        包含可执行的机器指令和数据的静态实体。

    进程或任务是处于活动状态的程序。
    程序是一个文件的概念,其中包含了代码和数据。

2.进程的分类
    1)交互进程:通过一个人机界面与用户交换信息。
    2)批量进程:在无人值守的情况下,集中执行一个复杂而且耗时的任务。
    3)守护进程(幽灵进程/精灵进程)

3.查看进程

    1)简单形式
        $ ps
        以简略的格式显示当前用户拥有控制终端的进程信息。

    2)BSD风格常用选项
        $ ps axu
            a - 所有用户有控制终端的进程
            x - 包括无控制终端的进程
            u - 详尽方式显示
            w - 增加列宽

    3)SVR4风格常用选项
        $ ps -efl
            -e/-A  - 所用用户的进程
            -a     - 当前终端的进程
            -u 用户名/用户ID - 特定用户的进程
            -g 组名/组ID     - 特定组的进程
            -f   - 按完整格式显示
            -F   - 按更完整格式显示
            -l   - 按长格式显示

4)进程信息列表
    USER/UID:进程的实际用户ID
    PID:进程ID
    %CPU/C:处理器使用率
    %MEM:内存使用率
    VSZ:占用虚拟内存大小(KB)
    RSS:占用半导体内存大小(KB)
    TTY:终端机次设备号,“?”表示无控制终端
    STAT/S:进程状态
      O - 就绪。等待被调度。
      R - 运行。正在被处理机执行。 Linux下没有O状态,
          处于就绪状态的进程也用R表示。
      S - 可唤醒的睡眠。系统中断、获得资源,收到信号,都可被唤醒,
          转入运行状态。
      D - 不可唤醒的睡眠。只能被wake_up系统调用唤醒。
      T - 停止。收到SIGSTOP信号转入停止状态。
            收到SIGCONT信号重新转入运行状态。
      W - 等待内存分页。2.6版以后的Linux内核以取消此状态。
      X - 终止。死亡。不可见。
      Z - 僵尸。进程已经终止,但其父进程尚未回收该进程的状态
                (尸体)。
      < - 高优先级。
      N - 低优先级。
      L - 存在被锁入内存的分页。实时进程。
      s - 会话首进程。
      l - 多线程化的进程。
      + - 在前台进程组中的进程。
    START/STIME:进程开始时间
    COMMAND/CMD:进程启动命令
    F:进程标志,1 - 通过fork产生但是没有调用exec。
                 4 - 拥有超级用户权限。
    PPID:父进程的PID。
    NI:进程nice值,-20到19,衡量进程对处理器而言的友好程度。
           越小越友好。
    PRI:进程实际优先级。一个进程的实际优先级由两部分组成:
             静态优先级 = 80 + nice,60到99,值越小优先级越高
             动态优先级,系统内核可以根据进程表现进行奖惩。
             对于处理机消耗型进程,惩罚,降低优先级。
             对于I/O消耗型进程,奖励,提升优先级。
    ADDR:内核进程的起始地址。普通进程显示“-”。
    SZ:虚拟内存页数。
    WCHAN:进程正在阻塞的系统调用。
    PSR:进程当前正在被哪个处理器执行。

4.父进程、子进程、孤儿进程、僵尸进程
    1) 在一个进程中通过特定的系统调用可以创建另一个进程,
       创建进程的进程就被成为父进程,被创建的进程被称为子进程。
        进程A创建进程B,进程B创建了进程C。
        A是B的父进程,B是C的父进程。
        C是B的子进程,B是A的子进程。

        内核进程(PID=0)
        ->init进程(PID=1)
            ->xinetd
                ->in.telnetd <- 远程用户登录
                    ->login
                        ->bash
                            ->vi
                            ->gcc
                            ->a.out
                            ->...
        每个父进程可能会有1到多个子进程,
        但是每个子进程只有一个父进程――树状结构。

    2)父进程先于子进程结束,子进程就成为孤儿进程,
        同时被init进程收养,即成为init进程的子进程。
    3)子进程先于父进程结束,但是父进程没有及时回收子进程的状态(尸体),此时该子进程即为僵尸进程。
        如果父进程在结束前一直都没有回收子进程的尸体,该尸体就会被init进程回收。

    5.进程标识符(PID)
        1)系统中的每个进程都以一个唯一的标识号,即PID。
        2)任何时刻,不能存在PID相同的进程。进程一旦终止,而且没有成为僵尸,该进程的PID就可以被其它进程复用。
        3)延迟重用。
        pid_t getpid (void);
        获取调用进程的PID。永远不会失败。

二、进程ID相关的函数:getxxxid

#include <unistd.h>
getpid  - 获取进程标识
getppid - 获取父进程标识
getuid  - 获取实际用户ID,即登录shell的用户ID
getgid  - 获取实际组ID,即登录shell的组ID
geteuid - 获取有效用户ID
getegid - 获取有效组ID

进程有效用户/组ID默认情况下,取自其实际用户/组ID。
但是,如果该进程所对应的可执行文件带有设置用户/组ID位,
那么该进程的有效用户/组ID,就取自可执行文件的用户/组。

假设a.out文件的用户和组都是root,以tarena用户的身份登录系统,
并执行a.out程序,其运行结果:
    实际用户ID:1000
    实际组ID:1000
    有效用户ID:1000
    有效组ID:1000
以root修改a.out文件的权限:
    # chmod u+s a.out
    # chmod g+s a.out
    # ls -l a.out
    -rwsr-sr-x 1 root root ... a.out
再次以tarena用户的身份登录系统,并执行a.out程序,其运行结果:
实际用户ID:1000
实际组ID:1000
有效用户ID:0
有效组ID:0

三、fork

#include <unistd.h>
pid_t fork (void);
    1.创建一个子进程,失败返回-1。
    2.调用一次,返回两次。
        分别在父子进程中返回子进程的PID和0。
        利用该函数返回值的不同,分别父子进程编写处理代码。
    3.子进程是父进程的副本,
      子进程获得父进程的数据段和堆栈段(包括I/O流缓冲区)的拷贝,
      但是父子进程共享同一个代码段。
    4.fork函数被调用以后父子进程各自独立地运行,
      其被执行的先后顺序无法确定。
      某些UNIX实现,会先调度子进程。
    5.fork函数被调用以后父进程的文件描述符表,
      也会被复制一份给子进程,二者同一个文件表。
    6.如果当前线程数已经达到系统的最大线程数,
      或者当前用户的进程数已经达到该用户的最大进程数,
      那么fork函数会失败。
    $ cat /proc/sys/kernel/threads-max
    $ ulimit -u
    ioctl