系统编程day03-进程

发布于:2025-09-04 ⋅ 阅读:(24) ⋅ 点赞:(0)

1.进程

1.1程序和进程的区别

程序:代码或者指令的有序堆叠,是静态的,存放在磁盘空间。

进程:程序的运行实例,进程是动态的

1.2单道程序和多道程序

单道程序:所有进程一个一个排队执行。若 A 阻塞,B 只能等待,即使 CPU 处于空闲状态。而在人机交互时阻塞的出现是必然的。所有这种模型在系统资源利用上及其不合理,在计算机发展历史上存在不久,大部分便被淘汰了。(例如一开始的dos系统)

多道程序:在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之下,相互穿插的运行

1.3并发和并行

并行(一定是多核的):多道程序同时在多个处理器上运行。

并发:宏观上的并行,并不是真正的并行,而是基于多道程序设计的一种根据时间片轮换cpu使用权的一种特殊机制。

1.4进程控制块

PCB:process-control-block

进程运行时,内核为进程每个进程分配一个 PCB(进程控制块),维护进程相关的信息,Linux 内核的进程控制块是 task_struct 结构体。

查看这个结构体:

PCB:

2.进程的状态

2.1进程的三种状态

就绪状态:准备完毕,等待执行

运行状态:获得CPU资源,正在使用

等待状态:当有事情发生,条件不满足,需要重新准备

2.2进程状态的查看

ps:ps可以查看进程信息

可以结合命令参数:

-a 显示终端上的所有进程,包括其他用户的进程
-u 显示进程的详细状态
-x 显示没有控制终端的进程
-w 显示加宽,以便显示更多的信息
-r 只显示正在运行的进程
-ajx 更深层次的进程信息

ps -aux 相当于Linux中的任务管理器

stat的参数意义如下:

3.进程号

3.1什么是进程号

每个进程都由一个进程号(pid)来标识,其类型为 pid_t(整型),进程号的范围:0~32767。进程号总是唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再次使用。

两个特殊进程:

  • 0进程:

    • 早期叫做:交换进程

    • 现在叫做:空闲进程(当CPU空闲的时候,会执行这个进程)

  • 1进程:init进程,是所有用户进程的祖先(所有用户进程都是有1号进程直接或者间接创建)。(1号进程由0号进程创建)

pid:进程的编号

ppid:父进程,每一个进程都是由父进程创建而来的。ppid就是父进程的编号。

pgid:进程组,一个或者多个进程的集合,这些进程的集合称为进程组,进程组有一个组号,叫做pgid。这些进程相互关联,可以接收终端传来的各种信号。

3.2获取进程的函数

getpid

获取当前进程的进程号

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);

返回值:获取的当前进程的进程号,pid_t

参数:无参数

getppid

#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);

返回值:获取的当前进程的父进程号,pid_t

参数:无参数

getpgid

#include <sys/types.h>
#include <unistd.h>
pid_t getpgid(pid_t pid);

返回值:获取的pid进程的进程组号,pid_t

参数:需要传入要找的pid号

4.进程的创建

4.1进程的创建函数

进程的创建由fork函数来完成。

fork函数:

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

pid_t fork(void);

函数功能:创建一个新的进程。这个被创建的新的进程叫做子进程,是调用fork函数这个进程(父进程)的子进程。

返回值:

如果创建成功:

  • 在父进程中返回的是子进程的id

  • 在子进程中返回的是0

如果创建失败:

  • 返回-1

4.2父子进程的关系

子进程是父进程的一个复制品(从fork的下一句开始复制),两个进程都有各自单独的空间。

这里说的fork的下一句并不是if(pid == -1);而是fork函数创建子进程完后,进行的赋值操作。

最终的终端打印结果如上,一个是在父进程中进行,一个是在子进程中进行。

子进程从父进程那里继承了整个进程的地址空间。

地址空间:包括进程上下文、进程堆栈、打开的文件描述符等。

