体验进程的生命周期
通过调用fork()
,exec
,wait()
, sleep()
,exit()
等系统调用,编写代码,体验进程创建到结束的整个过程。
函数详解
fork()
fork()
函数会新生成一个进程,调用 fork
函数的进程为父进程,新生成的进程为子进程。在父进程中返回子进程的 pid,在子进程中返回 0,失败返回-1
一个进程调用fork()
函数后,系统先给新的进程分配资源。然后把原来的进程的所有值都复制到新的新进程中,相当于克隆了一个自己。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
void do_something(long t)
{
int i=0;
for(i=0;i<t;i++)
for(i=0;i<t;i++)
for(i=0;i<t;i++)
;
}
int main(){
pid_t pid;
printf("PID before fork():%d\n",getpid());
//getpid()获取当前进程的进程ID
pid=fork();
//fork 父进程返回>0 子进程返回0
pid_t npid=getpid();
//获取当前进程的pid
if(pid<0)//fork失败
perror("fork error\n");
else if(pid==0)//子进程
{
while(1)
{
printf("I am child process,PID is %d\n",npid);//打印对应npid
do_something(1000000000);
}
}
else if(pid>0)//父进程
{
while(1)
{
printf("I am father process,PID is %d\n",npid);//打印对应npid
do_something(1000000000);
}
}
}
可以看到其顺序是不规则的,即两个进程并发执行
exec
exec函数族的作用是根据指定的文件名找到可执行文件
使用exec函数族主要有两种情况:
(1)当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用exec函数族中的任意一个函数让自己重生。
(2)如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用exec函数族中的任意一个函数,这样看起来就像通过执行应用程序而产生了一个新进程
if(fork()!=0)
{
//parent code
wait(NULL);
}
else
{
//children code
exec(command,parameters,0);
}
wait()
wait()
是一个系统调用,用于使父进程等待子进程的终止,并获取子进程的退出状态。如果他找到了一个已经变成僵尸的子进程,wait就会收集这个子进程的信息并释放其PCB,没有找到时则一直等待。
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(){
pid_t pc,pr;
pc=fork();
if(pc<0)
{
printf("error ocurred!\n");
}
else if(pc==0)
{
printf("This is child process with pid of %d\n",getpid());
sleep(10);
}
else{
pr=wait(NULL);
printf("I catched a child process with pid of %d\n",pr);
}
exit(0);
}
某些时候父进程要等待子进程执行结束后才能继续运行,或者子进程的功能是为父进程提供了下一步执行的先决条件,这时需要进程间的同步,就是要协调好进程,使之以安排好的次序依次执行
sleep()
sleep
函数可以使计算机程序(进程,任务或线程)进入休眠,使其在一段时间内处于非活动状态。当函数设定的计时器到期,或者接收到信号、程序发生中断都会导致程序继续执行。
exit()
用来终止进程,执行对应的内核函数do_exit()
,该函数回收与进程相关的各种内核数据结构,把进程的状态设为TASK_ZOMBIE
,并把其所有的子进程都托付给init进程,最后调用schedule()
函数,选择新的进程执行。
使用exit
后,进程不会立即消失,而是变为僵尸状态,等待父进程的wait
进行回收。
代码示例
//test
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
int main(){
pid_t pid,pc;//定义一个pid结构体,pid_t在task_struct下;
char command;
char *arg_ps_tree[]={"pstree",NULL};//shell 中pstree;
char *arg_ps_ef[]={"ps","-ef",NULL};//shell中ps -ef;
char *arg_ps_a[]={"ps","-a",NULL};//shell中ps -a;
char *arg_ps_x[]={"ps","-x",NULL};//shell中ps -x;
char *envp[]={0,NULL};
while(1){
printf("**********************************\n");
printf("输入ps相关命令,来查看进程相关信息\n");
printf("输入t执行'pstree'命令\n");
printf("输入e执行'ps -ef'命令\n");
printf("输入a执行'ps -a'命令\n");
printf("输入x执行'ps -x'命令\n");
printf("输入q退出\n");
command = getchar();//获取命令;
getchar();//回车;
pid = fork();//创建子进程;
if(pid<0) {
perror("Error!");//创建子进程失败;
return -1;
}
else if(pid==0){//子进程;
printf("This is the child process with pid of %d\n",getpid());
switch(command){
case 't':
execve("/bin/pstree",arg_ps_tree,envp);
break;
case 'e':
execve("/bin/ps",arg_ps_ef,envp);
break;
case 'a':
execve("/bin/ps",arg_ps_a,envp);
break;
case 'x':
execve("/bin/ps",arg_ps_x,envp);
break;
case 'q':
break;
default:
perror("wrong command:\n");
break;//子进程结束;
}
exit(0);//子进程进入僵尸状态;
}
else{//父进程
pc=wait(NULL);
sleep(30);
printf("This is father process with pid of %d\n",getpid());
if(command == 'q') break;
printf("I catched a child process with pid of %d\n",pc);
}
}
return 0;
}
//test1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t child_pid;
int status;
// 创建一个子进程
child_pid = fork();
if (child_pid == -1) {
perror("Fork failed");
exit(1);
}
if (child_pid == 0) {
// 子进程的代码
printf("这是子进程,进程ID:%d\n", getpid());
// 使用 exec 函数来运行另一个程序
char *args[] = {"ls", "-l", NULL};
execvp("ls", args);
// 如果 exec 函数执行失败,打印错误信息
perror("Exec failed");
exit(1);
} else {
// 父进程的代码
printf("这是父进程,子进程的进程ID:%d\n", child_pid);
// 使用 wait 函数等待子进程的结束
wait(&status);
if (WIFEXITED(status)) {
printf("子进程正常结束,退出状态:%d\n", WEXITSTATUS(status));
} else {
printf("子进程异常结束\n");
}
// 父进程休眠一段时间
sleep(2);
printf("父进程结束\n");
}
return 0;
}
结果展示
test
ps -a
ps -x
ps -ef|more
pstree
test1
心得体会
通过使用 fork()
, exec()
, wait()
, sleep()
, 和 exit()
等系统调用,我深刻理解了进程创建和管理的基本原理。父进程可以通过 fork()
创建子进程,并可以使用 wait()
等待子进程的结束,以获取其退出状态。子进程可以通过 exec()
执行其他程序,并替换自己的代码和数据。此外,sleep()
可用于进程的暂停,exit()
可用于正常或异常退出进程。示例代码也能够很好的展示了进程创建到结束的完整生命周期,有助于深入地理解。