APUE_notes项目解析:深入理解进程关系与作业控制

APUE_notes项目解析:深入理解进程关系与作业控制

【免费下载链接】APUE_notes 【免费下载链接】APUE_notes 项目地址: https://gitcode.com/gh_mirrors/ap/APUE_notes

引言

在Unix/Linux系统中,进程关系是一个核心概念,它决定了进程如何组织、如何交互以及如何受终端控制。本文将基于APUE_notes项目中的内容,深入剖析进程关系、会话、进程组以及作业控制等关键概念,帮助读者全面理解Unix/Linux系统的进程管理机制。

终端登录过程解析

BSD系统登录流程

  1. init进程:系统启动时,内核创建PID为1的init进程,这是所有用户进程的始祖。

  2. 终端初始化

    • init读取/etc/ttys文件
    • 为每个允许登录的终端设备fork子进程
    • 子进程exec执行getty程序
  3. getty工作

    • 打开终端设备(文件描述符0、1、2)
    • 显示"login:"提示
    • 获取用户名后调用login程序
  4. login验证

    • 获取用户密码并验证
    • 验证成功后设置用户环境:
      • 更改工作目录到用户主目录
      • 设置终端所有权和权限
      • 初始化环境变量(HOME, SHELL等)
    • 最后启动用户shell

不同系统的差异

  • Mac OS X:使用launchd代替init,默认提供图形终端
  • Linux:使用/etc/inittab代替/etc/ttys

进程组详解

基本概念

进程组是一个或多个进程的集合,具有以下特点:

  • 同一进程组内的进程接收来自同一终端的信号
  • 每个进程组有唯一的进程组ID
  • 进程组组长是创建该组的进程,其PID等于组ID
  • 进程组生命周期独立于组长进程

关键函数

  1. 获取进程组ID

    pid_t getpgrp(void);  // 获取调用进程的组ID
    pid_t getpgid(pid_t pid);  // 获取指定进程的组ID
    
  2. 设置进程组ID

    int setpgid(pid_t pid, pid_t pgid);
    

    使用规则:

    • pid=pgid:指定进程成为新组长
    • pid=0:使用调用者PID
    • pgid=0:使用pid作为新组ID

重要限制:进程只能为自己或子进程设置组ID,且必须在子进程exec前设置。

会话机制深入

会话概念

会话是一个或多个进程组的集合,特点包括:

  • 每个会话有唯一会话ID(等于首进程的PID)
  • 一个会话最多有一个控制终端
  • 会话首进程是与终端建立连接的进程

会话管理函数

  1. 创建新会话
    pid_t setsid(void);
    

    成功时:

    • 调用进程成为新会话首进程
    • 创建新进程组(调用进程为组长)
    • 断开原有控制终端

关键点:调用进程不能是进程组组长,通常先fork让子进程调用。

  1. 获取会话ID
    pid_t getsid(pid_t pid);
    

作业控制全解析

基本概念

作业控制允许用户在单个终端上管理多个任务:

  • 一个作业对应一个进程组
  • 分为前台作业和后台作业
  • 通过特殊控制字符管理:
    • Ctrl+C (SIGINT)
    • Ctrl+\ (SIGQUIT)
    • Ctrl+Z (SIGTSTP)

终端控制函数

  1. 获取/设置前台进程组

    pid_t tcgetpgrp(int fd);
    int tcsetpgrp(int fd, pid_t pgrpid);
    
  2. 获取会话ID

    pid_t tcgetsid(int fd);
    

作业控制行为

  1. 后台作业限制

    • 尝试读取终端 → 收到SIGTTIN信号
    • 尝试写入终端 → 可能收到SIGTTOU信号
  2. 孤儿进程组处理

    • 定义:组中所有进程的父进程都不在同一会话的其他组中
    • 处理:向包含停止进程的孤儿进程组发送SIGHUP和SIGCONT信号

实际应用示例

进程组操作示例

void demo_process_group() {
    pid_t pid = fork();
    if (pid == 0) {  // 子进程
        setpgid(0, 0);  // 创建新进程组
        printf("Child: PID=%d, PGID=%d\n", getpid(), getpgrp());
    } else {  // 父进程
        printf("Parent: PID=%d, PGID=%d\n", getpid(), getpgrp());
        wait(NULL);
    }
}

会话创建示例

void demo_session() {
    pid_t pid = fork();
    if (pid == 0) {
        setsid();  // 子进程创建新会话
        printf("New session ID: %d\n", getsid(0));
    } else {
        wait(NULL);
    }
}

总结

理解进程关系是掌握Unix/Linux系统编程的关键。通过本文的讲解,我们了解到:

  1. 进程通过进程组组织,通过会话管理终端连接
  2. 作业控制机制允许灵活管理前后台任务
  3. 孤儿进程组有特殊处理逻辑
  4. 相关系统调用提供了精细的控制能力

这些概念构成了Unix多任务环境的基础,对于开发可靠的系统程序至关重要。

【免费下载链接】APUE_notes 【免费下载链接】APUE_notes 项目地址: https://gitcode.com/gh_mirrors/ap/APUE_notes

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值