Linux(九)fork复制进程与写时拷贝技术

一、printf隐藏的缓冲区

首先,大家一起思考一个问题:为什么会有缓冲区的存在呢?

因为屏幕是一个硬件设备,是由操作系统来管理的,因此printf打印的时候需要调用操作系统的接口才能完成,这个时候我们需要从用户态切换到内核态,这个开销是比较大的。所以先刷新到缓冲区,当缓冲区满时,打印到屏幕上,这样在不需要时回到缓冲区即可,减少开销;程序结束就再次刷新缓冲区。

那我们为什么感觉不到缓冲区的存在呢?

大家可以在虚拟机中编写上面的main.c程序,运行后就会发现结果会先睡眠3秒,然后再打印hello,这就是因为printf先进入了缓冲区。

那么,有伙伴可能就会想缓冲区刷新到界面(屏幕)上的条件是什么呢?

(1)缓冲区放满

(2)缓冲区未满,强制刷新缓冲区到屏幕;

(3)程序结束时,自动刷新缓冲区:exit方法;

那我们就在介绍一下强制刷新缓冲区的两种方法:

(1)遇到\n自动刷新

在这里添加了'\n',运行结果就是先hello,后睡眠

(2)手动刷新

fflush(stdout);

std:标准库,out输出

运行结果也是先hello,后睡眠

小编在额外说一下exit(0)

exit是先刷新缓冲区,然后再调用_exit(真正的退出)。

_exit直接退出,不会刷新缓冲区。

大家可以试着运行一下下面这个代码,就会发现,不输出hello

复制代码
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
  printf("hello");
  sleep (3);
  _exit (0);
 }

二、fork复制进程

1、shell

在计算机科学中,Shell俗称壳(用来区别于核),是指"为使用者提供操作界面"的软件(command interpreter,命令解析器)。它类似于DOS下的COMMAND.COM和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。

我们就是通过命令解释器(称为shell)(bash是命令解释器中的一种)和内核和系统进行交互的

(Windows通过图形界面进行交互的);例如我们把Is交给bash,bash帮我们运行Is,然后把结果给用

户;

大家可以ps -f一下就可以发现: 一个终端一个bash,因为PID不同

2、fork如何复制进程

fork是把已有的进程复制一份,当然把PCB也复制了一份,然后申请一个PID,子进程的PID=父进程的PID+1;

如果父子进程想要做不同的事情,那么我们通过返回值来判断。

我们现在想要使用fork(),但是我们既不知道参数类型也不知道返回值,这个时候就要用到

man fork 这个命令调用fork的帮助手册我们通过执行下面的程序可以来感受一下父子进程:

复制代码
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
 
int main(){
    char *s=NULL;
    int n=0;//控制父子进程执行的次数;
 
    pid_t id=fork();
    assert(id !=- 1);
 
    if(id == 0){
        s="child";
        n=3;
    }//子进程
    
    else{
        s="parent";
        n=7;
    }//父进程
    //父子进程
    int i=0;
    for(;i<n;i++){
        printf("s=%s\n",s);
        sleep(1);//是因为两个进程同时打印太快了,一下就出来感受不到,sleep后就感觉到同时
    }
    exit(0);
}

通过上面的代码和运行结果,我们知道了父子进程是两个独立的进程,各自执行各自的代码;如果父子进程要做不一样的事情,就通过if else返回值来操作。

3、fork的时机

有的朋友可能会想,我一直调用fork,不就一直在复制进程,没有终止了吗?这就涉及到了fork的时机。

fork产生的这个子进程不是从头开始执行的,而是从fork之后开始执行的,就是说fork下面的代码

子进程才开始执行,具体的是说从返回值这里子进程开始执行,子进程不会再fork了,所以不会出现

子进程再去fork产生一个子进程的问题.也就是说:从返回值这里开始,父进程返回子进程的PID,子进程返回0;

4、getppid与getpid

getppid:得到一个进程的父进程的PID;

getpid:得到当前进程的PID;

同样的,我们使用命令:man getpid; man getppid 来查看如何使用getpid,getppid

相关推荐
Lsetea13 小时前
OpenSSL verify报certificate signature failure:error 7与证书签名排查
运维·https·ssl证书·openssl·证书链
吴声子夜歌14 小时前
Nginx应用与运维——Nginx Web服务应用实战(HTTP增强协议服务器的搭建)
运维·前端·nginx
小白的码BUG之路14 小时前
Docker -- 构建ruoyi-auth镜像
运维·docker·容器
我叫洋洋16 小时前
涂鸦 T5+ hashcat 自动化破解 WPA2:从单缺 M2 到全自动值守的踩坑实录 破解wifi 密码
运维·单片机·自动化
左手の明天1 天前
SLIP 协议封装与解析详解:原理、C 代码与实战案例
linux·c语言·开发语言·c++
꯭自꯭闭꯭1 天前
达梦SQL优化相关
linux·运维·数据库·sql
ZhangJun951 天前
在 32GB 内存电脑上本地搭建 Qwen3.6-35B-A3B 大模型踩坑实录
运维·人工智能·阿里云·ai·软件构建
一条破秋裤1 天前
Linux 共享内存通信:POSIX 共享内存与 mmap
linux·运维·服务器
贵州山魈羡民1 天前
【玩转手机】通过 Podroid 上的 Alpine 部署 1Panel,把微型服务器装进口袋
运维·服务器
小白的码BUG之路1 天前
Docker -- 构建nacos镜像
运维·docker·容器