Linux 第十九章

🐶博主主页: @ᰔᩚ. 一怀明月ꦿ

❤️‍🔥专栏系列: 线性代数C初学者入门训练题解CC的使用文章「初学」C++linux

🔥座右铭:"不要等到什么都没有了,才下定决心去做"

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

mm_struct

写时拷贝

fork

fork常规用法​​​​​​​

fork调用失败的原因

创建一个多进程

进程终止

main函数的返回值

strerror

[​​​​​​​有时候用echo $?显示退出码并不和库里标准的对应](#有时候用echo $?显示退出码并不和库里标准的对应)

C语言的错误码


mm_struct

写时拷贝

父进程创建子进程的时候,首先将自己的页表读写权限改为只读,然后再创建子进程

(注意:代码段一直是只读的),但是这个过程用户是不知道的!用户就有可能对某一批数据进行写入!
此时,页表就会因为权限问题出错

1)真的出错了,例如用户想要修改代码的数据

2)例如用户想要修改数据段的数据,操作系统就会重新申请内存

注意:

1.在地址空间里面绝对不会出现划分的区域重叠的

2.Os操作系统肯定知道我们在读还是在写

为什么子进程发生写时拷贝的时候,需要拷贝父进程数据段的代码再进行写入,而不是直接申请空间直接写入呢?
子进程不一定要对父进程数据段全部进行修改,可能修改一部分

fork

fork常规用法​​​​​​​

1.一个父进程希望复制自己,使父子进程同时执行不同的代码段,例如,父进程等待客户端请求,生成子进程来处理请求。

2.一个进程执行一个不同的程序。例如子进程从fork返回后,调用exec函数

fork调用失败的原因

fork失败的返回值小于0

1.系统进程太多

2.实际用户的进程数超过了限制

创建一个多进程

cpp 复制代码
#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函数的返回值

cpp 复制代码
[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是一个库函数,用于将错误码或退出码转换为对应的错误消息字符串。

cpp 复制代码
通过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 $?

cpp 复制代码
[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 $?显示退出码并不和库里标准的对应

cpp 复制代码
[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

一般代码出异常,代码是没有运行完的

cpp 复制代码
异常问题------引入一下
[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检测到的

结论:进程出异常,本质是进程收到了对应的信号,自己终止了!!

🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸

相关推荐
Betty’s Sweet7 分钟前
[Linux]:线程(三)
linux·线程·信号量·生产者消费者模型
0DayHP13 分钟前
HTB:Bike[WriteUP]
运维·服务器
DieSnowK14 分钟前
[C++][第三方库][httplib]详细讲解
服务器·开发语言·c++·http·第三方库·新手向·httplib
程序员南飞2 小时前
ps aux | grep smart_webrtc这条指令代表什么意思
java·linux·ubuntu·webrtc
StrokeAce2 小时前
linux桌面软件(wps)内嵌到主窗口后的关闭问题
linux·c++·qt·wps·窗口内嵌
热爱嵌入式的小许6 小时前
Linux基础项目开发1:量产工具——显示系统
linux·运维·服务器·韦东山量产工具
韩楚风10 小时前
【linux 多进程并发】linux进程状态与生命周期各阶段转换,进程状态查看分析,助力高性能优化
linux·服务器·性能优化·架构·gnu
陈苏同学10 小时前
4. 将pycharm本地项目同步到(Linux)服务器上——深度学习·科研实践·从0到1
linux·服务器·ide·人工智能·python·深度学习·pycharm
Ambition_LAO10 小时前
解决:进入 WSL(Windows Subsystem for Linux)以及将 PyCharm 2024 连接到 WSL
linux·pycharm
Pythonliu710 小时前
茴香豆 + Qwen-7B-Chat-Int8
linux·运维·服务器