进程的概念
进程是一个运行着的程序,它包含了程序在运行时的各个资源,进程是操作系统进行调度的基本单位,也是 一个程序运行的基本单位。进程是一个程序一次执行的过程,是操作系统动态执行的基本单元。
进程的概念主要 有两点:
第一,进程是一个实体。每个进程都有自己的虚拟地址空间,这些地址空间包括代码区、数据区、和堆 栈区。文本区域存储处理器执行的代码;数据区存储变量和动态分配的内存;堆栈区存储活动进程动态申请的内 存和局部变量及函数调用时的返回值。
int main()
{
int a = 20;
int *p = &a;
pid_t pid = fork();//fork()函数的作用为创建一个子进程,
//返回值pid在子进程中为0,父进程中为子进程的pid
if (pid == 0)
{
printf("子进程:\n");
printf("%d\n", ++(*p));
printf("%p\n", p);
}
else
{
wait(NULL);//wait()函数的作用是等待子进程死亡
printf("父进程:\n");
printf("%d\n", *p);
printf("%p\n", p);
}
}
执行结果如下:
从结果可以看出子进程和父进程中指针p指向的地址相同,但是一个输出为21一个却是20,这是因为我们代码中分配的地址是虚拟地址并不是真实的物理地址,两者虚拟地址相同,但物理地址却不同。
第二,进程是一个“执行中的程序”,它和程序有本质区别。程序是静态 的,它是一些保存在磁盘上的指令的有序集合(文件);而进程是一个动态的概念,它是一个运行着的程序,包含了进程的动态创建、调度和消亡的过程,是 Linux 的基本调度单位。只有当处理器赋予程序生命时,它才能成 为一个活动的实体,称之为进程, 只是程序在内存,并能够得到执行的时候。
这是一个可执行程序
当执行之后,就会产生一个进程。
进程的状态
进程的描述
进程资源
创建进程
fork()函数
头文件:
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
函数描述:
fork()函数通过复制调用他的进程来创造进程。新的进程被叫做子进程,调用fork()的进程被称为父进程。fork()函数创建的进程会开辟一个新的空间,复制父进程所有的资源,并执行fork()函数之后的代码。
返回值:
成功时:在子进程中返回值PID为0,在父进程中返回值为子进程的PID。
失败时:返回值为-1,创建子进程失败。
代码演示:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
fork();
fork();
fork();
printf("hello world\n");
}
执行结果:
一共打印了8次hello world,其原理如下。
vfork 函数
头文件:
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
函数描述:
返回值:
成功时:在子进程中返回值PID为0,在父进程中返回值为子进程的PID。
失败时:返回值为-1,创建子进程失败。
代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <dirent.h>
#include <sys/wait.h>
int main()
{
int a=0;
int fatherSign=0;
int sonSign=0;
while (1)
{
pid_t pid=vfork();
if(pid==0)
{
printf("son's home\n");
printf("sign:%d\n",fatherSign);
exit(11);
}
if(pid!=0)
{
printf("daddy's home\n");
printf("sign:%d\n",fatherSign++);
}
a++;
if(a==2)
break;
}
return (0);
}
运行结果:
从上述执行结果,可以看到每次son都在daddy之前执行,且变量是共用的。
关闭进程
exit()函数
头文件:
#include <stdlib.h>
void exit(int status);
函数描述:
返回值:
无返回值;
_exit()函数
头文件:
#include <unistd.h>
void _exit(int status);
函数描述:
返回值:
无返回值;
int main()
{
pid_t pid=fork();
if(pid==0)
{
printf("son's home");
exit(11);
}
if(pid!=0)
{
int value=0;
wait(&value);//等待子进程关闭在执行父进程,为了美观,不必理会
printf("\n");
printf("daddy's home");
_exit(11);
}
return (0);
}
运行结果:
可以看到执行结果是子进程打印出结果,但是父进程有一句却没有打印出来。这是因为父进程调用了_exit()函数,进程结束了,却没有清空缓存区。
如果有同学不知道缓冲区,我只简单的说一下清空缓冲区的条件:出现"\n"、缓冲区满了、使用了fflus()函数等等。要想深入学习缓冲区,建议去看看其他写缓冲区的帖子。
进程资源回收
头文件:
函数描述:
阻塞当前进程等待子进程死亡 ,子进程死亡后会进行资源的回收利用并获取子进程的退出状态。可以接收到子进程exit或_exit函数中的参数;
int main()
{
pid_t pid = fork();
if (pid == 0)
{
printf("子进程关闭\n");
exit(11);
}
else
{
int wstatus;
wait(&wstatus);
printf("如果退出成功WIFEXITED输出1\n");
printf("WIFEXITED输出结果:%d\n",WIFEXITED(wstatus));
printf("WEXITSTATUS解出来为exit退出时的参数\n");
printf("WEXITSTATUS输出结果:%d\n",WEXITSTATUS(wstatus));
}
}
执行结果:
头文件:
函数描述:
阻塞当前进程等待子进程死亡 ,子进程死亡后会进行资源的回收利用并获取子进程的退出状态。可以接收到子进程exit或_exit函数中的参数;
int main()
{
pid_t pid = fork();
if (pid == 0)
{
printf("子进程关闭\n");
exit(11);
}
else
{
waitpid(pid,NULL,WNOHANG);
printf("父进程死亡\n");
exit(22);
}
}
执行结果: