c语言多任务处理(并发程序设计)
. 多任务处理(并发程序设计)
1. 何谓多任务处理?
多任务处理是指同一时间内处理或者执行多个独立的功能任务,这样的事物处理方法
称为多任务处理。
2. 多任务处理方式的背景?
应用程序中往往是要支持多种复杂功能,我们就不能按照以前,某个功能执行完毕后再执行下一个功能任务,
设计方式来设计程序,我们期望的是某些独立的功能任务可以在同一时间共同执行(并发),以提升程序的运行
效率,这样就需要多任务处理方式了。
3. 多任务处理方式的实现
3.1 多进程
3.2 多线程
4. 进程初步
4.1 基础概念
1) 进程:
统一认知:进程是程序的一次执行过程,是程序执行和资源管理的最小单元
2) 进程的资源信息描述 (PCB)
PCB(进程控制块):用于记录系统对进程资源,状态,标识符分配信息等等
本质是Linux内核提供的一个数据类型 (struct task_struct)来表示。
3) 进程的标识(PID)
PID(进程号):用来唯一标识系统中的一个进程,本质是非零的正整数。
4.2 进程特征
4.2.1 独立性:进程一旦产生系统会为每一个进程分配4GB虚拟地址空间,每一个进程都运行在自己的
虚拟地址空间中,互不干扰
4.2.2 并发性:不同的进程可以在同一时间内同时运行;
4.2.3 异步性:由于进程要竞争CPU资源,所以会产生周期性的等待,也就是说进程其实并不是"齐头并进";
4.2.4 动态性:因为进程是程序执行过程,有创建及消亡的过程,
4.2.5 结构特征:
统会为每一个进程分配4GB虚拟地址空间,同时系统将该地址空间进行可结构划分,主要划分为
以下的内存结构,(自下而上)
代码段
数据段
堆段
内存映射段
栈段
命令行参数段
环境变量存储段
4.3 进程的模式
4.3.1 内核模式:
不可被干扰,内核模式执行完毕,进程会转入用户模式:
4.3.2 用户模式
大部分情况下,进程都是运行在用户模式的,一旦遇到系统调用或者发生硬件中断,进行
将转入内核模式.
4.4 进程的状态及状态转换
基本状态:
1) 运行状态: 进程正在运行
2) 就绪状态: 进程已经获取除了CPU之外的所有资源,一旦分配CPU资源就可以运行
3) 阻塞状态: 进程因为等待某个事件的发生,暂停运行
4) 僵尸状态: 进程已经运行完毕,但是进程资源(PCB)没有被回收
5) 停止状态: 进程已经运行完毕,进程资源(PCB)被回收
状态转换: 详见 进程状态转换.bmp
注意事项: 如果在实现多任务处理过程中,选择使用多进程解决方案,则一定要确保父进程不能先于子进程
退出,否则会造成程序已经运行结束的假象。而且使得子进程称为孤儿进程。
5. 进程基础操作
5.1 进程创建
函数头文件 #include <unistd.h>
#include <sys/types.h>
函数原型: pid_t fork(void) / pid_t vfork(void)
函数功能: 进程创建
函数参数: 无
函数返回值: 说明:fork/vfork 调用一次返回两次:
成功 子进程返回0
父进程返回子进程PID
失败 返回 -1, 错误码放在errno
fork / vfork 区别:
1. fork 创建子进程成功后, 系统为子进程分配独立的地址空间
vfork 创建子进程成功后,子进程和父进程共享父进程的地址空间
2. fork 创建子进程成功后, 父子进程执行顺序取决于系统的调度
vfork 创建子进程成功后,子进程先于父进程运行,子进程只有在调用了
exit/_exit 及 exec函数簇,父进程才可以正常运行。
3. 应用场景:
1.在系统硬件资源充足的情况下,推荐使用fork创建进程,否则使用vfork,
2.在使用exec函数簇时,推荐使用vfork创建子进程,在子进程中利用exec启动新程序。
5.2 获取进程ID
函数头文件 #include <unistd.h>
#include <sys/types.h>
函数原型: pid_t getpid(void) / pid_t getppid(void)
函数功能: 进程ID获取
函数参数: 无
函数返回值: 说明:getpid 返回自身进程ID:
getppid 返回父进程ID:
该函数调用不会失败
5.3 进程退出
函数头文件 #include <unistd.h>
#include <stdlib.h>
函数原型: void exit(int status) / void _exit(int status)
函数功能: 进程退出
函数参数: status: 进程退出后传递给父进程的数据(status & 0377)
函数返回值: 无
exit / _exit 区别:
1. exit 是标准库函数, _exit Linux内核函数
2. exit 在进程退出前会处理IO缓存。
5.4 进程等待
函数头文件 #include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
函数原型: pid_t wait(int *status) /pid_t waitpid(pid_t,pid,int *status,int option)
函数功能: 阻塞调用进程,直到某个子进程/指定子进程 退出
函数参数: [OUT]status: 用户获取子进程退出值(需要利用 WEXITSTATUS 宏转换)
pid: 指定的子进程ID
option: 附加选项: 往往取值为0
函数返回值: 退出的子进程ID
说明: 进程等待的目的:
1. 父进程想要获取子进程退出值;如果父进程不关注子进程的退出值,status可以设为NULL
2. 回收子进程的进程资源。
3. 为了确保父进程不能先于子进程退出。
5.5 进程中启动新程序
函数头文件 #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,const char* argp[]);
int execvp(const char *file,const char* argp[]);
int execve(const char *path,const char* argp[],char* const envp[]);
函数功能: 进程中启动新程序
函数参数: path:待启动的应用程序路径
arg: 待启动的应用程序命令行参数,以程序名开始,NULL结束
argp: 存储待启动的应用程序命令行参数的字符指针数组,元素以程序名开始,NULL结束
envp:用户自定义环境变量数组(NULL结束),用于在启动新程序的同时,传递给新程序的数据。
函数返回值: 成功返回0
失败返回-1,错误码放置在errno
引申的知识点: 自定义环境变量
自定义环境变量的语法格式:
环境变量名(大写) = 环境变量值 (字符串)
例子: NAME = TeacherYu
AGE = 45
char* const envp[] = {"NAME = TeacherYu"," AGE = 45",NULL};
exec函数簇的区分:
函数名的第五个字符:
l: (list) 利用exec函数参数罗列被启动程序的命令行参数
v: (vector) 用数组将被启动程序的命令行参数先存储起来,然后将数组传递给exec函数参数
函数名的第六个字符:
无:
p: (Path):系统会到PATH环境变量所代表的路径下去找寻待启动程序,如果未找到,exec函数调用失败
e: (enviorment): 在启动启动程序时,可以通过自定义环境变量向待启动程序传递数据。