进程管理详解


进程管理深入探讨

在操作系统中,进程是资源分配和调度的基本单位。理解进程的概念及其相关机制对于掌握系统编程和操作系统的原理至关重要。本文将探讨进程、并发与并行的基本概念,以及 Linux 中进程管理的常用函数和特性。

进程相关概念

1. 进程

  • 定义: 进程是一个正在执行的程序,是系统进行资源分配和调度的基本单位。每个进程都有自己的地址空间、数据栈和其他辅助数据,用于跟踪执行。
  • 状态: 进程在运行期间可以经历多种状态,常用的状态模型主要有三状态模型和五状态模型。
    • 三状态模型: 就绪、运行、阻塞。
    • 五状态模型: 就绪、运行、阻塞、结束、创建。

2. 单道程序与多道程序

  • 单道程序: 同一时间内只有一个进程在执行,其他程序需等待。这种模式下,CPU 的利用率较低。
  • 多道程序: 允许多个进程同时加载到内存中,通过时间片轮转实现并发执行,提高了 CPU 的利用率。

3. 并行与并发

  • 并行: 多个进程真正同时执行,例如在多核处理器上,多个进程可以在不同的 CPU 核心上同时运行。
  • 并发: 多个进程交替执行,虽然它们在微观上是交替的,但在宏观上看起来像是在同时执行。操作系统通过快速切换进程来实现这种效果。

进程组与会话

  • 进程组: 由多个相关进程组成的集合,第一个进程称为组长进程。进程组用于信号的发送和接收。
  • 会话: 由一个或多个进程组组成,第一个进程称为会长进程,通常是在终端中启动的 shell 进程。会话用于管理终端和进程组。

终端

终端设备用于连接计算机的输入输出,常见的终端文件地址为 /dev/tty。终端可以是物理设备,也可以是虚拟设备。

进程管理函数

以下是 Linux 中与进程管理相关的几个重要函数和系统调用:

1. 进程创建

  • fork(): 创建一个新进程,返回值:
    • 子进程:0
    • 父进程:新创建的子进程的 PID
    • 错误: -1
pid_t fork();
  • exec系列函数: 用于在当前进程中执行新程序,替换当前进程的映像。

2. 进程状态获取

  • getpid(): 获取当前进程的进程号。
  • getppid(): 获取当前进程的父进程号。
  • getpgid(): 获取指定进程的进程组号。
  • getpgrp(): 获取当前进程的进程组号。
  • getsid(): 获取当前进程所在的会话号。

3. 进程回收

  • wait(): 阻塞父进程,直到一个子进程结束,并获取子进程退出状态。
pid_t wait(int *stat);
  • waitpid(): 允许父进程回收已结束的特定子进程。
pid_t waitpid(pid_t pid, int *stat, int flag);

4. 进程退出

  • exit(): 用于正常退出程序,返回状态码。
  • _exit(): 系统调用,直接退出,不会执行退出处理函数。
  • 退出状态码范围为 0-255,0 代表正常退出,非0 代表异常退出。

5. 进程休眠

  • sleep(int s): 让进程休眠 s 秒。
  • usleep(int us): 让进程休眠 us 微秒。

特殊进程

1. 僵尸进程

当子进程结束,但父进程未调用 wait() 进行回收时,会导致僵尸进程的产生,浪费系统资源。僵尸进程仍占用进程表项,但不占用其他资源。

2. 孤儿进程

当父进程结束,子进程仍在运行,这时子进程成为孤儿进程,并被 init 进程(PID 1)接管。孤儿进程不会造成资源浪费。

3. 守护进程

守护进程是一种后台进程,通常用于执行定期任务,特点是其生命周期不受用户会话的限制。守护进程在系统启动时创建,并在系统关闭时结束。

守护进程制作步骤

  1. 忽略挂起信号:使用 signal()
  2. 创建子进程并退出父进程:使用 fork()
  3. 子进程设置会话:使用 setsid()
  4. 修改工作目录为根目录:使用 chdir("/")
  5. 设置文件掩码:使用 umask(022)
  6. 关闭标准输入输出:使用 close(0)close(1)close(2)

进程的注册与退出状态

1. 进程退出注册

可以使用 atexit(void (*fun)(void)) 注册一个函数,在调用 exit() 时自动执行。最多可以注册 32 个函数,注册的函数会在进程退出时从下向上执行。

2. 退出状态的处理

通过使用 WIFEXITED(status)WEXITSTATUS(status) 获取进程的退出状态信息。可以判断进程是否正常退出,并获取其退出状态码。

案例

多进程的创建与回收

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>
// 多进程的创建与回收
int main(int argc, char const *argv[])
{
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        int sonPid = fork();
        if (sonPid == 0)
        {
            printf("创建的子进程id=%d\n", getpid());
            break;
        }
    }

    if (i == 0)
    {
        printf("i = %d,当前进程号:%d\n", i, getpid());
    }
    else if (i == 1)
    {
        printf("i = %d,当前进程号:%d\n", i, getpid());
    }
    else if (i == 2)
    {
        printf("i = %d,当前进程号:%d\n", i, getpid());
    }
    else if (i == 3)
    {
        printf("父进程id=%d\n", getpid());
        printf("i = %d,父进程\n", i);
        while (1)
        {
            int pid = waitpid(-1, NULL, WNOHANG);
            if (pid == -1)
            {
                printf("所有子进程都已被回收\n");
                break;
            }
        }
        return 0;
    }
}

精灵进程(守护进程)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>
int main(int argc, char const *argv[])
{
    // 1 忽略SIGHUP(信号)防止被终端误杀
	// SIG_IGN:忽略指定的信号
	// signal(SIGHUP, SIG_IGN);
    signal(SIGHUP,SIG_IGN);
    // 2 创建子进程,父进程退出(必须) 所有工作在子进程中进行形式上脱离了控制终端 
    int pid = fork();
    if (pid > 0)
    {
        return 0;
    }
    // 3 在子进程中创建新会话(必须) setsid()函数使子进程完全独立出来,脱离控制 
    setsid();
    // 4 改变当前目录为根目录(不是必须) chdir()函数 防止占用可卸载的文件系统 也可以换成其它路径 
    chdir("/");
    // 5 重设文件权限掩码(不是必须)umask()函数 防止继承的文件创建屏蔽字拒绝某些权限 增加守护进程灵活性
    umask(0022);
    // 6 关闭文件描述符(不是必须) 继承的打开文件不会用到,浪费系统资源,无法卸载
    close(0);
    // close(1);
    close(2);
    // 7 开始执行守护进程核心工作(必须) 守护进程退出处理程序模型
    while (1)
    {
        sleep(3);
        printf("我是一个守护进程\n");
    }
    
    return 0;
}

```\
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值