linux-进程(2)

1.通过系统调用获取进程标示符

进程id(PID)

父进程id(PPID)

每一个可执行程序运行起来之后都会成为一个进程,每个进程都有一个自己的id,以及一个父进程id,父进程就是创建自己进程的进程,每个进程都是一个执行起来的程序,所以肯定在这个程序中创建另一个程序,就是自己的子进程。

使用getpid这个函数就可以查看到自己这个进程的id,使用getppid这个函数就可以查看到父进程的id,getpid是一个系统调用函数,需要注意的是一个子进程只有对应的一个父进程,但是一个父进程可以有多个子进程。

进程每一次被启动时对应的pid都不一样,但是父进程的pid永远不会变。

#include<iostream>
#include<unistd.h>
#include<sys/types.h>
using namespace std;int main()
{pid_t id=getpid();while(1){cout<<id<<endl;sleep(1);}return 0;
}

int main()
{pid_t id=getpid();pid_t pid=getppid();cout<<"Im child: "<<id<<endl;cout<<"Im father: "<<pid<<endl;return 0;
}

\


2.通过系统调用创建进程-fork初识

这个是fork函数的介绍,需要包含两个头文件,fork函数的返回值是pid_t,并且fork函数有两个返回值。

我们来做一个小测试,看看返回值到底是怎么回事。

#include <unistd.h>
#include <sys/types.h>
#include <iostream>
using namespace std;int main()
{cout << "before fork:Im a process, pid:" << getpid() << ", ppid:" << getppid()<<endl;fork();cout << "after fork:Im a process, pid:" << getpid() << ", ppid:" << getppid()<<endl;sleep(2);return 0;
}

可以看到fork之后的代码被执行了两次,也就是从一个进程变成了两个进程,第三个是子进程,它的ppid就是父进程的pid。

那么我们怎么知道哪个是子进程,哪个是父进程呢?可以通过fork的返回值来判断,如果fork成功则对子进程返回0,对父进程返回子进程的id。此时就可以使用if来分流,让父子进程做不一样的事情。

我们都知道进程=内核数据结构+可执行代码和数据,那么子进程的代码和数据是什么呢?它是怎么运行起来的,实际上父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)。但是进程是具有独立性的,互相之间是不影响的。


3.阻塞状态

大家还需要知道的是,一个cpu只有一个运行队列,那么有些进程在等待硬件资源的时候,就会被os从运行队列中拿出来放到对应的硬件的队列中,并且改成阻塞状态,因为cpu也属于硬件,硬件就有自己的结构体,里面就有对应的队列。

那么当我们用户从硬件中输入数据的时候,这个进程就会被从硬件的队列中拿出来链入到cpu的运行队列,并且将进程状态改为运行状态。

4.阻塞挂起状态

挂起状态通常伴随着阻塞,这个状态的前提是计算机的资源比较吃紧,这个时候os就会将这个进程的代码和数据写入到外设当中,当资源足够时再拿过来,写入的这个过程就是在腾空间。这个写入的本质就是用时间换空间,宁愿让进程慢点,也不要让os挂掉。


5.进程状态

linux内核源代码是这样描述进程的各种状态的。

/*

* The task state array is a strange "bitmap" of * reasons to sleep. Thus "running" is zero, and * you can test for combinations of others with * simple bit tests.

*/

static const char * const task_state_array[] = { "R (running)", /* 0 */

"S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ "t (tracing stop)", /* 8 */

"X (dead)", /* 16 */ "Z (zombie)", /* 32 */ };

R运行状态(running) : 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。

D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。

T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态

进程大多数情况下是大量的在运行,那么os怎么管理这些进程呢?就需要先描述再组织,描述就是数据结构+可执行代码和数据,那么组织就需要使用队列来对进程进行排队,因为大部分进程并不是一直在运行,有些进程也可能在等待资源,比如等待我们从键盘输入,所以进程有许多种状态。
 

那么进程的状态决定了上面呢?决定了后序的动作,那么动作的先后就需要进行排队,每一个cpu都有一个自己的运行队列,那么只有进程被放入了这个运行队列,就都是运行状态,就算在排队也是运行状态。

5.1睡眠状态

