🐶博主主页:@ᰔᩚ. 一怀明月ꦿ
❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux
🔥座右铭:“不要等到什么都没有了,才下定决心去做”
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
目录
mm_struct
写时拷贝
fork
fork常规用法
fork调用失败的原因
创建一个多进程
进程终止
main函数的返回值
strerror
有时候用echo $?显示退出码并不和库里标准的对应
C语言的错误码
mm_struct
写时拷贝
父进程创建子进程的时候,首先将自己的页表读写权限改为只读,然后再创建子进程
(注意:代码段一直是只读的),但是这个过程用户是不知道的!用户就有可能对某一批数据进行写入!
此时,页表就会因为权限问题出错
1)真的出错了,例如用户想要修改代码的数据
2)例如用户想要修改数据段的数据,操作系统就会重新申请内存
注意:
1.在地址空间里面绝对不会出现划分的区域重叠的
2.Os操作系统肯定知道我们在读还是在写
为什么子进程发生写时拷贝的时候,需要拷贝父进程数据段的代码再进行写入,而不是直接申请空间直接写入呢?
子进程不一定要对父进程数据段全部进行修改,可能修改一部分
fork
fork常规用法
1.一个父进程希望复制自己,使父子进程同时执行不同的代码段,例如,父进程等待客户端请求,生成子进程来处理请求。
2.一个进程执行一个不同的程序。例如子进程从fork返回后,调用exec函数
fork调用失败的原因
fork失败的返回值小于0
1.系统进程太多
2.实际用户的进程数超过了限制
创建一个多进程
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #define N 10 typedef void (*callback_t)();//函数指针 void Worker()//子进程执行的方法 { int cnt=10; while(cnt) { printf("I am child process,pid:%d ,ppid: %d ,cnt:%d\n",getpid(),getppid(),cnt); sleep(1); cnt--; } } void createSubProcess(int n,callback_t cb)//创建n个子进程+ { int i=0; for(i=0;i<n;i++) { sleep(1); pid_t id=fork(); if(id==0) { printf("create child process success:%d\n",i); //child cb(); exit(0); } } } int main() { createSubProcess(N,Worker); //只有父进程走到这里 sleep(100); return 0; } 当n个子进程运行完之后,父进程并没有结束(因为sleep(100)),所以子进程就会形成僵尸进程 PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 17831 26353 26353 17831 pts/0 26353 S+ 1000 0:00 ./myprocess 26353 26380 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26458 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26540 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26555 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26629 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26711 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26758 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26808 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26882 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct> 26353 26929 26353 17831 pts/0 26353 Z+ 1000 0:00 [myprocess] <defunct>
进程终止
main函数的返回值
[BCH@hcss-ecs-6176 ~]$ cat test.c #include<stdio.h> int main() { return 10; } [BCH@hcss-ecs-6176 ~]$ ./mytest [BCH@hcss-ecs-6176 ~]$ echo $?//获取main函数的返回值 10
?:保存的就是最近一个子进程执行完毕时的退出码
就类似于 echo $环境变量 将环境变量的内容输出到屏幕上
echo $?将最近一个子进程执行完毕时的退出码输出到屏幕上在多进程环境中,我们(父进程)创建子进程的目的是什么?
帮我们办事
子进程把事情办得怎么样呢??main函数的返回值,就叫做进程的退出码,0->成功,非0表示失败
非0的时候,这个进程因为什么原因失败!
1,2,3,4,5,6……我们可以用不同的数字表示不同的原因!!纯数字能表示出错原因,但是不便于人阅读,exit code->exit code string (将数字转化为字符串的形式便于人去阅读)
strerror
在C语言中,strerror是一个库函数,用于将错误码或退出码转换为对应的错误消息字符串。
通过strerror查看linux中c的退出码(错误码) [BCH@hcss-ecs-6176 ~]$ cat test.c #include<stdio.h> #include<string.h> int main() { int i=0; for(i=0;i<200;i++)//之所以是200,因为我不知道linux中退出码的范围(实际上退出码的范围0-133) { printf("%d:%s\n",i,strerror(i)); } return 10; } [BCH@hcss-ecs-6176 ~]$ ./mytest 0:Success 1:Operation not permitted 2:No such file or directory 3:No such process 4:Interrupted system call 5:Input/output error 6:No such device or address 7:Argument list too long 8:Exec format error 9:Bad file descriptor 10:No child processes 11:Resource temporarily unavailable 12:Cannot allocate memory 13:Permission denied 14:Bad address 15:Block device required 16:Device or resource busy 17:File exists 18:Invalid cross-device link 19:No such device 20:Not a directory 21:Is a directory 22:Invalid argument 23:Too many open files in system 24:Too many open files 25:Inappropriate ioctl for device 26:Text file busy 27:File too large 28:No space left on device 29:Illegal seek 30:Read-only file system 31:Too many links 32:Broken pipe 33:Numerical argument out of domain 34:Numerical result out of range 35:Resource deadlock avoided 36:File name too long 37:No locks available 38:Function not implemented 39:Directory not empty 40:Too many levels of symbolic links 41:Unknown error 41 42:No message of desired type 43:Identifier removed 44:Channel number out of range 45:Level 2 not synchronized 46:Level 3 halted 47:Level 3 reset 48:Link number out of range 49:Protocol driver not attached 50:No CSI structure available 51:Level 2 halted 52:Invalid exchange 53:Invalid request descriptor 54:Exchange full 55:No anode 56:Invalid request code 57:Invalid slot 58:Unknown error 58 59:Bad font file format 60:Device not a stream 61:No data available 62:Timer expired 63:Out of streams resources 64:Machine is not on the network 65:Package not installed 66:Object is remote 67:Link has been severed 68:Advertise error 69:Srmount error 70:Communication error on send 71:Protocol error 72:Multihop attempted 73:RFS specific error 74:Bad message 75:Value too large for defined data type 76:Name not unique on network 77:File descriptor in bad state 78:Remote address changed 79:Can not access a needed shared library 80:Accessing a corrupted shared library 81:.lib section in a.out corrupted 82:Attempting to link in too many shared libraries 83:Cannot exec a shared library directly 84:Invalid or incomplete multibyte or wide character 85:Interrupted system call should be restarted 86:Streams pipe error 87:Too many users 88:Socket operation on non-socket 89:Destination address required 90:Message too long 91:Protocol wrong type for socket 92:Protocol not available 93:Protocol not supported 94:Socket type not supported 95:Operation not supported 96:Protocol family not supported 97:Address family not supported by protocol 98:Address already in use 99:Cannot assign requested address 100:Network is down 101:Network is unreachable 102:Network dropped connection on reset 103:Software caused connection abort 104:Connection reset by peer 105:No buffer space available 106:Transport endpoint is already connected 107:Transport endpoint is not connected 108:Cannot send after transport endpoint shutdown 109:Too many references: cannot splice 110:Connection timed out 111:Connection refused 112:Host is down 113:No route to host 114:Operation already in progress 115:Operation now in progress 116:Stale file handle 117:Structure needs cleaning 118:Not a XENIX named type file 119:No XENIX semaphores available 120:Is a named type file 121:Remote I/O error 122:Disk quota exceeded 123:No medium found 124:Wrong medium type 125:Operation canceled 126:Required key not available 127:Key has expired 128:Key has been revoked 129:Key was rejected by service 130:Owner died 131:State not recoverable 132:Operation not possible due to RF-kill 133:Memory page has hardware error 134:Unknown error 134//这里已经没有了
连续echo $?
[BCH@hcss-ecs-6176 ~]$ echo $? 10 [BCH@hcss-ecs-6176 ~]$ echo $? 0 [BCH@hcss-ecs-6176 ~]$ echo $? 0
为什么第一个echo $?输出的就是10,第二个echo $?输出就是0呢?
因为第一个echo $?中的?保留的是./mytest进程的退出码,而./mytest进程的退出码是10,
第二个echo $?中的?保留的是第一个echo $?这个进程的退出码,因为第一个echo $?进程退出码是0
有时候用echo $?显示退出码并不和库里标准的对应
[BCH@hcss-ecs-6176 ~]$cd /home/wcq bash: cd: /home/wcq: Permission denied [BCH@hcss-ecs-6176 ~]$echo $? 1 库里13:Permission denied,Permission denied表示的退出码是13 但是echo $?显示的值是1 所以这里用退出码不是使用的C语言标准库的,而是使用的自定义的
main函数的退出码是可以被父进程获取的,用来判断子进程的运行结果
C语言的错误码
c语言中的全局变量errno
在C语言中,errno是一个全局变量,用于表示最近一次发生的错误码。它定义在 <errno.h> 头文件中。
错误码VS退出码
错误码通常是衡量一个库函数或者是一个系统调用一个函数的调用的情况
退出码通常是一个进程退出的时候,他退出结果当失败的时候,用来衡量函数,进程出错时的出错详细信息原因
linux内核是c语言写的,linux中很多的系统调用就设置了errno
一般代码出异常,代码是没有运行完的
异常问题——引入一下 [BCH@hcss-ecs-6176 ~]$ cat test.c #include<stdio.h> #include<string.h> int main() { int a=10; a/=0; } [BCH@hcss-ecs-6176 ~]$ ./mytest 浮点数例外//异常 [BCH@hcss-ecs-6176 ~]$ cat test.c #include<stdio.h> #include<string.h> int main() { int *p=NULL; *p=100; return 10; } [BCH@hcss-ecs-6176 ~]$ ./mytest 段错误//异常 进程异常会被os杀掉这个进程(kill) 进程异常的时候,会转化成信号,会被os检测到的
结论:进程出异常,本质是进程收到了对应的信号,自己终止了!!
🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