一,进程与PCB
进程典型定义
(1)进程是程序的一次执行。
(2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
(3)进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
特征:动态性,并发性,独立性,异步性。
基本状态:
PCB
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
内容分类
(1)标识符:描述本进程的唯一标识符,用来区别其它进程。
(2)状态:任务状态,退出代码,退出信号等。
(3)优先级
(4)程序计数器
(5)内存指针:包括程序代码和进程相关数据的指针,还有和其它进程共享的内存块的指针。
(6)上下文数据
(7)I/O状态信息
(8)记账信息
进程通俗说就是放在内存里的程序。
接下来我们了解一下我们电脑里的进程情况(由于太多只截取了部分)
命令解释: -e表示列出全部进程 -o pid,comm,cmd表示我们需要PID,COMMAND,CMD信息。
这里PID:像我们的身份证一样,进程根据PID区别于其他进程
COMMAND:进程名。
CMD:进程所对应的程序以及运行时所带的参数。
像这里的1号进程init就有很重要的作用,后面的知识会有所应用。
进程状态
(1)R运行状态(running):进程或是在运行中,或是在运行队列中。
(2)S睡眠状态(sleeping):进程等待事件完成。
(3)D磁盘休眠状态(Disk sleep):也叫不可中断睡眠状态,在这个状态的进程通常会等待IO结束。
(4)T停止状态(stopped):可通过发送SIGSTOP信号给进程来停止(T)进程。SIGCONT信号让进程继续运行。
(4)X死亡状态(dead):只是一个返回状态,将不会在任务列表中看到这个状态。
二,fork与vfork
fork()系统调用的作用是复制一个进程,当一个进程调用它,完成个就出现两个几乎一摸一样的进程,新进程是子进程,原来的进程是父进程.
pid_t fork(void)若成功,父进程中返回子进程ID,子进程中返回0,若出错返回-1;
#include <unistd.h>
#include <stdio.h>
int main (void)
{
int count = 0;
pid_t pid; /*此时仅有一个进程*/
pid = fork(); /*此时已经有两个进程在同时运行*/
if(pid < 0)
{
printf("error in fork!");
exit(1); /* fork出错退出*/
}
else if(pid==0)
printf("I am the child process, the count is %d, my process ID is %d\n",count,getpid());
else
printf("I am the parent process, the count is %d, my process ID is %d\n",++count,getpid());
return 0;
}
结果:
[a@localhost ~]$ ./a.out
I am the parent process, the count is 1, my process ID is 2727
[a@localhost ~]$ I am the child process, the count is 0, my process ID is 2728
vfork与fork是有区别的
(1)fork要拷贝父进程的数据段,而vfork不需要完全拷贝父进程的数据段,在子进程没有调用exec或者exit函数之前,子进程会与父进程共享数据段。
#include<stdio.h>
#include<unistd.h>
int main(void)
{
int count = 1;
int child;
printf("Before create son,father count is :%d\n", count);
child = vfork();
if(child < 0)
{
printf("error in vfork!\n");
exit(1);
}
if(child == 0)
{
printf("The is son,pid is:%d and the count is: %d\n",getpid(),++count);
exit(1);
}
else
{
printf("After son,This is father,pid is: %d and the count is:%d,the child is:%d\n", getpid(), count, child);
}
return 0;
}
结果:
[a@localhost ~]$ ./a.out
Before create son,father count is :1
The is son, pid is:4846 and the count is: 2
After son,This is father, pid is: 4845 and the count is:2,the child is:4846
[a@localhost ~]$
(2)在fork中子进程与父进程的执行顺序不确定,而在vfork中,子进程先运行,父进程挂起,直到子进程调用了exec或者exit函数,父进程才被执行。
#include<stdio.h>
#include<unistd.h>
int main()
{
int count = 1;
int child;
printf("Before create son,the father count is :%d\n",count);
if(!(child = vfork()))
{
int i;
for(i = 0; i < 100; i++)
{
printf("This is son, The i is: %d\n", i);
if(i ==5)
exit(1);
}
printf("This is son,pid is: %d and the count is :%d\n",getpid(),++count);
exit(1);
}
else
{
printf("After son,This is father,pid is:%d and the count is: %d,and the child is: %d\n", getpid(), count, child);
}
return 0;
}
结果:
[a@localhost ~]$ ./a.out
Before create son,the father count is :1
This is son, The i is: 0
This is son, The i is: 1
This is son, The i is: 2
This is son, The i is: 3
This is son, The i is: 4
This is son, The i is: 5
After son,This is father,pid is:5018 and the count is: 1,and the child is: 5019
从结果可以看出父进程是在等子进程执行完毕后才开始执行的。
三,僵尸进程与孤儿进程
僵尸进程:当一个进程完成它的工作终止之后,内核释放该进程的所有资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(进程号,推出状态,运行时间等)所以他的父进程需要调用wait/waitpid系统调用取的子进程的终止状态,释放那段信息。当进程退出但是父进程没有读取到子进程退出的返回代码时,就会产生僵尸进程。
危害:僵尸进程会始终保持在进程表中,如果产生大量的僵尸进程,系统因没有可用的进程号而不能产生新的进程。
来创建一个维持30秒的进程例子
#include<stdio.h>
#include<stdlib.h>
int main()
{
pid_t id = fork();
if(id<0)
{
perror("fork");
return 1;
}
else if(id>0)
{
printf("parent[%d] is sleeping ...\n",getpid());
sleep(30);
}
else
{
printf("child[%d] is begin Z...\n",getpid());
sleep(5);
exit(EXIT_SUCCESS);
}
return 0;
}
清除僵尸进程
1)改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信 号后,执行 waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略, 如果想响应这个消息,可以设置一个处理函数。
2)把父进程杀掉。父进程死后,僵尸进程成为”孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
避免僵尸进程
1)在一个程序的开始调用函数
signal(SIGCHLD,SIG_IGN);
2)调用fork两次。
3)用waitpid等待子进程返回.
孤儿进程:顾名思义,孤儿即失去了父母,在这里是父进程先退出,子进程就称为“孤儿进程”。
孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
pid_t id = fork();
if(id<0)
{
perror("fork");
return 1;
}
else if(id==0)
{
printf("start:i am child[%d],parent is [%d]\n",getpid(),getpid());
sleep(10);
printf("finish:i am child[%d],parent is [%d]\n",getpid(),getppid());
}
else
{
printf("I am parent, pid:%d\n",getpid());
sleep(3);
exit(0);
}
return 0;
}