IO进程学习——4

一、进程概述

进程概念

​ 进程简单来说就是正在执行的程序,每个程序执行一次生成一个进程,可以同时生成多个进程。

​ 程序是静态的,是保存在外存上的指令的有序集合,而进程是动态的,是在内存中。

​ 进程是程序执行和资源管理的最小单位。

进程区分

每个进程都有自己的进程号(PID),父进程(PPID),在linux内可以使用 ps -elf查看进程的信息。

获取进程PID的方法

pid_t getpid(void);
pid_t getppid(void);

进程是处于执行期的程序以及它所管理的资源(如打开的文件、挂起的信号、进程状态、地址空间等等)的总称。注意,程序并不是进程,实际上两个或多个进程不仅有可能执行同一程序,而且还有可能共享地址空间等资源。

在 Linux 中每一个进程都由 task_struct 数据结构来定义。task_struct 就是我们通常所说的 PCB。她是对进程控制的唯一手段也是最有效的手段。当我们调用 fork () 时,系统会为我们产生一个 task_struct 结构。然后从父进程那里继承一些数据,并把新的进程插入到进程树中,以待进行进程管理。因此了解 task_struct 的结构对于我们理解任务调度(在 linux 中任务和进程是同一概念)的关键。

在进行剖析 task_struct 的定义之前,我们先按照我们的理论推一下它的结构: 1、进程状态,将纪录进程在等待、运行,或死锁 2、调度信息,由哪个调度函数调度、怎样调度等 3、进程的通讯状况 4、因为要插入进程树,必须有联系父子兄弟的指针,当然是 task_struct 型 5、时间信息,比如计算好执行的时间,以便 cpu 分配 6、标号,决定改进程归属 7、可以读写打开的一些文件信息 8、进程上下文和内核上下文 9、处理器上下文 10、内存信息

因为每一个 PCB 都是这样的,只有这些结构,才能满足一个进程的所有要求。

进程的运行状态

进程的执行模式分为用户模式和内核模式

运行态:此时进程或者正在运行,或者准备运行。

等待态:此时进程在等待一个事件的发生或某种系统资源。 ◆ 可中断 ◆ 不可中断

停止态:此时进程被中止。

死亡态:这是一个已终止的进程,但还在进程向量数组中占有一个 task_struct 结构。

alt

进程相关的基本命令

命令 功能说明 用法示例
ps 查看系统中的进程 ps aux(查看所有进程详细信息);ps -ef(查看进程父子关系)
top 动态显示系统中的进程 top(直接运行,实时刷新进程状态,按q退出);top -p 1234(仅查看 PID 为 1234 的进程)
nice 按用户指定的优先级运行进程 nice -n 5 ./myprogram(以优先级 5 运行程序,优先级范围 - 20~19,值越小优先级越高)
renice 改变正在运行进程的优先级 renice 10 1234(将 PID 为 1234 的进程优先级改为 10);renice -5 -u username(将用户username的所有进程优先级改为 - 5)
kill 向进程发信号 kill 1234(向 PID 为 1234 的进程发送终止信号 SIGTERM);kill -9 1234(发送强制终止信号 SIGKILL,无法被进程忽略)
bg 将挂起的进程在后台执行 bg %1(将编号为 1 的挂起作业放到后台运行,作业号可通过jobs命令查看)
fg 把后台运行的进程放到前台运行 fg %1(将编号为 1 的后台作业调到前台运行);fg(默认调回最近的后台作业)

kill命令常用信号:

信号名 编号 作用
SIGINT 2 中断信号,等同于ctrl + c
SIGCHLD 17 子进程死亡时给父进程发送的信号
SIGKILL 9 杀死进程

用法:

	kill	-2		PID
	kill	SIGINT	 PID

二、进程的使用

进程创建 fork()

函数功能:

​ 创建一个子进程

函数原型:

​ pid_t fork(void);

函数头文件:

​ #include <sys/types.h> ​ #include <unistd.h>

函数参数:

​ 无

函数返回值:

​ 0——在子进程中

​ >0——在父进程中,子进程的PID

​ -1——子进程创建失败,并设置errno

创建示例

