IO进程day04(进程)

news2024/9/25 8:03:08

目录

进程

1》什么是进程

1> 概念

 2> 特点

 3> 进程段

4> 进程分类 

5> 进程状态

6> 进程状态切换图

7> 进程相关命令

 <补充>优先级调度

 2》进程函数接口

1> 创建进程 fork()

2> 回收资源

3> 结束进程

4> 获取进程号

3》exec函数族

 4》守护进程

1> 守护进程的特点

2> 创建步骤

练习:创建一个守护进程,循环间隔1s向文件中写入一串字符“hello”

3> 总结


进程

1》什么是进程

1> 概念

程序:编译好的可执行文件

存放在磁盘上的指令和数据的有序集合(文件)

程序是静态的,没有任何执行的概念


进程:一个独立的可调度的任务

执行一个程序分配资源的总称

进程是程序执行的一次过程

进程是动态的,包括创建、调度、执行、消亡

 2> 特点

(1)系统会为每一个进程分配0-4g 的虚拟空间,其中 0-3g (用户空间)是每个进程所独有的,3-4g (内核空间)是所有进程所共有的。进程间通信通过内核空间。

 (2)CPU调度进程时会给进程分配时间片(几毫秒 ~十几毫秒),当时间片用完后,cpu再进行其他进程的调度,实现进程的轮转,从而实现多任务的操作。(没有外界干预的情况下怎么调度进程是CPU随机分配的)

 

进程控制块task_struct (了解)

  • 进程控制块pcb: 包含描述进程的相关信息
  • 进程标识PID:唯一的标识一个进程

主要进程标识:

进程号(PID: Process Identity Number)

父进程号:(Parent Process ID: PPID)

  • 进程用户
  • 进程状态、优先级
  • 文件描述符(记录当前进程打开的文件)

 3> 进程段

Linux中的进程大致包含三个段:

数据段:存放的是全局变量、常数以及动态数据分配的数据空间(如 malloc 函数取得的空间)等。

正文段:存放的时程序中的代码

堆栈段:存放的是函数的返回地址、函数的参数以及程序中的局部变量

4> 进程分类 

交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户的输入后,该类进程会立刻响应,典型的交互式进程有:shell命令进程、文本编辑器等

批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行。

守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。

5> 进程状态

D               uninterruptible sleep (usually IO) 不可中断的睡眠态

R               running or runnable (on run queue) 运行态

S               interruptible sleep (waiting for an event to complete) 可中断的睡眠态

T                stopped by job control signal 暂停态

t                 stopped by debugger during the tracing 因为调试而暂停

X               dead (should never be seen) 死亡态

Z                defunct ("zombie") process, terminated but not reaped by its parent 僵尸态

<                high-priority (not nice to other users) 高优先级

N                low-priority (nice to other users) 低优先级

L                 has pages locked into memory (for real-time and custom IO) 锁在内存中

s                 is a session leader 会话组组长

l                  is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)多线程

+                 is in the foreground process group 前台进程

没有+时,默认是后台进程

6> 进程状态切换图

进程创建后,进程进入就绪态,当cpu调度此进程时进入运行态,当时间片用完时,此进程就会进入就绪态,如果此进程正在执行一些IO操作(阻塞操作)会进入阻塞态,完成IO操作(阻塞结束)后又可进入就绪态,等待CPU的调度,当进程运行结束即进入结束态。

什么是阻塞和非阻塞?

阻塞(blocking)、非阻塞(non-blocking):可以简单理解为需要做一件事能不能立即得到返回应答,如果不能立即获得返回,需要等待,那就阻塞了,在等待的过程中可以做其它事情。否则就可以理解为非阻塞。

7> 进程相关命令

ps 查看系统中的进程 -aux -ef

top 动态查看系统中的进程

nice 按用户指定的优先级运行进程

renice 改变正在运行的进程的优先级

kill 给进程发信号

fg 将进程切换到前台执行

bg 将进程切换到后台执行

jobs 擦看当前终端的后台进程

 <补充>优先级调度

根据进程的优先级进行调度,优先级高的进程先执行。

