1、进程概念
进程是具有独立功能的程序的一次运行活动,同时也是系统进行资源分配和调度的最小单位。
简而言之,进程就是一个正在执行的程序或者一个程序执行的实例
2、描述进程
进程的信息放在一个叫做程序控制块的数据结构中。系统利用PCB(程序控制块)对并发进行的程序加以控制和描述。所谓创建进程就是创建进程映像中的PCB,而撤销进程就是撤销PCB。
3、进程和程序的区别
1> 动态性: 进程具有动态性,有着创建,活动,暂停,终止等过程,有一定的生命周期,进程不可在计算机之间迁移。而程序是一组静态指令的集合,可以对应文件进行复制和粘贴。进程的动态性是进程最基本的特征。
2> 并发性 :引入进程的目的就是为了使程序能够与其他进程的程序并发的进行,以提高内存利用率。多个进程实体能同存于内存,同时运行。并发性是进程最重要的特征,同时也是操作系统最重要的特征。
3> 独立性:进程实体是一个可以独立运行,独立分配资源和系统调度的基本单位。但凡是没有建立PCB的程序都不能够独立的参与运行。
4> 结构性:从结构上看,进程通过PCB对其进行控制和描述。主要由程序段,数据段,进程控制段三个部分组成的。
4、进程的创建
一个父进程调用fork()函数后把自己复制一份,而这个复制出来的新进程,就是子进程,就成功完成了进程的创建。
在Linux中主要提供**fork()、vfork()、clone()**三个进程创建方法,这里先总结下fork()
fork在创建一个进程时,子进程只是完全复制父进程的资源,复制出来的子进程有自己的task_struct结构和pid,但却复制父进程其他所有的资源。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret=fork();
if(ret<0)
{
perror("Fail");
return -1;
}
else
{
printf("hello proc:%d\n!,getpid(),ret);
}
sleep(1);
return 0;
}
5、僵尸进程
为什么会产生僵尸进程?由于子进程优先于父进程退出,为了保存退出原因,因此它的资源并没有完全释放。在子进程退出时,操作系统会通知父进程,让父进程获取子进程的退出原因,然后释放子进程的所有资源,若父进程没有关注子进程的退出状态,也就是没有调用wait/waitpid,那么子进程便会成为一个僵尸进程。
值得注意的是僵尸进程是一个早已死亡的进程,但它在进程表中仍然占据着一个位置。
6、僵尸进程的解决方法
(1)父进程通过wait/waitpid等函数等待子进程的结束,这会导致父进程挂起。子进程在终止后会立即把它在进程表中的数据返回给父进程,系统会立即删除该结点。
(2)若父进程很忙,那么可以用signal函数为SIGHLD安装handler.在子进程结束后,父进程会收到该信号,可以在handler中调用wait函数
(3) 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号。
(4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。
7、孤儿进程
一个父进程创建了多个子进程,当父进程退出时,它的一个或者多个子进程还在运行,那么子进程就会成为孤儿进程。孤儿进程会被init进程所收养,并由init进程对他们完成状态收集工作。
僵尸进程会导致资源的浪费,但是孤儿进程不会
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid id=fork();
if(id < 0)
{
perror("fork ");
return 1;
}
else if(id==0)
{//子进程
printf("I am child,pid:%d\n",getpid());
sleep(10);
}
else
{//父进程
printf("I am parent,pid:%d\n",getpid());
sleep(3);
exit(0);
}
return 0;
}
输出的结果为
I am parent,pid:4416
I am child,pid:4417