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
结构。
进程相关的基本命令
命令 | 功能说明 | 用法示例 |
---|---|---|
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;
}
运行截图
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函数
pid_t waitpid(pid_t pid, int *wstatus, int options);
参数:
pid:
>0: 只回收进程号= pid的这个子进程的资源
-1: 可以回收任意子进程的资源
wstatus:与wait一致,获取进程退出状态
options:选项
0:阻塞回收
WNOHANG:非阻塞回收,没子进程退出也会立即返回
返回值:
>0:回收到的子进程的进程号PID
=0:非阻塞回收时,没收到子进程资源返回
-1:失败,并设置errno