华清远见25072班I/O学习day3

发布于:2025-09-03 ⋅ 阅读:(19) ⋅ 点赞:(0)

重点内容:

一、多进程引入

1.1 引入目的

程序员写程序时,一个程序可能由多个任务组成,如果使用的是单进程,或单任务,那么当该任务执行阻塞时,其他任务就无法执行,必须等到该任务解除阻塞后,才能去执行其他任务。

多进程或多线程,可以解决同一个程序中多个任务并发执行的情况

1.2 进程的概念

1> 进程是程序的一次执行过程,是有生命周期的:创建态、就绪态、运行态、阻塞态、死亡态

2> 进程是程序资源分配的基本单位,系统会给每个进程分配4G的虚拟内存,分为0--3G的用户空间和3--4G的内核空间

        多个进程共享内核空间,用户空间相互独立

3> 进程是一个动态的过程,有生命周期的概念,分为创建态、就绪态、运行态、阻塞态、死亡态

4> 进程在内核空间中存储在一个名为task_struct的结构体中(PCB)

5> 单核cpu处理多任务是,一般使用的是时间片轮询机制

6> 进程与程序的区别:程序是静态的,是存储在磁盘上的二进制代码。进程是动态的,是有生命周期的

7> 进程的组成:进程控制块(PCB)、数据段、程序段

1.3 进程的种类

进程一共分为三大类:交互进程、批处理进程、守护进程

1> 交互进程:他是由shell控制,用于直接跟用户进行交互的进程。例如:vim编辑器、文本编辑器

2> 批处理进程:本质上维护了一个队列,被放入队列中的进程会统一被调度。例如gcc编译器的一步到位的编译

3> 守护进程:脱离了终端而存在的进程,随着系统的启动而开始,随着系统的结束而终止。例如:服务进程

1.4 进程号的概念

每个进程在系统中都有一个唯一的标识位,用一个整数表示,这就是该进程的进程号(PID)

1> PID(process ID):当前进程的进程号

2> PPID(parent process ID):当前进程的父进程的进程号

1.5 特殊的进程

1> 0号进程:也成为 idel 进程,他是操作系统启动后执行的第一个进程,这个进程也叫空闲进程,当没有其他进程执行时,系统会默认执行该进程。1号进程和2号进程都是由0号进程创建出来的。

2> 1号进程:也称 init 进程,该进程由0号进程产生,主要完成系统创建时一些软件硬件的初始化工作。当其他进程的父进程死亡后,会托管其子进程

3> 2号进程:也称 kthreadd,该进程由0号进程产生,也成为调度进程,当某个就绪进程时间片轮到时,该进程负责进程的调度工作

4> 孤儿进程:当前进程的父进程死亡后,但是当前进程还没有结束,那么当前进程称为孤儿进程,孤儿进程会由1号进程收养

5> 僵尸进程:当前进程已经死亡,但是其父进程没有为其收尸,那么该进程为僵尸进程

1.6 进程的相关指令

1> 查看进程信息的命令:ps ,跟不同的选项,执行不同的操作

        ps -ef:显示进程之间的关系

        ps -ajx:可以显示进程的状态

        ps -aux:可以查看进程资源使用情况

2> top或htop

可以动态展示进程的占用情况

3> pstree:展示进程树,可以显示进程的父子关系

4> 查看给定进程的进程号:pidof 进程名

4> kill : 向进程发送信号,发信号的格式

        kill -信号名(号) 进程号

        能够发送的信号号有:可以通过指令 kill -l查看

1.7 进程的状态

1> 主要状态一共有五个:创建态、就绪态、运行态、阻塞态、死亡态

2> 程序中的进程的状态显示:可以通过指令 man ps查看进程的状态

        进程的状态由两部分组成:主状态和附加态

主状态:

         D 不可中断的休眠态 (usually IO)

        R 运行态 (on run queue)

        S 可中断的休眠态 (waiting for an event to complete)

        T 暂停态,会给出作业号进行控制 t 程序调试时的暂停态 W 已经弃用 X 死亡态 (should never be seen)

        Z 僵尸态

附加态:

        < 高优先级的进程 (not nice to other users)

        N 低优先级的进程 (nice to other users)

        L 锁到内存中的进程 (for real-time and custom IO)

        s 会话组组长,默认为当前终端

        l 包含多线程的进程 (using CLONE_THREAD, like NPTL pthreads do)

        + 表示是前台运行的进程

二、多进程编程

2.1 进程的创建函数 fork

        pid_t fork(void);

        功能:创建一个子进程,并返回相关进程号

        参数:无

        返回值:对于父进程而言,得到的是子进程的pid号,对于子进程而言,得到的是0

        注意:当fork执行后,子进程会完全拷贝父进程的所有资源,并且父子进程的用户空间完全独立 父子进程执行是没有先后顺序的,遵循时间片轮询机制