睡眠状态也就是进程在等待事件的完成,那么为什么执行了一下的代码,在查询该进程信息的时候,会是睡眠状态呢?这个进程不是一直在运行吗?当我们去掉sleep的时候,这个进程还是s状态,其实是因为cout这个函数的本质是向显示器进行打印,显示器是在我们面前,可是这个可执行程序是在远端的云服务器上运行的,cpu比外设要快很多,所以注定了cout的大部分时间是在等待着执行。

#include<iostream>
#include<unistd.h>
#include<sys/types.h>
using namespace std;int main()
{while(1){cout<<"Im a process, pid: "<<getpid()<<endl;sleep(1);}return 0;
}

当我们去掉cout,死循环里面什么都不做,就能查到是运行状态。 

 进程的睡眠状态其实就是os的阻塞状态,这里的睡眠有时候也叫做可中断睡眠,因为可以用ctrl+c来终止。  

 5.2停止状态

当我们使用kill -19 这条命令就可以使一个进程停止,也就是进入停止状态。

 如果我们还想让这个进程跑起来,可以使用kill -18这个命令。

5.3僵尸状态

僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程

僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

我们创建进程就是为了给我们完成某件事,那么这个进程退出时就需要给我们返回结果,一个进程在退出时可以释放掉代码和数据,因为都没有用了,但是需要先保存一下pcb,因为需要被os或者其他进程获取到该进程的退出信息,我们把一个进程已经退出但并没有被获取退出信息的状态称为僵尸状态。

下面这段代码就是前5秒父子进程同时运行。后面5秒父进程单独运行,在第5秒时子进程会被强制退出,那么就进入了僵尸状态Z。 为什么要有Z状态呢?我们创建进程就是为了将这个进程完成某个工作, 

int main()
{pid_t id = fork();if (id == 0){int cnt = 5;while (cnt){cout << "Im child, pid: " << getpid() << ", ppid: " << getppid() << endl;sleep(1);cnt--;}exit(0);}int cnt = 10;while (cnt){cnt--;cout << "Im father, pid: " << getpid() << ", ppid: " << getppid() << endl;sleep(1);}wait(NULL);cout<<"father wait child success....."<<endl;return 0;
}

  僵尸进程危害

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!

维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说, Z状态一直不退出, PCB一直都要维护?是的!

那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!

内存泄漏?是的!

 5.4孤儿状态

父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?

父进程先退出,子进程就称之为“孤儿进程”

孤儿进程被1号init进程领养,当然要有init进程回收喽。

当父进程退出之后,子进程还一直在运行,这时子进程就会变成孤儿进程, 它的ppid也变成了1,也就是被1号进程领养了,被回收了,从s+变成了s,也就是变成了后台进程,此时只能使用kill -9这个命令来杀掉。

int main()
{pid_t id = fork();if (id == 0){int cnt = 100;while (cnt){cout << "Im child, pid: " << getpid() << ", ppid: " << getppid() << endl;sleep(1);cnt--;}exit(0);}int cnt = 10;while (cnt){cnt--;cout << "Im father, pid: " << getpid() << ", ppid: " << getppid() << endl;sleep(1);}// wait(NULL);// cout<<"father wait child success....."<<endl;return 0;
}

 


6.进程优先级 

6.1基本概念

cpu资源分配的先后顺序,就是指进程的优先权(priority)。

优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能.。

 在一台计算机中,资源绝对是占多数,硬件绝对是占少数,所以要合理的安排进程,就需要给它们设置优先级。 

6.2查看系统进程

在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容:

UID : 代表执行者的身份

PID : 代表这个进程的代号

PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号

PRI :代表这个进程可被执行的优先级,其值越小越早被执行

NI :代表这个进程的nice值

6.3 PRI and NI

PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高,PRI的范围是60-99,进程的默认PRI是80

那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值

PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为: PRI(new)=PRI(old)+nice

这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值

nice其取值范围是-20至19,一共40个级别

6.4 PRI vs NI

需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。

可以理解nice值是进程优先级的修正修正数据

6.5查看进程优先级的命令

6.5.1用top命令更改已存在进程的nice:

top

进入top后按“r”–>输 入进程PID–>输入nice值

7.其他概念

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级

独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰

并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行

并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发


今天的分享到这里就结束啦,感谢大家的阅读!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SCI一区级 | Matlab实现BES-CNN-GRU-Mutilhead-Attention多变量时间序列预测

