Linux进程(二)--- 进程的调用与状态

proc目录下的进程

在Linux中,/proc 文件系统是一个虚拟文件系统,提供了关于内核、进程以及系统硬件的信息。每个正在运行的进程在 /proc 目录下都有一个对应的子目录,其名称为该进程的PID(进程ID)。这些子目录包含了该进程的各种信息,如内存映射、文件描述符、状态等。

以下是如何在 /proc 目录下查找进程目录的一些方法:

1. 列出所有进程目录

你可以使用 ls 命令列出 /proc 目录下的内容,PID 目录是数字命名的目录。

2. 查看特定进程的信息

假设你知道进程的PID,可以直接进入该PID目录查看详细信息。例如,PID 为 1234 的进程:

ls /proc/1234

这个目录中包含了许多文件和子目录,以下是一些常见的内容:

  • cmdline: 进程启动时的命令行。
  • cwd: 当前工作目录的符号链接。
  • environ: 进程的环境变量。
  • exe: 可执行文件的符号链接。
  • fd/: 文件描述符的目录。
  • stat: 进程状态信息。
  • status: 进程的详细状态信息。

cwd

在Linux系统中,/proc文件系统为每个正在运行的进程提供了一个虚拟目录,其中包含该进程的各种信息。/proc/[pid]/cwd是一个符号链接,指向该进程的当前工作目录 (Current Working Directory, CWD)。

当前工作目录的更改

在Linux中,更改当前工作目录(Current Working Directory, CWD)的系统调用是chdir。这个系统调用允许一个进程更改其工作目录。

chdir系统调用

chdir的定义如下:

#include <unistd.h>

int chdir(const char *path);
  • path:指向要设置为当前工作目录的路径。
  • 返回值:成功时返回0,失败时返回-1,并设置errno以指示错误。

示例:使用chdir更改当前工作目录

下面是一个使用chdir的简单C程序示例,该程序更改当前工作目录并打印新的工作目录:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

int main() {
    const char *new_path = "/tmp"; // 要更改到的目录路径

    // 更改当前工作目录
    if (chdir(new_path) != 0) {
        perror("chdir failed");
        return 1;
    }

    // 获取并打印新的工作目录
    char cwd[1024];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        printf("Current working directory changed to: %s\n", cwd);
    } else {
        perror("getcwd failed");
        return 1;
    }

    return 0;
}

运行后,程序将输出:

Current working directory changed to: /tmp

fork

fork 是 Unix 和类 Unix 操作系统中的系统调用,用于创建一个新的进程。新创建的进程称为子进程,它是调用 fork 的进程(父进程)的副本。fork 调用后,父进程和子进程将继续从调用 fork 的地方执行。

fork 调用的返回值

  • 在父进程中,fork 返回子进程的 PID。
  • 在子进程中,fork 返回 0。
  • 如果 fork 调用失败,返回 -1 并设置相应的 errno 值。

代码解析

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("before fork: i am a process, pid: %d, ppid: %d\n", getpid(), getppid());

    pid_t id = fork();
    if (id < 0) return 1;
    else if (id == 0) {
        // 子进程
    } else {
        // 父进程
    }

    printf("after fork: i am a process, pid: %d, ppid: %d, returnID: %d\n", getpid(), getppid(), id);

    sleep(2);

    return 0;
}

运行结果示例

运行这段代码后,输出可能会是类似于以下内容:

before fork: i am a process, pid: 1234, ppid: 5678
after fork: i am a process, pid: 1234, ppid: 5678, returnID: 1235
after fork: i am a process, pid: 1235, ppid: 1234, returnID: 0

为什么父进程是子进程的pid, 子进程返回值是0、

fork 系统调用在创建一个新的子进程时,会返回不同的值给父进程和子进程。这是设计 fork 的一个特性,目的是让父进程和子进程能够知道它们各自的身份,并能够采取不同的行为。具体来说:

  • 在父进程中fork 返回子进程的PID(一个正数)。
  • 在子进程中fork 返回 0。
父进程需要知道子进程的PID
  1. 管理和控制子进程:父进程通常需要知道子进程的PID,以便后续操作。例如,父进程可能需要等待子进程完成 (waitwaitpid),检查子进程的状态,或者向子进程发送信号。
  2. 跟踪多个子进程:在父进程中,可以创建多个子进程。每个 fork 调用返回的子进程PID不同,父进程可以通过这些PID来区分和管理不同的子进程。

fork函数为什么会返回两个值

父进程返回子进程的PID
  1. 子进程管理

    • 父进程需要知道子进程的PID,以便在需要时对子进程进行管理。例如,父进程可能需要等待子进程完成(使用waitwaitpid),或向子进程发送信号(使用kill)。
  2. 多子进程管理

    • 父进程可能会创建多个子进程。每次调用fork时,父进程可以通过fork返回的PID来区分和管理不同的子进程。