两种类型:

  1. 非剥夺式(非抢占式)优先级调度算法。当一个进程正在处理上运行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在进行的进程继续运行,直到由于其自身原因而主动让出处理机(任务完成或等待事件),才把处理机分配给更为重要或紧迫的进程。
  2. 剥夺式(抢占式)优先级调度算法。当一个进程正在处理机上运行时,若有某个更为重要或紧迫的进程进入就绪队列,则立即暂停正在运行的进程,将处理机分配给更重要或紧迫的进程。

 2》进程函数接口

1> 创建进程 fork()

pid_t fork(void);

功能:创建子进程

返回值:

成功:在父进程中:返回子进程的进程号 >0

           在子进程中:返回值为0

失败:-1并设置errno

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char const *argv[])
{
    pid_t pid;
    pid = fork();  //创建了一个子进程,此时子进程和父进程
//宏观上同时执行,微观上根据分配时间片区分先后,子进程和父进程都会执行下面的if判断语句
    if(pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if(pid == 0)//子进程
    {
        printf("i am child process\n");
        while(1);   //为了让子进程不要结束
    }
    else//父进程
    {
        printf("i am parent process\n");
        while(1);   //为了让父进程不要结束
    }

    return 0;
}

特点:

(1)子进程几乎拷贝了父进程的全部内容,包括代码、数据、系统数据段中的pc值、栈中的数据、父进程中打开的文件等,但是他们的PID、PPID是不同的。

(2)父子进程有独立的地址空间,互不影响,当在相应的进程中改变全局变量、静态变量,都互不影响。

(3)若父进程先结束,子进程成为孤儿进程,被INIT进程收养,子进程变成后台进程。

(4)若子进程先结束,父进程如果没有及时回收资源,子进程会变成僵尸进程(要避免僵尸进程的衬产生,会占用空间)

2> 回收资源

pid_t wait(int *status);

功能:回收子进程资源(阻塞)

参数:status:子进程退出状态,不接受子进程状态设为NULL

返回值:成功:回收的子进程的进程号

              失败:-1

pid_t waitpid(pid_t pid, int *status, int options);

功能:回收子进程资源

参数:

pid:>0 指定子进程进程号

        =-1 任意子进程

        =0 等待其组ID等于调用进程的组ID的任一子进程

        <-1 等待其组ID等于pid的绝对值的任一子进程

status:子进程退出状态

options:0:阻塞 WNOHANG:非阻塞

返回值:正常:结束的子进程的进程号

              当使用选项WNOHANG且没有子进程结束时:0

              出错:-1

 wait 练习:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>

int main(int argc, char const *argv[])
{
    pid_t pid;
    int a = 0;
    pid = fork(); // 创建了一个子进程
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        printf("i am child process  %d  %d\n", getpid(), getppid());
        sleep(3); // 让子进程睡眠 3 秒之后再结束
    }
    else
    {
        wait(NULL); // 给任意结束的子进程回收资源,如果没有子进程结束就会一直阻塞等待
        // waitpid(-1,NULL,0); //代表阻塞,此时和wait(NULL)的效果一样
        printf("i am parent process %d  %d\n", pid, getpid());
        while (1)
            ; // 为了让父进程不要结束
    }

    return 0;
}

waitpid 练习:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>

int main(int argc, char const *argv[])
{
    pid_t pid;
    int a = 0;
    pid = fork(); // 创建了一个子进程
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        printf("i am child process  %d  %d\n", getpid(), getppid());
        sleep(3); // 让子进程睡眠 3 秒之后再结束
    }
    else
    {
        // wait(NULL); // 给任意结束的子进程回收资源,如果没有子进程结束就会一直阻塞等待
        waitpid(-1, NULL, WNOHANG);//WNOHANG 代表不阻塞,此时当父进程执行到这句时,子进程可能处于睡眠状态,还未结束,这时就不会回收子进程资源,可能就会使子进程变成僵尸进程
        
        //可以采用循环来解决这个问题,不停的检测是否有结束的子进程,直到回收了之后才结束
        while (1)
        {
            if (waitpid(-1, NULL, WNOHANG) > 0)
                break;
        }
        printf("i am parent process %d  %d\n", pid, getpid());
        while (1)
            ; // 为了让父进程不要结束
    }

    return 0;
}

