系统编程day03-进程
1.进程
1.1程序和进程的区别
程序:代码或者指令的有序堆叠,是静态的,存放在磁盘空间。
进程:程序的运行实例,进程是动态的。
1.2单道程序和多道程序
单道程序:所有进程一个一个排队执行。若 A 阻塞,B 只能等待,即使 CPU 处于空闲状态。而在人机交互时阻塞的出现是必然的。所有这种模型在系统资源利用上及其不合理,在计算机发展历史上存在不久,大部分便被淘汰了。(例如一开始的dos系统)
多道程序:在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之下,相互穿插的运行。
1.3并发和并行
并行(一定是多核的):多道程序同时在多个处理器上运行。
并发:宏观上的并行,并不是真正的并行,而是基于多道程序设计的一种根据时间片轮换cpu使用权的一种特殊机制。
1.4进程控制块
PCB:process-control-block
进程运行时,内核为进程每个进程分配一个 PCB(进程控制块),维护进程相关的信息,Linux 内核的进程控制块是 task_struct 结构体。
查看这个结构体:
PCB:
2.进程的状态
2.1进程的三种状态
就绪状态:准备完毕,等待执行
运行状态:获得CPU资源,正在使用
等待状态:当有事情发生,条件不满足,需要重新准备
2.2进程状态的查看
ps:ps可以查看进程信息
可以结合命令参数:
-a 显示终端上的所有进程,包括其他用户的进程
-u 显示进程的详细状态
-x 显示没有控制终端的进程
-w 显示加宽,以便显示更多的信息
-r 只显示正在运行的进程
-ajx 更深层次的进程信息
ps -aux 相当于Linux中的任务管理器
stat的参数意义如下:
3.进程号
3.1什么是进程号
每个进程都由一个进程号(pid)来标识,其类型为 pid_t(整型),进程号的范围:0~32767。进程号总是唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再次使用。
两个特殊进程:
0进程:
早期叫做:交换进程
现在叫做:空闲进程(当CPU空闲的时候,会执行这个进程)
1进程:init进程,是所有用户进程的祖先(所有用户进程都是有1号进程直接或者间接创建)。(1号进程由0号进程创建)
pid:进程的编号
ppid:父进程,每一个进程都是由父进程创建而来的。ppid就是父进程的编号。
pgid:进程组,一个或者多个进程的集合,这些进程的集合称为进程组,进程组有一个组号,叫做pgid。这些进程相互关联,可以接收终端传来的各种信号。
3.2获取进程的函数
getpid
获取当前进程的进程号
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
返回值:获取的当前进程的进程号,pid_t
参数:无参数
getppid
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
返回值:获取的当前进程的父进程号,pid_t
参数:无参数
getpgid
#include <sys/types.h>
#include <unistd.h>
pid_t getpgid(pid_t pid);
返回值:获取的pid进程的进程组号,pid_t
参数:需要传入要找的pid号
4.进程的创建
4.1进程的创建函数
进程的创建由fork函数来完成。
fork函数:
#include <sys/types.h>
#include <unistd.h>pid_t fork(void);
函数功能:创建一个新的进程。这个被创建的新的进程叫做子进程,是调用fork函数这个进程(父进程)的子进程。
返回值:
如果创建成功:
在父进程中返回的是子进程的id
在子进程中返回的是0
如果创建失败:
返回-1
4.2父子进程的关系
子进程是父进程的一个复制品(从fork的下一句开始复制),两个进程都有各自单独的空间。
这里说的fork的下一句并不是if(pid == -1);而是fork函数创建子进程完后,进行的赋值操作。
最终的终端打印结果如上,一个是在父进程中进行,一个是在子进程中进行。
子进程从父进程那里继承了整个进程的地址空间。
地址空间:包括进程上下文、进程堆栈、打开的文件描述符等。
子进程所独有的只有它的进程号,计时器等。因此,使用 fork 函数的代价是很大的。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc, char const *argv[])
{pid_t pid = fork();if (pid == -1){perror("fork");return 0;}else if (pid > 0){printf("hello,welcome to parent process\n");printf("当前进程id%d,他的子进程id%d\n", getpid(), pid);}else if (pid == 0){printf("hello,welcome to child process\n");printf("子进程pid:%d,他的父进程id:%d\n", getpid(), getppid());}return 0;
}
拓展1:sleep函数
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
函数功能:将进程挂起(休眠)一段时间。
返回值:
如果sleep函数里面的seconds走完了,那么返回0
如果没有休眠完,被强制打断,那么返回剩余待休眠的时间
参数:挂起或者休眠的时间
拓展2:全缓冲与行缓冲
全缓冲:缓冲区满了以后强制进行刷新,即write函数可以将数据输出到终端
行缓冲:遇到\r\n,进行刷新,此时write函数可以将数据输出到终端
拓展3:exit函数 与 _exit函数
exit函数:
功能:库函数,将进程相关数据 清理后退出
除了调用_exit函数还会调用一个清理函数
所以使用exit函数的运行效率比较低
#include <stdlib.h>
void exit(int status);
_exit函数:
功能:系统调用函数,将进程直接退出,不进行清理
void _exit(int status);
5.进程资源回收
在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块 PCB 的信息(包括进程号、退出状态、运行时间等)。 父进程可以通过调用 wait 或waitpid 得到它的退出状态同时彻底清除掉这个进程。
5.1wait函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status)
函数功能:等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。
参数:
status : 进程退出时的状态信息,是一个指针
返回值:
成功:返回运行结束的进程的进程号
失败:-1
代码案例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main(int argc, char const *argv[])
{pid_t pid = fork();if (pid == -1){perror("fork");return 0;}else if (pid == 0){for (int i = 1; i <= 5; i++){printf("我还能再玩%ds\n", 5 - i);sleep(1);}_exit(0);}else if (pid > 0){int status = 0;printf("你5s以后必须停止玩游戏\n");sleep(5);wait(&status); //阻塞,等待子进程退出if (WIFEXITED(status) != 0){printf("status:%d\n", WEXITSTATUS(status));if (WEXITSTATUS(status)==0){printf("好的,你说的对,听你的\n");}else if (WEXITSTATUS(status)==1){printf("好的,你说的不对,但是我还是听你的\n");}}}return 0;
}
查看WEXITSTATUS函数的定义可以看出来,她是将status中除了8-15位的其他位数全部置零,并且向右偏移了8位。可以取到status具体的值。
5.2waitpid函数
waitpid() 是 Unix/Linux 系统中用于进程同步的核心系统调用,专门用于父进程等待特定子进程的状态变化。相比通用的 waitpid(),它提供了更精细的控制能力
函数原型:
#include <sys/types.h>
#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
参数解释:
1.pid :目标子进程标识
- >0:等待指定PID的子进程
- -1:等待任意子进程(等效于wait())
- 0:等待同进程组的任意子进程
- <-1:等待进程组ID等于|pid|的任意子进程
2.status:状态信息指针:
- 存储子进程退出状态(使用宏解析)
- 可设为NULL(不关心退出详情)
3.option:控制选项(位掩码)
- 0:阻塞等待(默认行为)
- WNOHANG:非阻塞模式(立即返回)
- WUNTRACED:报告已经停止的子进程
- WCONTINUED:报告已经继续执行的被停止子进程
返回值:
- 成功:返回状态变化的子进程PID
- 无状态变化:返回0
- 错误:返回-1