子进程所独有的只有它的进程号,计时器等。因此,使用 fork 函数的代价是很大的。

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(int argc, char const *argv[])
{
    pid_t pid = fork();
 
    if (pid == -1)
    {
        perror("fork");
        return 0;
    }
 
    else if (pid > 0)
    {
        
        printf("hello,welcome to parent process\n");
      
        printf("当前进程id%d,他的子进程id%d\n", getpid(), pid);
    }
 
    else if (pid == 0)
    {
        
        printf("hello,welcome to child process\n");
        
        printf("子进程pid:%d,他的父进程id:%d\n", getpid(), getppid());
    }
 
    return 0;
}
 

拓展1:sleep函数

#include <unistd.h>
unsigned int sleep(unsigned int seconds);

函数功能:将进程挂起(休眠)一段时间。

返回值:

  • 如果sleep函数里面的seconds走完了,那么返回0

  • 如果没有休眠完,被强制打断,那么返回剩余待休眠的时间

参数:挂起或者休眠的时间

拓展2:全缓冲与行缓冲

全缓冲:缓冲区满了以后强制进行刷新,即write函数可以将数据输出到终端

行缓冲:遇到\r\n,进行刷新,此时write函数可以将数据输出到终端

拓展3:exit函数 与 _exit函数

exit函数:

功能:库函数,将进程相关数据 清理后退出

除了调用_exit函数还会调用一个清理函数

所以使用exit函数的运行效率比较低

#include <stdlib.h>
void exit(int status);

_exit函数:

功能:系统调用函数,将进程直接退出,不进行清理

void _exit(int status);

5.进程资源回收

在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块 PCB 的信息(包括进程号、退出状态、运行时间等)。 父进程可以通过调用 wait 或waitpid 得到它的退出状态同时彻底清除掉这个进程。

5.1wait函数

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status)

函数功能:等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。

参数:

status : 进程退出时的状态信息,是一个指针

返回值:

  • 成功:返回运行结束的进程的进程号

  • 失败:-1

代码案例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(int argc, char const *argv[])
{
    pid_t pid = fork();
 
    if (pid == -1)
    {
        perror("fork");
        return 0;
    }
 
    else if (pid == 0)
    {
        for (int i = 1; i <= 5; i++)
        {
            printf("我还能再玩%ds\n", 5 - i);
            sleep(1);
        }
        _exit(0);
    }
 
    else if (pid > 0)
    {
        int status = 0;
        printf("你5s以后必须停止玩游戏\n");
        sleep(5);
        wait(&status); //阻塞,等待子进程退出
 
        if (WIFEXITED(status) != 0)
        {
            printf("status:%d\n", WEXITSTATUS(status));
            if (WEXITSTATUS(status)==0)
            {
                printf("好的,你说的对,听你的\n");
            }
            else if (WEXITSTATUS(status)==1)
            {
                printf("好的,你说的不对,但是我还是听你的\n");
            }
            
            
        }
    }
 
    return 0;
}

查看WEXITSTATUS函数的定义可以看出来,她是将status中除了8-15位的其他位数全部置零,并且向右偏移了8位。可以取到status具体的值。

5.2waitpid函数

waitpid() 是 Unix/Linux 系统中用于进程同步的核心系统调用,专门用于父进程等待特定子进程的状态变化。相比通用的 waitpid(),它提供了更精细的控制能力

函数原型:

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

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

参数解释:

1.pid :目标子进程标识

  • >0:等待指定PID的子进程
  • -1:等待任意子进程(等效于wait())
  • 0:等待同进程组的任意子进程
  • <-1:等待进程组ID等于|pid|的任意子进程

2.status:状态信息指针:

  • 存储子进程退出状态(使用宏解析)
  • 可设为NULL(不关心退出详情)

3.option:控制选项(位掩码)

  • 0:阻塞等待(默认行为)
  • WNOHANG:非阻塞模式(立即返回)
  • WUNTRACED:报告已经停止的子进程
  • WCONTINUED:报告已经继续执行的被停止子进程

返回值:

  • 成功:返回状态变化的子进程PID
  • 无状态变化:返回0
  • 错误:返回-1

    网站公告

    今日签到

    点亮在社区的每一天
    去签到