3> 结束进程

void exit(int status);

功能:结束进程,刷新缓存

void _exit(int status);

功能:结束进程,不刷新缓存

参数:status是一个整型的参数,可以利用这个参数传递进程结束时的状态。

           通常0表示正常结束;

           其他的数值表示出现了错误,进程非正常结束

 exit 练习:

 _exit 练习

4> 获取进程号

pid_t getpid(void);

功能:获取当前进程的进程号

pid_t getppid(void);

功能:获取当前进程的父进程号

 练习:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    pid_t pid;
    pid = fork(); //创建了一个子进程
    if (pid < 0)
    {
        perror("fork err");
        return -1;
    }
    else if (pid == 0)
    {
        printf("i am child process:%d %d\n", getpid(), getppid());
    }
    else
    {
        printf("i am parent process: %d %d\n", pid, getpid());
    }
    while (1);  //让父子进程都不要结束

    return 0;
}

3》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[]);

int execvpe(const char *file, char *const argv[],char *const envp[]);

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    printf("hello\n");
    //system("ls -l");   //原来进程不会被替换,执行完ls -l启动的进程之后继续执行原先进程
    execl("/bin/ls","ls","-l",NULL);  //原先进程被ls -l启动的进程替换了,所以后面打印语句不执行了
    printf("world\n");

    return 0;
}

 4》守护进程

Linux以会话(session)、进程组的方式管理进程,每个进程属于一个进程组,也就是多个进程组成一个进程组。会话是一个或多个进程组的集合,通常用户打开一个终端时,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话。终端关闭时,所有相关进程会被结束。但是守护进程却能突破这种限制,不受终端关闭的影响。

1> 守护进程的特点

守护进程是后台进程

生命周期比较长,从系统启动时开启,系统关闭时结束

它是脱离控制终端且周期执行的进程

2> 创建步骤

(1)创建子进程,父进程退出。

让子进程变成孤儿进程,成为后台进程; fork()

(2)在子进程中创建新会话

让子进程成为会话组组长并且脱离终端:为了让子进程完全脱离终端;setsid()

(3)改变进程运行路径为根目录

原因: 进程运行的路径不能被删除或卸载;chdir("/")

函数说明:chdir() 将进程当前的工作目录改变成以参数路径所指的目录

(4)重设文件权限掩码

目的:增大进程创建文件时权限,提高灵活性;umask(0)

子进程继承了父进程的文件权限掩码,给该子进程使用文件带来一定的影响,因此把文件 权限掩码设置为0,可以增强该守护进程的灵活性。

(5)关闭文件描述符

原因:子进程继承了父进程的一些已经打开了的文件,这些被打开的文件可能永远不会被 守护进程访问,但它们一样占用系统资源,而且还可能导致所在的文件系统无法被卸载。

将不需要的文件关闭:close()

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/stat.h>

int main(int argc, char const *argv[])
{
    pid_t pid = fork();//创建子进程
    if(pid<0)
    {
        perror("fork err");
        return -1;
    }
    if(pid == 0)
    {
        setsid();//在子进程中创建新会话
        chdir("/");//改变进程运行路径为根目录
        umask(0);//重设文件权限掩码
        for(int i=0;i<3;i++) //关闭默认打开的0 1 2
            close(i);
        while (1);
    }
    else 
    {
        exit(0);
    }

    return 0;
}

练习:创建一个守护进程,循环间隔1s向文件中写入一串字符“hello”
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>
#include <fcntl.h>


