一、创建进程
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()
{
// 标准IO
write(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> is
running.....\n",getpid());
sleep(4);
printf("child process <%d> ready to
exit.\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
缓冲区