Linux之进程
本文介绍了进程的基本概念及其在操作系统中的创建与管理方式。主要内容包括:1)进程是程序运行的实例,拥有独立资源;2)进程可通过系统调用或父进程创建,重点讲解了system、fork等函数的使用;3)特殊进程状态:孤儿进程(被init收养)和僵尸进程(父进程未调用wait回收);4)进程等待机制(wait/waitpid函数)及其应用场景。文章通过代码示例详细演示了进程创建、孤儿/僵尸进程的形成过程
·
一.什么是进程
通俗的来讲,进程就是程序在操作系统里运行起来后的一个实例,它有自己的资源、能和别的进程并发执行,系统通过进程来管理和调度任务。
二.进程的创建
创建进程有两种⽅式:1)操作系统创建 2)父进程创建。
进程的创建可以通过system函数、exec系列函数(execl、execlp等)、fork函数
实现。
1)system函数
函数原型为:int system(sonst char*filename)
使用时需要包含头文件:stdlib.h 主要用于执行shell命令.
功能为:简历独立进程,拥有独立的代码空间,内存空间,等待新的进程执行完
毕,system才返回,system函数其实内部会调用fork函数来产生子进程。
2)复制进程fork函数
函数原型为:pid_t fork(void)
功能:fork 函数会新生成⼀个进程,调⽤ fork 函数的进程为父进程,新生成的进 程为子进程。子进程复制了父进程打开的文件描述符。
函数返回值:函数返回类型 pid_t 实质是 int 类型
在父进程中返回子进程的 pid,在子进程中返回 0,失败返回-1。
所属头文件:unistd.h
示例代码
#include<stdio.h>
#include<unistd.h>
#include <time.h>
#include<sys/types.h>
#include<sys/wait.h>
void task1()
{
printf("#current id=%d\n",getpid());
for(int i=0;i<10;i++)
{
printf("##############---%d\n",i+1);
sleep(1);
}
}
void task2()
{
printf("$current id=%d\n",getpid());
for(int i=0;i<10;i++)
{
printf("$$$$$$$$$$$$---%d\n",i+1);
sleep(1);
}
}
int main()
{
int start=time(NULL);
printf("start=%d\n",start);
pid_t pid=fork();
if(pid>0)
task1();//父进程
else
task2(); //子进程
wait(NULL);
int end=time(NULL);
printf("end=%d\n",end);
return 0;
}
fork()函数出错可能有两种原因:
1、当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN
2、系统内存不足,这时errno的值被设置为ENOMEM
3)孤儿进程
如果⼀个子进程的父进程先与子进程结束,子进程就变成孤儿进程;孤儿进程将被 init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作(包括资源回收)。
示例代码
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main()
{
printf("parent process id=%d\n", getpid());
pid_t pid = fork();
if(pid > 0)
{
printf("father process\n");
sleep(1);
}
else if(pid == 0)
{
printf("father id=%d child id=%d\n", getppid(), getpid());
sleep(5);
printf("father id=%d child id=%d\n", getppid(), getpid());
}
printf("end==%d\n", getpid());
return 0;
}
运行结果
注意:带图形界⾯的ubuntu下上述打印孤儿进程的父进程可能不是1,是因为代替 了init进程来接管孤儿进程的进程名字叫做init–user,如果将程序在命令行下运就是发现是1。
形成过程
-
父进程先退出(62710):在子进程还在睡眠5秒时,父进程已经睡眠1秒后退出
-
子进程成为孤儿:父进程退出后,子进程被init进程(PID=1)收养
-
父进程ID变化:子进程第二次打印时,getppid
()
返回的不再是原来的父进程ID(62710),而是init进程的ID(1853)
4)进程等待
创建⼀个子进程后,父进程与子进程会争夺cpu,抢到者先执⾏,另⼀个挂起等待, 如果想要父进程等待⼦进程执行完毕以后再继续执行,可以在fork之后,调用wait 或waitpid。同时父进程通过上述函数还可以取得子进程的终止状态。
等待进程结束的函数主要有:
1、pid_t wait(int *status);
功能:wait函数会在父进程中阻塞,等待子进程结束,如果子进程结束,则返回子进程的PID。如果没有子进程则立刻返回-1。
注意:如果要获取wait的状态码,需要用该宏包裹WEXITSTATUS(status)才可以拿到。
2、pid_t waitpid(pid_t pid, int *status, int options);
waitpid函数等待子进程结束(options设置为WNOHANG时为⾮阻塞,设置为0 表示阻塞),如果子进程结束,则返回子进程的PID。如果没有子进程则立刻返 回-1,如果是非阻塞的并且子进程还没有结束,则返回0。
两者的区别:
wait函数作用就是父进程调用,将父进程挂起,直到所等待的进程结束,然后在执 行父进程。而如果有多个子进程的话,只要第⼀个子进程结束,wait就会接受到信 号,并结束等待。而waitpid就会指定⼀个进程号,父进程将会等待pid这个子进程 结束才会执行。
5)僵尸进程
(1) 僵尸进程概念:子进程先于父进程结束,父进程没有调用 wait 获取子进程退出
码。
(2) 如何处理僵死进程:父进程通过调用 wait()完成。
示例代码
#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include <sys/wait.h>
int main()
{
int retval;
printf("parent process id=%d\n",getpid());
pid_t pid=fork();
if(pid>0)
{
printf("father process\n");
wait(NULL);
sleep(10);
printf("father over\n");
}else if(pid==0)
{
printf("entry child process\n");
sleep(1);
}
return 0;
}
运行结果
过程分析
时间点0: 父子进程同时开始
时间点1: 子进程退出,成为僵尸进程
时间点1-11: 父进程仍在睡眠,子进程保持僵尸状态
时间点11: 父进程执行wait(NULL),回收子进程
时间点11+: 僵尸进程被清除
时间点1: 子进程退出,成为僵尸进程
时间点1-11: 父进程仍在睡眠,子进程保持僵尸状态
时间点11: 父进程执行wait(NULL),回收子进程
时间点11+: 僵尸进程被清除
避免僵尸进程的方法
及时调用wait()或waitpid()
使用信号处理SIGCHLD
双fork技术(让孙子进程被init收养)
更多推荐
所有评论(0)