子进程返回0
  1. 执行不同的代码路径

    • 子进程返回0,使得子进程可以通过检查fork的返回值来执行不同的代码路径。这是区分父进程和子进程的常用方法。例如:
      if (pid == 0) {
          // 子进程执行的代码
      } else {
          // 父进程执行的代码
      }
      
    • 避免重复PID检查

      • 如果fork在子进程中返回父进程的PID,子进程将需要额外的步骤来区分自己是子进程还是父进程。通过返回0,子进程可以立即知道自己是新创建的进程。

代码示例

以下是一个示例程序,展示了如何使用fork并处理父进程和子进程的不同返回值:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        // fork 失败
        perror("fork failed");
        return 1;
    } else if (pid == 0) {
        // 子进程
        printf("I am the child process, my PID is %d, my parent PID is %d\n", getpid(), getppid());
        // 子进程特定代码
    } else {
        // 父进程
        printf("I am the parent process, my PID is %d, I created a child process with PID %d\n", getpid(), pid);
        // 等待子进程结束
        wait(NULL);
        // 父进程特定代码
    }

    return 0;
}

输出示例

运行上述程序,可能会产生如下输出:

I am the parent process, my PID is 1234, I created a child process with PID 1235
I am the child process, my PID is 1235, my parent PID is 1234

fork返回的pid怎么可能同一个变量,即等于0,又大于0

fork 调用中,确实是同一个变量可以同时被赋值为 0 和一个正数,这看起来可能会令人困惑。实际上,这是因为 fork 调用后,代码是在两个独立的进程中执行的:父进程和子进程。在 fork 调用完成后,这两个进程独立运行并各自拥有自己的变量副本。

详细解释

  1. fork 调用后创建两个独立的进程

    • fork 被调用时,操作系统会创建一个新的进程(子进程),它是调用 fork 的进程(父进程)的副本。
    • 子进程继承了父进程的几乎所有属性,包括代码、数据、文件描述符等。
  2. 变量 pid 在两个进程中的值不同

    • 在父进程中,fork 返回子进程的 PID,这是一个正数。
    • 在子进程中,fork 返回 0。

虽然父进程和子进程在 fork 调用之后继续执行同一段代码,但它们是在各自独立的进程空间中运行的。因此,变量 pid 在父进程和子进程中的值是独立的。具体来说:

  • 父进程中,pid 被赋值为子进程的 PID(一个正数)。
  • 子进程中,pid 被赋值为 0。

进程的状态

在Linux和其他Unix-like操作系统中,进程的状态反映了进程当前所处的执行阶段或情况。理解这些状态对于系统管理、调试和性能优化至关重要。以下是进程可能的几种主要状态及其详解:

运行状态 (Running)

  • 状态代码:R
  • 描述:进程正在运行或准备运行。这意味着进程正在CPU上执行,或是处于可调度状态,等待被调度器分配CPU时间。
  • 示例:一个正在执行计算任务的进程。

可中断的睡眠状态 (Interruptible Sleep)

  • 状态代码:S
  • 描述:进程正在等待某个条件的满足(如I/O操作完成、资源可用等),可以被信号中断。进入这个状态的进程不会消耗CPU时间。
  • 示例:一个等待磁盘I/O完成的进程。

不可中断的睡眠状态 (Uninterruptible Sleep)

  • 状态代码:D
  • 描述:进程正在等待无法被中断的事件,如等待硬件设备响应。此状态通常用于内核态操作,避免进程在关键的系统操作过程中被中断。
  • 示例:一个等待硬件设备响应的驱动程序进程。

停止状态 (Stopped)

  • 状态代码:T
  • 描述:进程被暂停或停止。通常由信号(如SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU)引起,或者进程正在被调试。
  • 示例:用户按下Ctrl+Z暂停了一个正在前台运行的进程。

僵尸状态 (Zombie)

  • 状态代码:Z
  • 描述:进程已经终止,但其父进程尚未读取其退出状态信息。僵尸进程不会消耗系统资源,但会占用进程表项,过多的僵尸进程可能导致系统问题。
  • 示例:一个子进程已经结束,但父进程尚未调用waitwaitpid读取子进程的退出状态。

终止状态 (Terminated)

  • 状态代码:X
  • 描述:进程已经结束并已从进程表中移除。此状态在进程表中不可见,因为进程信息已经被清除。
  • 示例:一个正常退出的进程,其退出状态已被父进程读取。

孤儿状态 (Orphan)

  • 描述:当一个进程的父进程终止后,该进程就成为孤儿进程。孤儿进程会被init进程(PID 1)收养,以确保其能正确终止。
  • 示例:父进程意外崩溃时仍在运行的子进程。

附加状态

  • 状态代码:这些状态通常以大写字母表示,可以组合使用。
    • <:高优先级进程。
    • N:低优先级进程。
    • L:有些页面被锁在内存中。
    • s:包含子进程的会话领导。
    • l:多线程(使用 CLONE_THREAD 标志创建的)。
    • +:前台进程组。
  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值