/*===============================================
*   文件名称:fork.c
*   创 建 者:青木莲华
*   创建日期:2025年08月07日
*   描    述:fork函数示例
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>


int main(int argc, char *argv[])
{ 
    pid_t pid;
    if(0 > (pid = fork()))		//子进程会从这个语句的下一句开始执行
    {
        perror("fork");
    }
    else if(0 == pid)
    {
        printf("In child process!\n");
        printf("%d %d\n",getpid(),getppid());
    }
    else
    {
        printf("In parent process!\n");
    	printf("%d %d\n",pid,getpid());
    }
    return 0;
} 

运行截图

alt

fork创建的子进程基本拷贝了父进程的所有内容

fork创建的子进程是从fork的下一条语句开始执行,直到进程死亡

fork创建的子进程进程号,不会改变,但是父进程号可能会改变,当父进程先改变时,子进程会成为孤儿进程,然后再被init进程收养

进程的创建——2 vfork(void)

函数功能:

​ 创建一个子进程

函数原型:

​ pid_t vfork(void);

函数头文件:

​ #include <sys/types.h> ​ #include <unistd.h>

函数参数:

​ 无

函数返回值:

​ 0——在子进程中

​ >0——在父进程中,子进程的PID

​ -1——子进程创建失败,并设置errno

vfork与fork的区别:

vfork 创建的子进程会先执行,等子进程退出 exit 或者执行 exec 函数族时父进程才会开始执行

vfork 创建的子进程与父进程共享进程空间

exec函数簇

函数功能:

​ 执行一个可以执行的程序或脚本(需要有执行权限)

函数头文件:

​ #include <unistd.h>

函数原型:

int execl(const char *pathname, const char *arg, ...
          /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ...
           /* (char  *) NULL */);
int execle(const char *pathname, const char *arg, ...
           /*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
            char *const envp[]);

函数簇名解释:

名称 含义
l 把执行程序的命令以列表方式列出,最后以NULL作为结尾
v 把执行程序的命令以数组方式存放,以NULL结尾
p 自动去PATH路径下找可执行程序,非PATH路径下的文件还是需要根路径
e 执行程序的环境信息,以NULL结尾

函数返回值:

​ 只有出错才会返回:-1,且会设置errno

当执行了exec函数后,该进程的所有资源会被exec执行的程序夺舍。(调用exec的当前进程会终止)。

示例:

//execlp
if(0 > execlp("ps","ps","-elf",NULL)){
    perror("execl");
    return -1;
}     
//execvp
char *arr[] = {"ps","-elf",NULL};
if(0 > execvp("ps",arr)){
    perror("execvp");
    return -1;
}     

exit 和 _exit 函数

#include <stdlib.h>
void exit (int status);	// 刷新缓冲再结束进程
#include <unistd.h>
void _exit (intstatus);	// 不刷新缓冲直接结束进程

wait 函数

pid_t wait(int *wstatus);	//等待回收子进程资源

wait 函数的作用是:

​ 等待回收子进程资源(当有一个子进程结束时,进行回收)

函数原型与头文件

​ 需要包含头文件 <sys/wait.h> <sys/types.h>

参数说明:

​ wstatus——获取子进程退出状态,通过宏函数使用,当不想知道程序执行状态,则设置为NULL

​ WIFEXITED (wstatus) --- 当子进程正常退出时返回真 ​ WEXITSTATUS (wstatus) --- 当子进程正常退出时获取子进程退出状态值

返回值:

​ 成功:子进程的进程号PID

​ 失败:-1,并设置errno

示例:

/*===============================================
*   文件名称:wait.c
*   创 建 者:青木莲华
*   创建日期:2025年08月07日
*   描    述:
================================================*/
#include "head.h"

int main(int argc, char *argv[])
{ 
    pid_t pid;
    if(0 > (pid = fork()))
    {
        perror("fork");
        return -1;
    }
    else if(0 == pid)
    {
        sleep(3);
        printf("Child-- pid = %d\n",getpid());
        exit(5);
    }
    int wstatus;
    printf("Parent!\n");
    if(0 > (pid = wait(&wstatus)))
    {
        perror("wait");
        return -1;
    }
    printf("Wait child = %d\n",pid);
    if(WIFEXITED(wstatus))
    {
        printf("Exit normally!\n");
        printf("Exit value = %d\n",WEXITSTATUS(wstatus));
    }
    return 0;
} 

waitpid函数

alt

pid_t waitpid(pid_t pid, int *wstatus, int options);

参数:

​ pid:

​ >0: 只回收进程号= pid的这个子进程的资源

​ -1: 可以回收任意子进程的资源

​ wstatus:与wait一致,获取进程退出状态

​ options:选项

​ 0:阻塞回收

​ WNOHANG:非阻塞回收,没子进程退出也会立即返回

返回值:

​ >0:回收到的子进程的进程号PID

​ =0:非阻塞回收时,没收到子进程资源返回

​ -1:失败,并设置errno