Day03 linux高级系统编程--进程

概念

进程与程序的区别

进程:一个正在运行的代码就叫做进程,是动态的,会占用内存

程序:一段封装好的待运行的代码或可执行文件,是静态的,会占用磁盘空间

单道与多道程序

单道:程序一个一个排好队,一个一个执行,若代码A阻塞,则代码B不能立即执行,需等待代码A执行结束代码B才会执行

多道:程序之间相互独立,各个程序之间同时执行,各执行各的,互不影响,它们在系统管理程序控制下,相互穿插执行(并行,并发)

并行与并发的区别

并行:有多个核,一个程序对应一个核,同时执行程序

并发:有一个核,多个程序对应一个核,程序之间按顺序相互交替执行,在宏观上也是并行。

进程控制块(PCB)了解

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

task_struct控制块

        在 /usr/src/linux-headers-xxx/include/linux/sched.h 文件中可以查看
        task_struct 结构体定义
        其内部成员有很多,我们掌握以下部分即可:
        进程id:c 语言使用 pid_t 的类型表示 , 其实就是一个非负整数
        进程的状态: 有就绪、运行、挂起、停止等状态。
        ......
PCB存储位置

进程号

概念:

每一个进程都是由一个进程号来标识,其类型是pid_t,进程号范围是0~32767。进程号是唯一的,但是进程号是可以重复使用的(前提是当前程序1抢得cpu处理权后,执行1的代码,当1代码执行结束后,会释放该进程号,这时其他进程就可以使用该进程号)。

PID 进程号

PPID 父进程号

PGID 进程组号

获取进程id

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

pid_t getpid(void);

功能:获取当前进程号

参数:无

返回值:本进程号 (失败-1)

获取进程的父进程id

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

pid_t getppid(void);

功能:获取调用此函数进程的父进程id(ppid)

参数:无

返回值:调用此函数进程的父进程id(ppid)

获取组进程号

#include <unistd.h>

#include <sys/types.h>

pid_t getpgid(pid_t pid);

功能:获取当前组进程号,也就是同组进程中第一个进程的进程号

参数:pid 进程号

返回值:当前组进程号,也就是同组进程中第一个进程的进程号

创建进程fork

概念:

父子进程

系统允许一个进程创建新进程。此新进程就是子进程,这就是创建的父子进程。

int x = fork();

函数:

#include <unistd.h>

#include <sys/types.h>

pid_t fork(void);

功能:用于进程创建一个新进程,该新进程就是该进程的子进程,原进程被称为父进程。

参数:无
返回值:

        成功:子进程返回0,父进程返回子进程id。getpid pid_t为整型。

        失败:返回-1

注意:

fork创建失败的两个主要原因:

        1.当前进程数已到达了系统规定的最大上限数,这时的errno的值被设为EAGAIN。

        2.系统内存不足,这时errno的值被设为ENOM。

注意:

        子进程会在fork函数后开始执行

示例一:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
    printf("啦啦啦\n");
    int id = fork();
    if(id < 0)
    {
        printf("子进程创建失败id:%d\n",id);
    }
    else if(id > 0)
    {
        printf("父进程的进程号是:%d\n,子进程的进程号是:%d\n",getpid(),id);
    }
    else if(id == 0)
    {
        printf("父进程的进程号是:%d\n,创建的子进程的进程号是:%d\n,创建的id:%d\n",getppid(),getpid(),id);
    }
    printf("德玛西亚\n");
    while(1);
    return 0;
}

父子进程关系

使用 fork 函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的
地址空间。
地址空间 : 包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先
级、进程组号等。
子进程所独有的只有它的进程号,计时器等。
因此,使用 fork 函数的代价是很大的。
示例2:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
    
    printf("啦啦啦\n");
    int id = fork();
    printf("德玛西亚\n");
    while(1);
    return 0;
}

示例2:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
    printf("啦啦啦");//没有\n所以啦啦啦在缓冲区
    int x = fork();
    printf("德玛西亚\n");
    while(1);
    return 0;
}

啦啦啦在缓冲区,所以父进程打印不出来“啦啦啦”,当子进程执行时\n才会将父进程中的“啦啦啦”冲刷出来,和“德玛西亚“拼接起来一起打印,打印两遍。

