Linux__进程控制

1.进程创建

1.1fork函数初识

在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

#include <unistd.h> //fork函数的头文件
pid_t fork(void); //
返回值:创建成功给子进程返回0,父进程返回子进程id,出错返回-1

进程调用fork,当控制转移到内核中的fork代码后,内核(OS)做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程(fork创建子进程是以父进程为“模板”的)
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

在这里插入图片描述

在这里插入图片描述
如上图,fork之前父进程独立执行,fork之后,父子两个执行流分别执行。注意,fork之后,谁先执行完全由调度器决定。

1.2fork函数返回值

  1. 子进程返回0。
  2. 父进程返回的是子进程的pid。

其中,默认情况下,父子进程共享代码,但是数据各自私有一份!

代码共享:所有代码共享,不过一般都是从fork之后开始执行。

  • 问题:为什么代码共享?
    代码不可被修改,所以各自私有浪费空间。

数据私有:

  • 问题:数据为啥要私有一份?
    因为进程之间具有独立性。数据是很多的,不是所有的数据都立马要使用,并且不是所有的数据都要进行拷贝。但是如果立马要独立,就需要将数据全部拷贝,把本来可以在后面拷贝的,甚至不需要拷贝的数据都拷贝了,比较浪费时间和空间。

1.3 写时拷贝

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图:
在这里插入图片描述
问题:现在有数据10M,写入的时候1M,发生写时拷贝,是拷贝10M还是1M?
答案是:1M。数据段在页表中的映射是很细的,只会拷贝写入的新的数据。

1.3 fork的常规用法

1.一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
2.一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。

fork调用失败的原因

  1. 系统中有太多的进程。
  2. 实际用户的进程数超过了限制。

总结:如何理解子进程创建?

  • 本质是系统多了一个子进程,子进程以父进程为模板。

2.进程终止

2.1 进程退出的情况

  1. 代码跑完了,结果正确
  2. 代码跑完了,结果不正确
  3. 代码没跑完,程序异常终止

2.2 进程常见退出方法

正常终止(可以通过 echo $? 查看最近一次进程退出码):

  1. 从main返回
  2. 调用exit
  3. _exit

异常退出:

  • ctrl + c,信号终止

1、main函数用return退出进程;

main函数退出时, return的数字就是程序的退出码。
问题: 为什么main的return一般会写成0?
因为0在函数设计中,一般代表正确。非0代表错误。

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
  
using namespace std;
 
int main()
{
	cout << "hello" << endl;
    return 0;//退出码为0                                                                     
}

在这里插入图片描述

2、exit函数退出

#include <unistd.h>

void exit(int status);

exit:终止整个进程,任何地方都会调用,都会终止。
return:终止函数。main函数return 代表进程退出。

3、_exit函数

#include <unistd.h>
void _exit(int status);
参数:status 定义了进程的终止状态,父进程通过wait来获取该值
说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。

exit函数和_exit区别:

    1 #include <iostream>
    2 #include <unistd.h>
    3 #include <sys/types.h>
    4 #include <stdlib.h>
    5  
    6 using namespace std;
    7  
    8 int exe()
    9 {
   10   _exit(12);
   11   return 12;
   12 }
   13  
   14 int main()
   15 {
   16   cout << "you should running here!";                                                    
   17   sleep(5);//sleep5秒
   18   exit(13);                            
   19   return 0;//退出码为0                 
   20 }

在这里插入图片描述

加粗样式
_exit时系统调用,exit函数和_exit函数的最大区别就是exit函数可以刷新缓冲区而_exit函数不会刷新缓冲区。

3.进程等待

3.1进程等待的必要性

  • 子进程退出,父进程如果不处理,就可能造成僵尸进程的问题,从而导致内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,kill -9 也没办法杀死,因为谁也没有办法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。

3.2进程等待wait方法

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

在这里插入图片描述

3.3 waitpid方法

