APUE之进程笔记(下)

1. 进程组
    进程组是一个或多个进程的集合,同一进程中的各进程接收来自同一终端的各种信号。
    每个进程组都有一个组长进程,组长进程ID就是该进程组ID。
    只要进程组中有一个进程存在,该进程组就存在(跟组长进程是否提前终止无关)。
    /* API  : pid_t getpgrp(void)
     * 描述 : 返回调用进程的进程组ID
     */
    /* API  : pid_t getpgid(pid_t pid)
     * 描述 : 返回指定进程的进程组ID
     * @pid 为0时表示返回调用进程的进程组ID
     */
    /* API  : int setpgid(pid_t pid,pid_t pgid)
     * 描述 : 设置指定进程的进程组ID
     * @pid  为0时表示修改调用进程自身的进程组ID
     * @pgid 为0时表示使用指定进程的进程ID作为要设置的进程组ID
     *
     * 备注 : 根据不同入参值,本API可以产生2类不同语义:
     *              将指定进程加入一个现有的进程组;
     *              以指定进程为组长进程创建一个新的进程组。
     *
     *        一个进程只能为自身或其子进程设置进程组ID
     */

    保证一个进程不是组长进程的方法:先调用fork,然后使其父进程终止,由于子进程的进程组ID继承自父进程,可以确保子进程不是一个进程组的组长。

2. 会话
    会话是一个或多个进程组的集合。
    每个会话都有一个会话首进程,会话首进程肯定又是一个进程组的组长进程。
    每个会话最多可以有一个控制终端(也可以没有),并且只有会话首进程拥有获取控制终端的资格(这一点很重要)。
    每个会话最多可以有一个前台进程组(也可以没有),剩下的都是后台进程组。
    SIGINT 和 SIGQUIT 这两个信号都只会专门作用于每个前台进程。
    因为跟终端连接断开而产生的 SIGHUP 信号只会发送给控制进程。
    /* API  : pid_t getsid(pid_t pid)
     * 描述 : 返回指定进程的会话首进程的进程组ID(可以简单理解为就是指定进程所在会话的会话ID)
     * @pid 为0时表示返回调用进程的会话ID
     */
    /* API  : pid_t setsid(void)
     * 描述 : 创建一个新会话(前提是调用本API的进程不是一个进程组的组长进程,否则创建失败)
     *
     * 备注 : 本API具体执行了以下这些操作:
     *              调用进程将变成新会话的会话首进程;
     *              如果调用进程关联了一个控制终端,则将其切断,也就确保新的会话首进程没有控制终端;
     *              调用进程将成为一个新进程组的组长进程
     */

    后台进程和前台进程的本质区别在于是否关闭了标准输入。

3. 守护进程
    守护进程的唯一特性是没有控制终端(需要特别注意的是,没有控制终端不意味着关闭了标准输出),除此之外,以下2点并不对守护进程构成约束:
            大部分用户层守护进程都是进程组的组长进程;
            大部分用户层守护进程都是会话的首进程.
    /* API  : int daemon(int nochdir,int noclose)
     * 描述 : 创建守护进程
     * @nochdir     为0时将进程当前工作目录更改为根目录"/",非0时表示不修改当前工作目录
     * @noclose     为0时将进程标准输入、标准输出、标准错误都重定向到/dev/null,非0时表示不修改
     *
     * 备注 : 创建守护进程的过程主要分为以下几步:
     *              调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0表示不作任何屏蔽)
     *              调用fork,然后使父进程exit,目的是确保子进程不是一个进程的组长进程
     *              调用setsid创建新的会话,使调用进程成为新会话的首进程、成为新进程组的组长进程、没有控制终端
     *              将当前工作目录更改为一个已知目录(通常就是根目录"/")
     *              关闭不需要的文件描述符
     *              将进程标准输入、标准输出、标准错误都重定向到/dev/null(通常的守护进程都会这么做)
     */

4. 例子(验证没有控制终端情况下仍然继承了标准输出)
    #include 
    #include 
    #include 

    int main(int argc,char *argv[])
    {
        if(daemon(0,1) < 0){
            perror("daemon");
            return -1;
        }
        printf("current working dir : %s\n",getcwd(NULL,0));

        return 0;
    }



    操作步骤:
    [1]. 进入例子test.c所在目录,执行编译
            $ gcc -Wall test.c
    [2]. 编译成功后,在当前目录下生成对应的可执行文件a.out,执行该程序
            $ ./a.out
    [3]. 该程序创建一个守护进程(意味着没有控制终端),并输出打印,终端屏幕打印如下
                current working dir : /
    结论:显然该守护进程从父进程处继承了标准输出


你可能感兴趣的:(Linux环境编程)