分析:

printf() 在打印内存时会将打印的数据放在缓冲区
子进程将父进程的缓冲区也复制了一份
代码 1 printf() 输出时带有 \n, 触发了行刷新 ( 刷新状态 : 行刷新 , 关闭刷新 , 满刷新 ,
制刷新 ), 已经将缓冲区中的内容刷新出并打印 , 此时缓冲区中没有内容 , 子进程拷贝到的
缓冲区数据是空 .
所以父进程打印
        啦啦啦
        德玛西亚
子进程打印
        德玛西亚
代码 2 printf() 输出时没带有 \n, 无法触发了刷新 ( 刷新状态 : 行刷新 , 关闭刷新 , 满刷
, 强制刷新 ), 此时缓冲区中有内容 , 啦啦啦 , 子进程拷贝到的缓冲区数据也有啦啦啦 .
所以父进程打印
        啦啦啦德玛西亚
子进程打印
        啦啦啦, 德玛西亚
注意 : 库函数有缓存区 , 系统调用没有缓存区
思考题 : 请问输出的结果是 ?
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
//printf是库函数有缓冲区 hello world先放入缓冲区 被子进程复制了一份
    printf("hello world1");
//write是系统调用 直接将字符串 写入1号文件 没有缓冲区 不被子进程复制
//0,输入
//1,输出
//2,错误输出
    write(1,"hello world2",12);
//创建子进程
    pid_t pid = fork();
    return 0;
}

进程状态

分类:

可以分为三大状态与五大状态
三大状态
运行态,就绪态,阻塞态
五大状态
新建态、终止态,运行态,就绪态,阻塞态
ps 查看进程状态
ps 命令
作用 : 查看
参数 :
-a 显示终端上的所有进程,包括其他用户的进程
-u 显示进程的详细状态
-x 显示没有控制终端的进程
-w 显示加宽,以便显示更多的信息
-r 只显示正在运行的进程
ps -aux: 显示当前用户正在运行的进程信息
ps -ajx: 显示正在运行的相关联进程信息 ( 包含父进程 id(ppid), id(pdid))
显示信息中 STAT 参数含义
D 不可中断 Uninterruptible usually IO
R 正在运行,或在队列中的进程
S( 大写 ) 处于休眠状态
T 停止或被追踪
Z 僵尸进程
W 进入内存交换(从内核 2.6 开始无效)
X 死掉的进程
< 高优先级
N 低优先级
s 包含子进程
+ 位于前台的进程组

进程资源回收

概述:

在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存
等。但是仍然为其保留一定的信息 , 这些信息主要主要指进程控制块 PCB 的信息(包括
进程号、退出状态、运行时间等 );
回收原则 : 谁创建谁回收 ( 父进程回收子进程资源 );
wait 函数
作用 : 等待子进程运行结束
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);// 阻塞
功能:
等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资
源。
参数:
status: 进程退出时的状态信息。
返回值:
成功:已经结束子进程的进程号
失败: -1
注意 :
1, 会阻塞当前进程 , 直到回收一个子进程
2, 因为回收原则 , 谁创建谁回收 , 所以该函数在父进程中调用
示例 1
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{
    int x = fork();
    if(x < 0)
    {
        printf("创建失败\n");
    }
    else if(x == 0)
    {
        for(int i = 0;i < 10; i++)
        {
            printf("子进程正在进行第%d次执行",getpid(),i);
            sleep(1);
        }
    }
    else if(x > 0)
    {

        printf("父进程正在等待子进程结束\n");
        wait(NULL);
        printf("父进程已回收子进程%u\n",x);
    }
    return 0;
}

exit 函数 : 库函数
所需头文件
#include <stdlib.h>
函数 :
void exit(int status);
参数 :
退出状态 ,0, 正常退出 , 0 异常退出
_exit 函数 : 系统调用
所需头文件
#include <unistd.h>
函数 :
void _exit(int status);
参数 :
退出状态 ,0, 正常退出 , 0 异常退出
exit _exit 的区别
exit: 底层调用系统调用函数的 _exit 函数 , 所以相对与 _exit 而言效率低
_exit:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZHANGα

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值