一、创建进程
1、并发与并行
为了提高计算机执行任务的效率,一般采用的解决方案就是能够让多个任务同时进行,可以使用 并发 与
并行两种方式
并行 : 在 cpu 多核的支持下,实现物理上的同时执行
并发 : 在有限的 cpu 核芯的情况下 , 利用快速交替 ( 时间片轮转 ) 执行来达到宏观上的同时执行
总结:并行是基于硬件完成,而并发则可以使用软件算法来完成,可以创建多个进程并发执行来完成任务。
二、进程创建
1、fork() 函数
通过调用 fork() 函数,则会产生一个新的进程。调用 fork() 函数的进程叫做 父进程 ,产生的新进程则为 子 进程 。
函数头文件
#include <sys/types.h>
#include <unistd.h>
函数原型
pid_t fork(void);
函数功能
创建一个子进程
函数返回值
成功 : 返回给父进程是子进程的 pid, 返回给子进程的是 0
失败 : 返回 -1, 并设置 errno
代码
// 创建一个子进程,并打印 Hello fork
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{pid_t pid = fork();if(pid==-1){perror("fork");return -1;}printf("Hello fork.\n");return 0;
}
运行结果
问题 1 :为什么显示两次 "hello fork" ?
是因为打印语句在两个进程中都运行了
问题 1 :为什么两次显示结果有差异?
如果父进程在子进程打印之前结束,则会回到终端命令后继续执行子进程;如果子进程的
打印语句在父进程结束之前执行,则会在回到终端命令前执行完毕。
代码
// 创建一个子进程,并打印进程的pid
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{pid_t pid = fork();if(pid==-1){perror("fork");return -1;}printf("pid = %d,Hello fork.\n",getpid());return 0;
}
结果:
2 、父子进程
通过 fork() 函数创建子进程,有如下特点 :
父子进程并发执行,子进程从 fork() 函数之后开始执行
父子进程的执行顺序由操作系统算法决定的,不是由程序本身决定
子进程会拷贝父进程地址空间的内容,包括缓冲区、文件描述符等
例代码
// 父子进程数据空间拷贝,缓冲区的拷贝
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{// 标准IOwrite(1,"write hello.",12);// 文件IO自带缓冲区fputs("fputs hello.",stdout); // 注意没有换行符,stdout的缓冲区属于行缓冲pid_t pid = fork();if(pid==-1){perror("fork");return -1;}printf("pid = %d,Hello fork.\n",getpid());return 0;
}
文件描述符的拷贝
每个进程都会维护一个文件表项,即文件描述符与文件指针的映射表
在 Linux 内核中有一个 struct file 结构体来管理所有打开的文件
当子进程拷贝了父进程文件描述符后,则会共享文件状态标志与文件偏移量等信息
三、进程多任务
1、父子进程执行不同的任务
使用 fork() 函数之后,会创建子进程, fork() 之后的代码会在父子进程中都执行。
如果父子进程执行相同的任务,则正常执行
如果父子进程执行不同的任务,则需要利用 fork() 函数返回值
例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{printf("multitask.\n");pid_t pid = fork();if(pid == -1){perror("fork");return -1;}else if(pid==0){printf("child process task.\n");exit(EXIT_SUCCESS);}else{printf("parent process task.\n");}printf("parent and child task.\n");return 0;
}
2、创建多个进程、
在创建多个进程时 , 最主要的原则是 由父进程统一创建,统一管理,不能进行递归创建
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{pid_t pid = fork();if(pid == -1){perror("fork");return -1;}else if(pid==0){printf("child process <%d> is running.....\n",getpid());sleep(2);printf("child process <%d> ready to exit.\n",getpid());exit(EXIT_SUCCESS);}else{// 父进程中继续创建子进程pid = fork();if(pid == -1){perror("fork");return -1;}else if(pid == 0){printf("child process <%d> isrunning.....\n",getpid());sleep(4);printf("child process <%d> ready toexit.\n",getpid());exit(EXIT_SUCCESS);}printf("parent process task.\n");}printf("parent and child task.\n");return 0;
}
四、进程的退出
在进程结束时,需要释放分配给进程的地址空间以及内核中产生的各种数据结构
资源的释放需要通过 exit 函数或者 _exit 函数来完成
在程序结束时,会自动调用 exit 函数
1、exit和_exit
exit
函数头文件
#include <stdlib.h>
函数原型
void exit(int status);
函数功能
结束进程,并刷新缓冲区
函数参数
status: 退出状态值
在系统中定义了两个状态值 : EXIT_SUCCESS 正常退出, EXIT_FAILURE 异常退出,具体定义在
stdlib.h 中
#define EXIT_FAILURE 1 /* Failing exit status. */
#define EXIT_SUCCESS 0 /* Successful exit status. */
代码 1 :创建一个子进程,让子进程延时 2s 后退出
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{pid_t pid = fork();if(pid == -1){perror("fork");exit(EXIT_FAILURE);}else if(pid==0){printf("child process <%d> is running.....\n",getpid());sleep(2);exit(EXIT_SUCCESS);printf("child process <%d> ready to exit.\n",getpid());}else{sleep(5);}return 0;
}
代码 2 :说明 exit 函数会刷新缓冲区
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{pid_t pid = fork();if(pid == -1){perror("fork");exit(EXIT_FAILURE);}else if(pid==0){printf("child process <%d> is running.....",getpid()); // 注意此处没有换行符sleep(2);exit(EXIT_SUCCESS);printf("child process <%d> ready to exit.\n",getpid());}else{sleep(5);}return 0;
}
备注:发现执行 printf("child process <%d> is running.....",getpid()); 语句时并不是立即打
印,而是会延迟 2s 。
_exit
_exit 函数与 exit 函数功能相同
函数头文件
#include <unistd.h>
函数原型
void _exit(int status);
函数参数
status: 进程退出的状态值
代码 1: 创建一个子进程,使用 _exit 函数让子进程延时 2s 后退出
#include <sys/types.h>
#include <unistd.h>
int main()
{pid_t pid = fork();if(pid == -1){perror("fork");exit(EXIT_FAILURE);}else if(pid==0){printf("child process <%d> is running.....\n",getpid());sleep(2);printf("child process <%d> ready to exit.\n",getpid());_exit(EXIT_SUCCESS);}else{sleep(5);}return 0;
}
代码 2: 创建一个子进程,使用 _exit 函数让子进程延时 2s 后退出
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{pid_t pid = fork();if(pid == -1){perror("fork");exit(EXIT_FAILURE);}else if(pid==0){// 此条语句没有换行符printf("child process <%d> is running.....",getpid());sleep(2);_exit(EXIT_SUCCESS);}else{sleep(5);}return 0;}
1.3 exit 与 _exit 的不同
_exit() 属于 系统调用 ,能够使进程停止运行,并释放空间以及销毁内核中的各种数据结构
exit() 基于 _exit() 函数实现,属于库函数 , 会自动刷新 I/O 缓冲区