目次
一、前文
二、正文
1.进程创建
①fork函数初识
编辑
②fork函数返回值
③ 写时拷贝
④fork常规用法
⑤fork调用失败的原因
⑥怎样一次创建多个进程
2.进程终止
①进程退出场景
②信号
③进程常见退出方法
三、小提问
四、结语
一、前文
本文会为大家带来进程中有关进程控制的讲解,重点是进程的创建和终止,盼望大家可以或许从中有所收获。
二、正文
1.进程创建
①fork函数初识
在之前的讲解中我们已经见过了fork函数,在linux中fork函数时非常紧张的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
#include <unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1
进程调用fork,当控制转移到内核中的fork代码后,内核做:
●分配新的内存块和内核数据结构给子进程
●将父进程部分数据结构内容拷贝至子进程
●添加子进程到系统进程列表当中
●fork返回,开始调理器调理
当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以 开始它们自己的旅程,看如下程序。
-
- #include <unistd.h>
- pid_t fork(void);
- 返回值:自进程中返回0,父进程返回子进程id,出错返回-1
- int main( void )
- {
- pid_t pid;
-
- printf("Before: pid is %d\n", getpid());
- if ( (pid=fork()) == -1 )perror("fork()"),exit(1);
- printf("After:pid is %d, fork return %d\n", getpid(), pid);
- sleep(1);
- return 0;
- }
-
- 运行结果:
- [root@localhost linux]# ./a.out
- Before: pid is 43676
- After:pid is 43676, fork return 43677
- After:pid is 43677, fork return 0
复制代码 这里看到了三行输出,一行before,两行after。进程43676先打印before消息,然后它有打印after。另一个after 消息有43677打印的。注意到进程43677没有打印before,为什么呢?如下图所示
以是,fork之前父进程独立执行,fork之后,父子两个执行流分别执行。注意,fork之后,谁先执行完全由调理器决定。
②fork函数返回值
●子进程返回0,
●父进程返回的是子进程的pid。
③ 写时拷贝
通常,父子代码共享,父子再不写入时,数据也是共享的,当恣意一方试图写入,便以写时拷贝的方式各自一份副本。详细见下图:
④fork常规用法
●一个父进程盼望复制自己,使父子进程同时执行差异的代码段。比方,父进程等待客户端哀求,天生子 进程来处理哀求。
●一个进程要执行一个差异的程序。比方子进程从fork返回后,调用exec函数。
⑤fork调用失败的原因
●系统中有太多的进程
●实际用户的进程数超过了限制
⑥怎样一次创建多个进程
在之前的学习中我们都是用fork一次创建一个进程,但是如果我们想要一次性创建出多个进程应该怎么做呢,代码见下:
- 1 #include<stdio.h>
- 2 #include<unistd.h>
- 3 #include <sys/types.h>
- 4 #include <stdlib.h>
- 5 #define N 5
- 6
- 7 void runchild()
- 8 {
- 9 int cnt=10;
- 10 while(cnt)
- 11 {
- 12 printf("I'm child: %d, ppid: %d\n",getpid(),getppid());
- 13 sleep(1);
- 14 cnt-- ;
- 15 }
- 16 }
- 17
- 18 int main()
- 19 {
- 20 for(int i=0;i<N;++i)
- 21 {
- 22 pid_t id=fork();
- 23 if(id==0)
- 24 {
- 25 runchild();
- 26 exit(0);
- 27 }
- 28 }
- 29 sleep(1000);
- 30
- 31 }
复制代码
通过循环调用fork,我们就能创建一批子进程,而且最后打印出来的结果也显示出来我们确实创建的一批子进程,同时也印证了我们上面所讲到到调理器调用进程的顺序并不是顺序的的,而是随机的。
2.进程终止
大家在C/C++语言的学习中,每每main函数的末端要加一个“return 0”,为什么偏偏是返回0,我返回1,2不行吗,答案当然是可以的,就像下面如许
return 0
return 1
return 2
但是我们要怎么看到我返回的是多少呢,这时候就要借助一个指令
echo $?:查看最近一个进程竣事的退出码
当我们进程正常竣事且main函数返回的是0的时候,这时候我们打印出来就是0,同理,return 1就会打印出来1
信赖仔细的小同伴已经发现了上面说的是进程正常竣事的时候,为什么正常竣事,岂非进程还有可能不正常竣事吗?答案当然是肯定了,下面我们就来详细介绍一下进程退出场景。
①进程退出场景
●代码运行完毕,结果正确
●代码运行完毕,结果不正确
●代码异常终止
一样平常而言,进程的退出场景大致分为上面三种,其中哪些场景使我们用户最为关心的,当然是除了第一种正常退出场景以外的两种场景。在进程这个角度,谁会关心我运行的情况呢,一样平常而言是我们进程的父进程,但是它怎么能知道我们进程运行的情况呢,因为进程之间是相互独立,因此就需要用到“退出码”,即retrn的差异的返回值数字,来表征进程正常运行完毕后结果是否正确,如果不正确的话,就可以根据返回的数字让父进程相识到出错的原因,这也就为什么main函数要有返回值了,而且一样平常0这个返回值表征的是结果正确。那么除了0以外,别的的返回值大概说错误码表示的是什么原因呢,我们怎么能知道嘞?
strerror(错误码):查看错误码对应的错误信息
通过下面这段代码我们就可以将系统中全部的错误码对应的错误文本打印出来
- 1 #include<stdio.h>
- 2 #include<unistd.h>
- 3 #include <sys/types.h>
- 4 #include <stdlib.h>
- 5 #include<string.h>
- 6 #define N 5
- 7
- 8 int main()
- 9 {
- 10
- 11 for(int i=0;i<100;++i)
- 12 {
- 13 printf("%d: %s\n ",i,strerror(i));
- 14 }
- 15 return 0; //进程的退出码,表征进程的运行结果是否正确 0->success
- 16 }
复制代码
既然看到错误码对应的错误信息后,让我们验证一下是否真的存在如许的对应关系,当我们是查看一个不存在的文件时,会打印出来“No such file or directory”,由于ls也是一个进程,于是我们查看gain进程的退出码,发现是2,恰好与之对应。
因此,系统提供的错误码和错误码描述是有对应关系的,但是我可不可以不用系统提供的,自己设计一套退出码体系,答案当然是可以的,比如像下面代码如许,当然读者也可以根据自己的需求写出个性化的错误描述。
- 1 #include<stdio.h>
- 2 #include<unistd.h>
- 3 #include <sys/types.h>
- 4 #include <stdlib.h>
- 5 #include<string.h>
- 6 #define N 5
- 7
- 8 const char *errorString[]= {
- 9 "success",
- 10 "error 1",
- 11 "error 2",
- 12 "error 3 ",
- 13 "erroe 4",
- 14 "error 5",
- 15 };
- 16
- 17 int main()
- 18 {
- 19
- 20 for(int i=0;i<6;++i)
- 21 {
- 22
- 23 printf("%d: %s\n ",i,errorString[i]);
- 24 // printf("%d: %s\n ",i,strerror(i));
- 25 }
- 26 return 0; //进程的退出码,表征进程的运行结果是否正确 0->success
- 27 }
复制代码
在实际利用的时候我们每每会将退出码与错误码合并,即进程固然运行完毕,但是结果不正确,我们就会将错误码作为退出码进行返回,就像如许:
- 1 #include<stdio.h>
- 2 #include<unistd.h>
- 3 #include <sys/types.h>
- 4 #include <stdlib.h>
- 5 #include<string.h>
- 6 #include <errno.h>
- 7 #define N 5
- 8
- 9
- 10
- 11 int main()
- 12 {
- 13 int ret=0;//退出码
- 14 char *p= (char*)malloc(1000*1000*1000*4);
- 15 if(p == NULL)
- 16 {
- 17 //申请失败
- 18 printf("malloc error, %d: %s\n",errno, strerror(errno));
- 19 }
- 20 else{
- 21 //使用申请的内存
- 22 printf("malloc success\n");
- 23 }
- 24 return ret;
- 25 }
复制代码
不外进程的退出码只有当进程正常竣事的时候才故意义,如果代码异常的话,如果进程的退出码都没有返回,如果返回来可能也不具有可信度,因此当代码异常的时候,我们就要不再关心退出码了,而是关心为什么代码会出现异常,以及发生了什么异常?以是,综上所述,对于一个进程退出的步骤,我们应该是先看是否异常,然后才查看退出码。
②信号
当一个进程出现异常的时候,本质是我们的进程收到了对应的信号,那一共有哪些信号呢?
kill -l:查看所有信号
当我们执行下述代码时,进程就会被异常终止,而引起它的信号就是上面的8号信号
- int main()
- {
- int a=10/0;
- return 0;
- }
复制代码
③进程常见退出方法
当我们在执行代码的时候,如果结果不对,我们就可以让该进程退出,那么常见的进程退出方法有那么呢?
正常退出终止(可以通过echo $? 查看退出码)
1. 从main返回
2. 调用exit
3. _exit
异常退出
●ctrl + c,信号终止
exit函数
#include <stdlib.h>
void exit(int status);
exit函数就可以让我们的进程退出并伴有退出码,但是有的小同伴问我们不是可以return退出码吗,为什么还有另外有一个exit函数呢? 这是因为return只有在main函数中才能使进程竣事,但如果是在其他函数只是会让该函数返回,并不能使该函数竣事,而exit函数无论在那边调用,都可以使进程竣事
_exit函数
#include <unistd.h>
void _exit(int status);
_exit和exit两个函数都能使进程竣事,但是后者属于系统指令,也就是它就是单纯的竣事一个进程,但是前者在调用_exit竣事进程之前还做了其他的工作
1. 执行用户通过 atexit或on_exit界说的清算函数。
2. 关闭所有打开的流,所有的缓存数据均被写入
3. 调用_exit
三、小提问
到此为止,讲解的部分已经竣事了,不知道小同伴们能不能答复下面的问题呢?
1.怎样一次性创建多个进程
2.进程退出有哪些场景
3.进程常见的退出方式有哪些,有什么区别呢
四、结语
到此为止,本文有进程控制(上)的讲解就到此竣事了,如有不足之处,欢迎小同伴们指出呀!
关注我 _麦麦_分享更多干货:_麦麦_-CSDN博客
大家的「关注❤️ + 点赞 |