int main(int argc, char const *argv[])
{
    // FILE *fp;
    // fp = fopen(argv[1], "a+");
    // if (NULL == fp)
    // {
    //     printf("open err\n");
    //     return -1;
    // }
    // printf("open success\n");

    int fd;//定义一个变量接收文件描述符
    fd = open(argv[1],O_RDWR);//打开命令行中文件
    if(fd < 0)
    {
        printf("open err\n");
        return -1;
    }
    printf("open success\n");


    pid_t pid;//定义一个变量接收 fork()返回值
    pid = fork();
    if (pid < 0)//失败
    {
        perror("fork err\n");
        return -1;
    }
    else if (pid == 0)//fork 返回值为 0 说明是子进程
    {
        printf("child\n");

        setsid();//在子进程中创建新会话
        chdir("/");//改变进程运行路径为根目录
        umask(0);//重设文件权限掩码
        for(int i=0;i<3;i++) //关闭默认打开的0 1 2
            close(1);
        while (1)
        {
            // fputs("hello", fp);
            // fflush(NULL);
            write(fd,"hello",5);//向文件中写入”hello“ 
            sleep(1);//每隔 1 秒写入一次
        }
        //想要停止只能通过 kill -9 将守护进程杀死

        while (1)
            ;
    }
    else
    {
        printf("parent\n");
        // exit(0);
    }

    return 0;
}

3> 总结

(1)守护进程是一个生存周期较长的进程,通常独立于控制终端并且周期性的执行某种任务或者等待处理某些待发生的事件

(2)大多数服务都是通过守护进程实现的

(3)关闭终端,相应的进程都会被关闭,而守护进程却能够突破这种限制


 今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2084048.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 08网络自动化

云原生数据中心和老一代数据中心不同之处在于其核心概念是聚焦于高效运营。网络自动化就是达到此目标的关键因素。 要达到此目的&#xff0c;本章要解决诸如下述的一些问题&#xff1a; 什么是网络自动化以及为什么我们在乎它?为了学习网络自动化&#xff0c;我需要学习编程…

栈OJ题——栈的压入、弹出序列

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 栈的压入、弹出序列 题目描述&#xff1a;给定两个整型数组&#xff0c;判断数组2是否是数组1的某一个出栈顺序。该题默认可以边入栈边出栈。 二、解题思路 三、解题代码 但是&#xff0c;上述解题代码中还存在一…

HCIP第一天作业

要求&#xff1a;R1-R2-R3-R4-R5 RIP 100 运行版本2 R6-R7 RIP 200 运行版本1 1.使用合理IP地址规划网络&#xff0c;各自创建环回接口 2.R1创建环回 172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.减少路由条目数量&#xff0c;R1-R2之间增…

Echarts栅格进度条装饰实现

如下图&#xff0c;如果你的业务需要这么一个饼图&#xff0c;你单纯借助echarts是实现不了如图效果的&#xff0c;你需要借助dom操作&#xff0c;合svg的配合才能实现。 首先饼图部分结束echarts,实现以及通过配置实现你想要的效果。 中间的文字百分比计算需要自己计算&#…

springboot3.x入门系列【5】支持unix sock 套接字服务

目录 一、简介 二、springBoot3.x 套接字的支持 1. 环境要求 2. springboot内置tomcat 2.1 支持unix 设置 unixDomainSocketPath 2.2 windows 下unix服务测试 3. springboot外置tomcat 3.1 tomcat 配置unix socket 套接字 3.2 启动tomcat 服务 3.3 nginx 支持unix…

python中传递任意数量的实参

有时候&#xff0c;你预先不知道函数需要接受多少个实参&#xff0c;好在Python允许函数从调用语句中收集任意数量的实参。 一个*是元组&#xff0c;两个是字典。

交换机自动化巡检(H3C)

目的:通过python实现全自动化交换机巡检&#xff08;每周五下午五点进行自动化巡检&#xff09; 1、环境&#xff1a; 系统&#xff1a;windows10 工具&#xff1a;python-3.11.2&#xff08;自行安装&#xff09; 工具&#xff1a;PyCharm Community Edition 2022.3.3&…

Vue使用v-model收集各种表单数据、过滤器

目录 1. 使用v-model收集各种表单数据2. 日期格式化3. 过滤器 1. 使用v-model收集各种表单数据 若<input type“text”/>&#xff0c;则v-model收集的是value值&#xff0c;用户输入的就是value值若<input type“radio”/>&#xff0c;则v-model收集的是value值&a…

【Linux系列】du命令详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【线程池】

什么是线程池&#xff1f; 线程池是一个可以复用线程的技术。简单来说&#xff0c;线程池是一种基于池化技术的思想来管理线程的技术&#xff0c;旨在减少线程的创建和销毁次数&#xff0c;提高系统的响应速度和吞吐量。它预先创建了一定数量的线程&#xff0c;并将这些线程放…

