Linux进程管理

本文深入解析Linux进程的概念,包括进程状态、创建与回收机制,以及fork、exec族、wait/waitpid等关键函数的使用,帮助读者理解进程管理的核心原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍进程前,我们先来理解一下Linux的虚拟内存

虚拟内存(32位)

在这里插入图片描述

一、程序

  • 程序是存放在磁盘文件中的可执行文件,未执行。

二、进程

  • 程序被执行,运行时被称为进程,代码运行时,所需要的资源会分配到对应的虚拟内存空间中
  • 进程默认最大可占用空间为4G,但是并不会给进程分配4G的空间,会根据所占用实际的大小,用多少给多少。
  • 每个linux进程都一定有一个唯一的数字标识符,称为进程ID。
(一)进程的状态
  • 运行状态R
  • 可中断睡眠状态S
  • 不可中断睡眠状态D
  • 暂停状态T
  • 僵死状态Z
  • 退出状态X
    在这里插入图片描述
(二)创建进程
  • 头文件
    #include <sys/types.h>
    #include <unistd.h>
  • 函数
    pid_t fork(void);
    该函数一次调用,两次返回
    返回值:子进程中返回值为0,父进程中返回值为子进程ID,出错返回-1
  • 注意
    该函数读时共享,写时复制,那么怎么理解这句话?
    当fork()调用时,会将完全代码拷贝一份,但是PID不会拷贝,可通过区分PID来识别父子进程,但是并不会真正直接复制,只有当子进程中有变量更改时,才会复制代码,以达到节省空间的目的。
  • fork创建之后,父进程先执行还是子进程先执行是不确定的。这取决于内核所使用的调度算法

在这里插入图片描述

(三)僵进程
  • 当子进程结束时,父进程还存在,会遗留PCB在内核中,不做事,占用系统资源。

  • 那么如何回收僵进程?
    可通过wait/waitpid函数回收僵进程

  • 那么为什么会出现僵进程?
    为父进程提供手段,知道子进程是怎么终止的,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息

    wait/waitpid

    头文件
    #include <sys/types.h>
    #include <sys/wait.h>

    函数原型
    pid_t wait(int *status);
    pid_t waitpid(pid_t pid, int *status, int options);

    若调用成功则返回清理掉的子进程 id,出错则返回-1。
    父进程调用 wait或waitpid时可能会有几种情况:

    • 阻塞(如果它的所有子进程都还在运行)。
    • 带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)。
    • 出错立即返回(如果它没有任何子进程)。

    wait/waitpid两个函数的区别是:

    • 如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。
    • wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程。
(四)孤儿进程
  • 父进程结束,子进程存在时称作孤儿进程,此时PID为1的Init进程(孤儿所)会收留这个孤儿进程,并由init进程对它们完成状态收集工作。
    在这里插入图片描述
(五)进程原语
  • fork
  • exec族
  • wait/waitpid

前面已经介绍了其中的两种接下来介绍exec族,那么什么是exec族?有什么作用?

  • 用fork创建子进程后执行的是和父进程相同的程序(可能会不同),这时子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。
  • 调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

举一个例子

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


int main(void)
{
	printf("Hello World\n");

	execl("/bin/ls","ls","-l",NULL);

	printf("Hello Man\n");

	return 0;
}

运行结果
在这里插入图片描述
正常情况程序一个是打印完hello world 任何再打印hello man再退出,但是该程序执行完hello world后并没有执行hello man,这是因为执行了execl函数,该函数直接将接下来的代码变成执行‘ls -l’的Linux指令,然后结束程序

在这里插入图片描述

  • exec族的函数原型如下,功能基本一样,传递的参数不同
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

*调用的实例

char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execv("/bin/ps", ps_argv);
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execvp("ps", ps_argv);

其中NULL为哨兵,标志着参数结尾

注意
  • 通常fork会与exec族搭配使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值