2.2 进程号的获取(getpid() / getppid())

        pid_t getpid(void); //获取当前进程的pid号,返回值就是当前进程的id号,不会失败

        pid_t getppid(void); //获取当前进程的父进程的pid号,返回值就是父进程id,不会失败

2.3 进程的退出(exit/ _exit)

        void exit(int status);

        功能:是一个库函数,会导致调用该函数的进程结束

        参数:退出时的状态,会将该值和0377进行位与运算后的值返回给其父进程                 EXIT_SUCCESS(0):表示正常退出

                EXIT_FAILURE(1):表示异常退出

        返回值:无

        注意:对于标准库提供的该函数而言,退出进程之前,会自动将所有的已经打开的标准IO缓冲区刷新,并关闭文件指针后结束进程

        void _exit(int status);

        功能:直接退出当前进程(不会刷新标准IO的缓冲区)

        参数:退出时的状态,会将该值和0377进行位与运算后的值返回给其父进程                 EXIT_SUCCESS(0):表示正常退出

                EXIT_FAILURE(1):表示异常退出

        返回值:无

2.4 进程资源的回收

        pid_t wait(int *wstatus);

        功能:阻塞等待当前进程的任意一个子进程的退出,并回收该子进程的资源给系统

        参数:用于接受子进程退出时的状态,一般不接受,填NULL即可

        返回值:成功回收返回值回收的那个子进程的pid号,失败返回pid_t(-1)并置位错误码

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

        功能:以阻塞或者非阻塞的方式回收子进程资源

        参数1:要回收的进程id号

                >0:指定要回收的子进程pid号

                =0:回收父进程所在的进程组中的任意一个子进程

                -1:回收任意一个子进程

                <-1:表示去回收给定的pid的绝对值进程号所在的进程组中的任意一个子进程

        参数2:用于接受子进程退出时的状态,一般不接受,填NULL即可

        参数3:是否阻塞的选项

                0:表示阻塞回收

                WNOHANG:表示非阻塞回收

        返回值:

                >0:表示返回的是成功回收的子进程的pid号

                =0:表示以非阻塞形式回收资源时,但是没有回收到子进程时返回0

                -1:函数调用出错,并置位错误码


作业:

1> 使用多进程完成两个文件的拷贝:父进程拷贝前一半内容,子进程拷贝后一半内容

#include <25072head.h>
int main(int argc, const char *argv[])
{
    pid_t pid = -1;        //定义变量记录进程号
    int fd=-1;
    if((fd = open(argv[1], O_RDONLY)) == -1)
    {
        perror("open errpr:");
        return -1;
    }
    int fd1=-1;
    if((fd1 = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
    {
        perror("open error");
        return -1;
    }
    int len=lseek(fd,0,SEEK_END);
    lseek(fd,0,SEEK_SET);
    char buf;
    pid=fork();
    if(pid>0)
    {
        for(int i=0;i<len/2;i++)
        {
            read(fd,&buf,1);
            write(fd1,&buf,1);
        }
        wait(NULL);
        exit(EXIT_SUCCESS);
    }
    else if(pid==0)
    {
        for(int i=len/2;i<len;i++)
        {
            read(fd,&buf,1);
            write(fd1,&buf,1);
        }
        exit(EXIT_SUCCESS);
    }
    return 0;
}

2> 父进程创建两个子进程,子进程1负责拷贝文件前一半内容,子进程2负责拷贝文件后一半内容,父进程阻塞回收两个子进程资源

#include <25072head.h>
int main(int argc, const char *argv[])
{
    pid_t pid = -1;        //定义变量记录进程号
    pid_t pid1=-1;
    int fd=-1;
    if((fd = open(argv[1], O_RDONLY)) == -1)
    {
        perror("open errpr:");
        return -1;
    }
    int fd1=-1;
    if((fd1 = open(argv[2], O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
    {
        perror("open error");
        return -1;
    }
    int len=lseek(fd,0,SEEK_END);
    lseek(fd,0,SEEK_SET);
    char buf;
    pid=fork();
    if(pid==0)
    {
        for(int i=0;i<len/2;i++)
        {
            read(fd,&buf,1);
            write(fd1,&buf,1);
        }
        exit(EXIT_SUCCESS);
    }
    pid1=fork();
    if(pid1==0)
    {
        lseek(fd,len/2,SEEK_SET);
        for(int i=len/2;i<len;i++)
        {
            read(fd,&buf,1);
            write(fd1,&buf,1);
        }
        exit(EXIT_SUCCESS);
    }
    if(pid>0)
    {
        sleep(5);
        waitpid(pid, NULL, 0);  // 等待第一个子进程
        sleep(5);
        waitpid(pid1, NULL, 0);  // 等待第二个子进程
        sleep(5);
        exit(EXIT_SUCCESS);
    }

    return 0;
}

3> 详细绘制思维导图

4> 牛客网刷题