MySQL高可用性实践指南

一 、Mysql 在服务器中的部署方法 1、安装依赖性 yum install libtirpc-devel-0.2.4-0.16.el7.x86_64.rpm -y yum install ncurses-devel.x86_64 -y yum install gcc-c -y yum install openssl-devel -y yum install cmake -y 2、下载并解压源码包 tar zxf mysql-boost-5.7…

十年热爱,小鹏汽车开启AI新篇章

“每一台小鹏汽车&#xff0c;都是同级别智能的天花板。”在8月27日的小鹏10年热爱之夜暨小鹏MONA M03上市发布会上&#xff0c;小鹏汽车董事长、CEO何小鹏不无自豪地表示。 对于小鹏汽车来说&#xff0c;此次发布会有着非凡的意义&#xff0c;因为它不仅是对小鹏汽车过去十年辉…

前端用js发送邮箱 前端发送邮箱

前端发送邮箱 安装包依赖邮箱授权码demo eg:picture页面发送内容有效接受效果 code参考别人写的code 说明 安装包依赖 yarn add nodemailernodemailer官网 邮箱授权码 邮箱授权码: 邮箱授权码需要开通&#xff0c;以QQ邮箱为例&#xff0c;其它大同小异 demo eg: picture…

ROCm Code Object Tooling

ROCm&#xff08;Radeon Open Compute&#xff09;提供了一系列的工具&#xff0c;用于检查和提取编译器生成的代码对象&#xff0c;包括可执行文件、目标文件和共享对象库。 一、roc-obj roc-obj 是 ROCm&#xff08;Radeon Open Compute&#xff09;提供的一个高层级工具&a…

【软件文档】需求规格说明书编制模板

1 范围 1.1 系统概述 1.2 文档概述 1.3 术语及缩略语 2 引用文档 3 需求 3.1 要求的状态和方式 3.2 系统能力需求 3.3 系统外部接口需求 3.3.1 管理接口 3.3.2 业务接口 3.4 系统内部接口需求 3.5 系统内部数据需求 3.6 适应性需求 3.7 安全性需求 3.8 保密性需求 3.9 环境需求…

数据结构(邓俊辉)学习笔记】串 14——BM_GS算法:构造gs表

以下&#xff0c;就来简要地介绍 gs 表的具体构造算法。算法为了便于理解其原理&#xff0c;这里将整个算法划分为若干的层次&#xff0c;并逐层递进、逐层细化。 我们首先需要引入 MS 子串与 ss 表的概念。实际上&#xff0c;相对于模式串中的任何一个字符 P[j] &#xff0c…

RP2040 C SDK开发串口的使用

RP2040 C SDK开发串口的使用 &#x1f4cd;环境搭建部署篇《RP2040 VSCode C/C开发环境快速部署》&#x1f516;RP2040 有硬件串口资源有2个。&#x1f33f;参考RP2040 C SDK Hardware APIS&#xff1a;https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#grou…

安卓APK重签名并查看MD5值-2024最新版

重签名 命令行运行&#xff1a; apksigner sign --ks your_keystore.jks --out output.apk input.apk在这个命令中&#xff1a; –ks 或 --keystore 参数后面是你的keystore文件路径。 your_keystore.jks 是你的keystore文件。 –out 参数后面是输出的签名后的APK文件名。 out…

发布npm包到GitLab教程

之前在研究如何搭建UI组件库&#xff08;内部使用&#xff09;&#xff0c;其中重要的一步就是发布npm包到GitLab。中间踩了很多坑&#xff0c;在这里记录一下整个流程方便大家快速上手。不足之处欢迎指出&#x1f64f; 1. 获取Token 在gitlab中打开access tokens申请页面&am…

鲲鹏服务器之ARM探知

什么叫arm架构 ARM架构过去称作进阶精简指令集机器&#xff08;Advanced RISC Machine&#xff0c;更早称作&#xff1a;Acorn RISC Machine&#xff09;&#xff0c;是一个32位精简指令集&#xff08;RISC&#xff09;处理器架构&#xff0c;其广泛地使用在许多嵌入式系统设计…