Linux学习笔记之进程

进程

进程的定义

  进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,也是操作系统结构的基础。
  例如当QQ程序运行的时候,计算机会先从磁盘读取QQ程序到内存,然后OS管理这个程序,这就是进程。简单来说进程就是OS处理进程的数据结构加代码与数据。一般在Linux中这个数据结构就是task_struct结构体,本质就是单链表。如下代码就是一个进程结构体,里面有进程的状态,ID,分配的内存等。

struct task_struct {// 进程状态,例如:TASK_RUNNING, TASK_INTERRUPTIBLE 等volatile long state; // 进程的进程号(PID)pid_t pid; // 指向下个进程的 task_struct 指针struct task_struct *next; // 进程的进程组号pid_t tgid; // 进程的优先级int prio; // 进程的虚拟内存描述符struct mm_struct *mm; // 进程的内核栈指针void *stack; // 进程的可执行文件信息struct files_struct *files; // 进程的文件系统信息struct fs_struct *fs; // 进程的信号处理相关信息struct signal_struct *signal; // 进程的命名空间信息struct nsproxy *nsproxy; // 进程的调度信息struct sched_entity se; // 进程的运行时间统计u64 utime, stime; // 进程的启动时间unsigned long long start_time; // 进程的命令行参数char comm[TASK_COMM_LEN]; 
};

进程的目的与意义

  一般在电脑,手机上,我们不止要运行一个程序,会同时运行qq,杀毒软件,浏览器等。如果我们不先描述进程的属性定义一个结构体,进程之间会产生干扰,那么OS就无法较好的管理内存。计算机就会是不是蓝屏,这对于用户来说十分的不友好。

进程采用单链表的原因

  在使用QQ的时候,我们会打开他,不用的时候可以关闭它,或者让他保持后台运行,因此对于进程而言,就有大量的增删操作,如果我们采用数组的方式就会造成数据处理慢,也就是使用起来变卡了。
  其次使用数组就必须提前分配一大段连续的地址空间,但可能OS不需要如此大的空间,就会造成资源浪费,其次在数组扩容的时候,计算机可能没有足够大的连续空间,就会造成内存不够。采用单链表的方式就更加的灵活,充分利用散落空间。

  进程就是运行的程序,在Linux中被描述为task_struct结构体,用单链表进行管理。可以通过XShell来访问服务器.ps ajx命令来查看进程,如下图
在这里插入图片描述

1.进程相关的函数

1.1getpid()

	man getpid

  在Linux中可以用上述命令查询该函数。
在这里插入图片描述

  在操作系统中有多个进程,为了区别他们,就引入了类似于身份证号的概念,每个进程都有自己唯一的pid(process Identification),当程序运行起来时就是一个进程,因此可以在程序中获取当前pid然后打印出来。

 #include<stdio.h>2 #include<unistd.h>3 #include<sys/types.h>4 5 int main()6 {7 8     pid_t id=getpid();9 10     printf("我的ID是%d\n",id);                                                                                  11 12 13     return 0;14 }
~

运行程序,便可以得到如下ID。

在这里插入图片描述

1.2getppid()

	man getppid

  在Linux中可以用上述命令查询该函数。如下图
在这里插入图片描述
  在Linux中-bash就是命令行程序,我们输入的ls,mkdir命令在被bash解析后都会转化为一个进程,此时bash就是父进程,ls就是子进程,在子进程内调用getppid就可以得到父进程的pid。
在这里插入图片描述
  当我们打开多个命令行时便会有多个bash,如下图。
在这里插入图片描述
  我们可以通过如下例子证明任何进程(bash除外)是由父进程创造的,
在这里插入图片描述

ps ajx | head -1 && ps ajx | grep bash

  上述命令可以分为两个命令,用&& 连接,ps ajx | head -1 ,表示显示ps ajx结果的第一行,也就是状态栏。
在这里插入图片描述
  ps ajx | grep bash表示在进程信息中查询有关键词bash的,此时会有两个符合条件,第二个时命令行bash进程自然可以被检索到,而第一个仔细看其实是grep进程。grep是我们在命令行输入的指令,在经过bash解析后生成一个进程。此时grep的父进程ID就是32095,恰好就是bash的ID。
在这里插入图片描述

1.3 fork

  既然进程是由父进程创造的,那我们平时写的test.c文件在编译运行后也是一个进程,就可以由这个进程在创造出子进程,Linux就提供了fork函数建立子进程。
在这里插入图片描述
  在man介绍中返回值最重要,如下图