SCI一区级 | Matlab实现BES-CNN-GRU-Mutilhead-Attention秃鹰算法优化卷积门控循环单元融合多头注意力机制多变量时间序列预测 目录 SCI一区级 | Matlab实现BES-CNN-GRU-Mutilhead-Attention秃鹰算法优化卷积门控循环单元融合多头注意力机制多变量时间序列预测预测效果基本介绍…

二叉树-从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]前序遍历的…

Group Query Attention (GQA) 机制详解以及手动实现计算

Group Query Attention (GQA) 机制详解 1. GQA的定义 Grouped-Query Attention (GQA) 是对 Multi-Head Attention (MHA) 和 Multi-Query Attention (MQA) 的扩展。通过提供计算效率和模型表达能力之间的灵活权衡&#xff0c;实现了查询头的分组。GQA将查询头分成了G个组&#…

分享一个2099试用码!JetBrains 2024 版

程序员痛点&#xff1a; 好用的编程工具收费太贵 无法找到好且免费的编程资料&#xff08;书或者视频&#xff09; 今天我们话几分钟分享一个激活方法&#xff0c;一次学习&#xff0c;终身受益 一分钟激活全家桶旗下所有软件 支持更新 Stage 1.下载安装 toolbox-app&…

【论文阅读】《Octopus v2: On-device language model for super agent》,端侧大模型的应用案例

今年LLM的发展趋势之一&#xff0c;就是端侧LLM快速发展&#xff0c;超级APP入口之争异常激烈。不过&#xff0c;端侧LLM如何应用&#xff0c;不知道细节就很难理解。正好&#xff0c;《Octopus v2: On-device language model for super agent》这篇文章可以解惑。 对比部署在…

【小浩算法cpp题解】判断环形链表

目录 前言我的思路思路一 &#xff08;哈希表记录链表的访问&#xff09;&#xff1a;思路二 &#xff08;双指针&#xff0c;快指针在前&#xff0c;慢指针在后&#xff09;&#xff1a; 我的代码运行结果 前言 前几天我写的代码&#xff0c;都是把所有的内容写在main函数里&…

GPB | RegVar:基于深度神经网络的非编码区突变功能预测新方法

Genomics, Proteomics & Bioinformatics &#xff08;GPB&#xff09;发表了由军事医学研究院辐射医学研究所张成岗研究员、周钢桥研究员和卢一鸣副研究员团队完成的题为“RegVar: Tissue-specific Prioritization of Noncoding Regulatory Variants”的方法文章。我们的“…

Spring事务回滚核心源码解读

记一次Springboot事务超时不回滚的分析过程 在Springboot中&#xff0c;我用的xml进行事务管理&#xff0c;DataSourceTransactionManager作为事务管理器&#xff0c;配置了事务控制在Service层&#xff1b;在事务管理器中&#xff0c;配置了defaultTimeout事务超时时间为5秒&…

IEC 62680新规上线,慧能泰设备端PD协议芯片带你勇闯欧盟

2022年12月7日&#xff0c;欧盟发布了Directive (EU) 2022/2380&#xff0c;强化了通用充电器的规定&#xff0c;并计划于2024年12月28日起&#xff0c;在所有欧盟国家强制执行该指令。此修订主要针对新生产的手机、平板、相机、耳机、游戏机、音箱、电子书、键盘、鼠标、导航仪…

使用knuth Durstenfeld Shuffle置乱关键区域

示意图 使用knuth Durstenfeld Shuffle置乱关键区域。比较典型的就是当我们对图像进行小波变换后&#xff0c;能量主要集中在左上角。 将图像进行排列&#xff0c;将关键区域置于整个序列的末尾&#xff0c;从末尾开始逐个交换&#xff0c;完成置换。 为了演示方便&#xff0c…

第 3 篇 : Netty离线消息处理(可跳过)

说明 仅是个人的不成熟想法, 未深入研究验证 1. 修改 NettyServerHandler类 package com.hahashou.netty.server.config;import com.alibaba.fastjson.JSON; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHan…

CRM客户管理系统盘点2024:16款顶级系统PK赛,寻找最佳利器

客户关系管理系统&#xff08;CRM&#xff09;在企业数字化转型的过程中扮演着至关重要的角色。选择一个高效、功能丰富的CRM客户管理系统&#xff0c;对于确保企业未来健康、稳定的发展至关重要。当前市场上存在着众多的CRM客户管理系统件&#xff0c;每个软件都有其独特的功能…