pid_ t waitpid(pid_t pid, int *status, int options);
返回值

  • 当正常返回的时候waitpid返回收集到的子进程的进程ID;
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数

  • pid:
  • Pid=-1,等待任一个子进程。与wait等效。
  • Pid>0.等待其进程ID与pid相等的子进程。

status:

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
  • options: 默认是0,代表阻塞方式
  • WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
  1. 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  2. 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  3. 如果不存在该子进程,则立即出错返回。

在这里插入图片描述

3.4 获取子进程status

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
如果我们只想获取退出码的话我们可以(st>>8)&0xff
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4.进程程序替换

4.1替换原理

用fork创建子进程:想让子进程和父进程执行不同的程序。
这要调用exec函数来执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,代码也要发生写时拷贝。
调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
这些新的代码和数据的替换过程是由os操作的,os会给我们提供接口
在这里插入图片描述

4.2 替换函数

有六种以exec开头的函数,统称为exec函数:

#include <unistd.h>

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

在这里插入图片描述

4.3 函数解释

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1。
  • 所以exec函数只有出错的返回值而没有成功的返回值。

4.4 命名理解

  1. l(list) : 表示参数采用列表
    在这里插入图片描述

  2. v(vector) : 参数用数组
    在这里插入图片描述

  3. p(path) : 有p自动搜索环境变量PATH
    带p的传入系统中的程序或者命令的时候,会自动去搜索环境变量PATH中寻找对应的指令,不需要传入文件路径不带p的,则需要自己传入文件路径。

  4. e(env) : 表示自己维护环境变量

在这里插入图片描述

4.5示例

在这里插入图片描述

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 
  4 int main()
  5 {
  6 
  7   execl("/usr/bin/ls", "ls", "-i", "-l", "-a", NULL);
  8 
  9   //带p的,可以使用环境变量PATH,无需写全路径
 10   execlp("ls", "ls", "-i", "-l", "-a",NULL);
 11   
 12   char *arg[] = {"ls", "-a", "-l", "-i", NULL};
 13   
 14   execv("/usr/bin/ls", arg);
 15  
 16   //带p的,可以使用环境变量PATH,无需写全路径
 17   execvp("ls", arg);                                                                                 
 18   printf("you should running here!\n");                   
 19   return 0;                                               
 20 }

这个错误是由于无法连接到本地主机的10248端口导致的。这个端口通常是kubelet进程监听的端口,用于健康检查。出现这个错误可能是由于kubelet进程没有正确启动或者配置错误导致的。 解决这个问题的方法是检查kubelet进程的状态和配置。你可以按照以下步骤进行操作: 1. 检查kubelet进程是否正在运行。你可以使用以下命令检查kubelet进程的状态: ```shell systemctl status kubelet ``` 如果kubelet进程没有运行,你可以使用以下命令启动它: ```shell systemctl start kubelet ``` 2. 检查kubelet的配置文件。你可以使用以下命令查看kubelet的配置文件路径: ```shell kubelet --kubeconfig /etc/kubernetes/kubelet.conf --config /var/lib/kubelet/config.yaml --bootstrap-kubeconfig /etc/kubernetes/bootstrap-kubelet.conf config view ``` 确保配置文件中的端口号和地址正确,并且与你的环境相匹配。 3. 检查网络连接。你可以使用以下命令检查是否可以连接到localhost10248端口: ```shell curl -sSL http://localhost:10248/healthz ``` 如果无法连接,请确保端口没有被防火墙或其他网络配置阻止。 4. 检查docker的配置。有时候,kubelet进程依赖于docker进程。你可以按照以下步骤检查docker的配置: - 创建/etc/docker目录: ```shell sudo mkdir /etc/docker ``` - 编辑/etc/docker/daemon.json文件,并添加以下内容: ```json { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ], "registry-mirrors": ["https://tdhp06eh.mirror.aliyuncs.com"] } ``` - 重启docker进程: ```shell systemctl restart docker ``` 请注意,以上步骤是一种常见的解决方法,但具体解决方法可能因环境而异。如果以上步骤无法解决问题,请提供更多的错误信息和环境配置,以便我们能够更好地帮助你。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值