在这里插入图片描述
  成功时,在父进程中会返回子进程的进程 ID(PID),而在子进程中会返回 0。失败时,在父进程中返回 -1,不会创建子进程,并且会适当地设置 errno。

  于是我们便可以通过下属例子证明子进程是由父进程建立的。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{pid_t id=fork();if(id == -1){printf("子进程创建失败\n");}if(id == 0){//子进程while(1){printf("我是子进程,PID为%d,PPID为%d\n",getpid(),getppid());sleep(1);}}else {//父进程while(1){printf("我是父进程,PID为%d,子进程ID为%d\n",getpid(),id);sleep(1);}}return 0;
}

在这里插入图片描述

1.4结束进程

  在Linux中可以使用ctrl+c结束进程,如下图。按下ctrl+c后进程就结束了
在这里插入图片描述
  也可以使用kill -9 +ID结束进程。其中-9表示结束信号,

 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

  如下图使用kill -9在新开的命令行中结束父进程
在这里插入图片描述

2.进程属性

  在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 */
};

  Linux要管理进程就必须遵循一个原则先描述再组织,即将进程各种属性封装再结构体内部,在通过队列组织起来,这样对于进程的管理就转化为对于数据结构的管理。在Linux中通常以固定的时间片来刷新进程,即Linux为了保证每个进程公平,当一个进程运行固定的时间后就切换到下个进程,依次循环往复。对应于队列就是头删尾插。

2.1 R(运行状态)

  运行状态实际上就是进程正在内存中的运行队列中,此时进程就在CPU中不断切换运行。

  1 #include<stdio.h>2 3 int main()4 {5     while(1)6     {                                                                                 9     }10     return 0;11 }

  让上述进程进入到死循环中,就会一直运行,再查看进程就可以看到R(运行状态)。此时proc进程就一直在内存中的运行队列中。
在这里插入图片描述

2.2 S(休眠状态)

  根据之前的冯诺依曼架构可以知道,CPU只与内存进行数据交换,由于磁盘等输出设备读取速度相对于内存过慢,CPU不会与输出设备例如显示器,磁盘,进行数据交换,而是要通过内存间接交换
在这里插入图片描述
  当进程要与外部设备输出时,这个消耗的时间对于CPU来说时巨大的,为了避免资源的浪费,进程在Linux中就是一个task_struct结构体,可以放在任何task_struct队列中,于是便将进程从内存的运行队列中取出换到设备的等待队列中等待数据交换完毕。。此时进程的属性就是休眠状态。如下例子

#include<stdio.h>2 3 int main()4 {5     while(1)6     {7         printf("我是一个进程\n");8                                                                                                  9     }10     return 0;11 }

  在上述的代码中仅仅加入一条打印语句,进程就处于S休眠状态,说明此时进程被移动到显示器的等待队列中了。但这个程序是个死循环,一定会有再次加载到CPU中的时候,也就是R状态,但是由于CPU运行速度十分快,刚好查询到R状态概率十分小

在这里插入图片描述

for i in $(seq 1 1000); do ps ajx | head -1 && ps ajx | grep ‘<proc>’ | grep -v grep; done

  运行上述脚本,在尝试数分钟后终于也是找到了那一瞬间为R的状态

在这里插入图片描述

2.3 D(深度休眠状态)

  这个状态十分少见,仅在系统濒临崩溃的边缘才可以看见。假如现在进程A里存储了1000W人口信息资料,此时要保存到磁盘中,那么给他状态设置为S,此时OS(操作系统)在管理进程,内存严重的不足了,OS的使命是保护系统正常运行,他看见A进程是S状态,又占用大量的内存,就把A进程强制结束了(就像手机打开应用闪退一样)。此时如果磁盘正常存储结束也就没什么问题,但是D磁盘内存也可能不足,此时磁盘向进程A返回错误信息,发现A进程没了,直接人傻了。十分重要的数据就发生了丢失,这是决定不允许的。
  于是便引入了D状态(磁盘休眠/深度休眠)。==系统可以崩溃,但是数据不可以丢失。==二者取其轻,选择相对可以接受的选择。

2.4 T(停止状态)

  在Linux中可以使用kill向进程发送信号。如下图
在这里插入图片描述
  其中19号信息就可以暂停进程。

在这里插入图片描述

在这里插入图片描述

kill -18 16394

可以再次启动进程

2.5 t(追踪停止状态)

  在Linux中经常会使用gdb进行调试,虽然不怎么好用哈。当我们在程序中打断点,并且r运行到断点时,此时程序就是t状态。

gcc -g -o test test.c
gdb test

在这里插入图片描述

在这里插入图片描述

2.6 X(死亡状态)

  死亡状态就是进程彻底结束,资源全部释放的时候,是一瞬间的状态,不能被检测到。

2.7 Z(僵尸状态)

  僵尸状态就是进程没有完全释放的状态,但是进程不在运行了。进程可以理解为是内核数据结构(task_struct)+代码和数据,当子进程完成任务时,就会先释放代码加数据,但保留task_struct供父进程判断检测子进程是否完成任务。死亡状态也就是将最后的tast_struct也释放完毕的瞬间状态。

  ls对于命令行解释器bash来说是其的子进程,当ls进程执行完时,返回的退出状态可以用==echo ¥?==显示出来,如下图。
在这里插入图片描述
  ls执行错误时也会返回对应的错误码,然后bash根据错误码打印对应信息。
在这里插入图片描述
  如果僵尸进程不被父进程管理回收那么他将永远是僵尸进程,如果父进程不管理僵尸进程的话,由此便有可能造成内存泄漏,也可以通过下面例子认识。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>int main()
{pid_t id=fork();if(id == -1){printf("子进程创建失败\n");}if(id == 0){//子进程while(1){printf("我是子进程,PID为%d,PPID为%d\n",getpid(),getppid());sleep(1);}}else {//父进程while(1){printf("我是父进程,PID为%d,子进程ID为%d\n",getpid(),id);sleep(1);}}return 0;
}

  运行上述程序并且使用kill结束子进程,但此时父进程没有做任何处理,那么子进程就是僵尸状态,如果父进程一直不处理那么就会造成内存泄漏。

在这里插入图片描述
  刚才是父进程和子进程都存在的情况下,结束子进程那么子进程是僵尸进程。如果结束父进程的话那么子进程就会操作系统领养称之为孤儿进程。操作系统的pid为一。

在这里插入图片描述

3 进程优先级

3.1 进程优先级意义

   CPU的资源具有稀缺性,进程优先级是进程获得CPU资源的一种先后顺序,即竞争力,就像我们下课时去食堂排队打饭一样,先到的他的优先级就越高就越先打到饭吃饭。

3.2 修改优先级

  Linux优先级由两部分组成一部分是priority(默认优先级),一部分是nice(微调优先级),进程的真实优先级等于priority+nice。在Linux中修改进程的优先级只能修改nice不能修改默认优先级。
在这里插入图片描述

3.2.1 top修改优先级

  按照以下步骤操作修改,top -> r ->pid -> nice值
在这里插入图片描述
在这里插入图片描述
  需要注意的是普通用户禁止频繁修改优先级, 如果被系统弹出禁止,需要切换到超级用户或者用sudo执行top。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
  在这里需要注意的是nice的值的范围是从-20到19,输入超过这个范围的值会自动适配到这个范围内。并且这个priority优先级是最初的优先级加上修改后的nice,而不是上一次的priority加上nice值
在这里插入图片描述

3.2.2 命令行修改优先级

renice <nice值> -p <进程ID>

在这里插入图片描述
  也可以修改为负数。在原来八十的优先级上减十就得到了现在七十的优先级.
在这里插入图片描述

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

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

相关文章

深入理解 MyBatis 框架的核心对象:SqlSession

Mybatis框架中的SqlSession对象详解 引言 MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的工作。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息&#xff0…

Tcp_socket

Tcp不保证报文完整性&#xff08;面向字节流&#xff09; 所以我们需要在应用层指定协议&#xff0c;确保报文完整性 // {json} -> len\r\n{json}\r\n bool Encode(std::string &message) {if(message.size() 0) return false;std::string package std::to_string(m…

【运维心得】Centos7安装Redis7.4.2并处理相关告警

概述 单机版的redis安装比较简单&#xff0c;这里重点记录下告警的处理。 安装步骤 1. 确认版本 可以通过官方仓库或者知名的网站获取最新安装包&#xff0c;截止20250213&#xff0c;未找到官方安装包。 rpmfind: RPM resource redis(x86-64)https://rpmfind.net/linux/rpm2h…

社区版IDEA中配置TomCat(详细版)

文章目录 1、下载Smart TomCat2、配置TomCat3、运行代码 1、下载Smart TomCat 由于小编的是社区版&#xff0c;没有自带的tomcat server&#xff0c;所以在设置的插件里面搜索&#xff0c;安装第一个&#xff08;注意&#xff1a;安装时一定要关闭外网&#xff0c;小编因为这个…

K8s之存储卷

一、容忍、crodon和drain 1.容忍 即使节点上有污点&#xff0c;依然可以部署pod。 字段&#xff1a;tolerations 实例 当node01上有标签test11&#xff0c;污点类型为NoSchedule&#xff0c;而node02没有标签和污点&#xff0c;此时pod可以在node01 node02上都部署&#xff0c…

【MySQL — 数据库基础】深入解析 MySQL 的联合查询

1. 插入查询结果 语法 insert into table_name1 select* from table_name2 where restrictions ;注意&#xff1a;查询的结果集合&#xff0c;列数 / 类型 / 顺序 要和 insert into 后面的表相匹配&#xff1b;列的名字不要求相同&#xff1b; create table student1(id int , …

spring cloud 使用 webSocket

1.引入依赖,(在微服务模块中) <!-- Spring WebSocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 2.新建文件 package com.ruoyi.founda…

《aarch64汇编从入门到精通》-204页PPT+实验

&#x1f534;【课程特色】 ✅1、依照官方文档总结制作&#xff0c;体系更完整&#xff0c;不遗漏知识&#xff1b; ✅2、基于Armv8/Armv9架构讲解汇编。真正的ARM汇编&#xff1b; ✅3、资料更全。200多页PPT资料&#xff0c;其它参考资料; ✅4、学完ARM架构&#xff0c;再学汇…

企业使用统一终端管理(UEM)工具提高端点安全性

什么是统一终端管理(UEM) 统一终端管理(UEM)是一种从单个控制台管理和保护企业中所有端点的方法&#xff0c;包括智能手机、平板电脑、笔记本电脑、台式机和 IoT设备。UEM 解决方案为 IT 管理员提供了一个集中式平台&#xff0c;用于跨所有作系统和设备类型部署、配置、管理和…

20250213 隨筆 雪花算法

雪花算法&#xff08;Snowflake Algorithm&#xff09; 雪花算法&#xff08;Snowflake&#xff09; 是 Twitter 在 2010 年開發的一種 分布式唯一 ID 生成算法&#xff0c;它可以在 高併發場景下快速生成全局唯一的 64-bit 長整型 ID&#xff0c;且不依賴資料庫&#xff0c;具…

QT 异步编程之多线程

一、概述 1、在进行桌面应用程序开发的时候&#xff0c;假设应用程序在某些情况下需要处理比较复制的逻辑&#xff0c;如果只有一个线程去处理&#xff0c;就会导致窗口卡顿&#xff0c;无法处理用户的相关操作。这种情况下就需要使用多线程&#xff0c;其中一个线程处理窗口事…

leetcode 543. 二叉树的直径

题目如下 数据范围 示例 显然直径等于左右子树高之和的最大值。通过代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* Tr…

IP 路由基础 | 路由条目生成 / 路由表内信息获取

注&#xff1a;本文为 “IP 路由” 相关文章合辑。 未整理去重。 IP 路由基础 秦同学学学已于 2022-04-09 18:44:20 修改 一. IP 路由产生背景 我们都知道 IP 地址可以标识网络中的一个节点&#xff0c;并且每个 IP 地址都有自己的网段&#xff0c;各个网段并不相同&#xf…

sql:时间盲注和boolen盲注

关于时间盲注&#xff0c;boolen盲注的后面几个获取表、列、具体数据的函数补全 时间盲注方法 import time import requests# 获取数据库名 def inject_database(url):dataname for i in range(1, 20):low 32high 128mid (low high) // 2while low < high:payload &q…

zyNo.22

常见Web漏洞解析 命令执行漏洞 1.Bash与CMD常用命令 &#xff08;1&#xff09;Bash 读取文件&#xff1a;最常见的命令cat flag 在 Bash 中&#xff0c;cat 以及的tac、nl、more、head、less、tail、od、pr 均为文件读取相关命令&#xff0c;它们的区别如下&#xff1a; …

《Python 中 JSON 的魔法秘籍:从入门到精通的进阶指南》

在当今数字化时代&#xff0c;网络编程无处不在&#xff0c;数据的高效传输与交互是其核心。JSON 作为一种轻量级的数据交换格式&#xff0c;凭借其简洁、易读、跨语言的特性&#xff0c;成为网络编程中数据传输与存储的关键技术。无论是前后端数据交互&#xff0c;还是不同系统…

部门管理(体验,最原始方法来做,Django+mysql)

本人初学&#xff0c;写完代码在此记录和复盘 在创建和注册完APP之后&#xff08;我的命名是employees&#xff09;&#xff0c;编写models.py文件创建表 手动插入了几条数据 1.部门查询 urls.py和views.py在编写之前&#xff0c;都要注意导入对应的库 urls.py&#xff1a;…

Docker的容器

Docker的容器 一&#xff0e;容器 容器是一种轻量级的虚拟化技术。它有效的将单个操作系统的资源划分到各独立的组中&#xff0c;以便更好的平衡这些独立的组之间资源的使用。 容器主要包含了命名空间&#xff08;Namespaces&#xff09;和cgroup&#xff08;Control Groups…

[Redis] Redis分布式锁与常见面试题

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

Word 公式转 CSDN 插件 发布

经过几个月的苦修&#xff0c;这款插件终于面世了。 从Word复制公式到CSDN粘贴&#xff0c;总是出现公式中的文字被单独提出来&#xff0c;而公式作为一个图片被粘贴的情况。公式多了的时候还会导致CSDN禁止进一步的上传公式。 经过对CSDN公式的研究&#xff0c;发现在粘贴公…