Linux进程-1

一:什么是进程

课本概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。

进程=内核数据结构(PCB)+   程序代码和数据

操作系统是如何管理进程的呢? 先描述,再组织。

先描述:1. 处于磁盘的程序加载到内存 2.操作系统在内核中生成一个结构对象(PCB),linux下称为task_struct. task_struct指向内存中对应的代码和数据,并存有其相关的属性 信息。

再组织:把每个task_struct通过链表这种数据结构进行管理。就可以管理每个进程。

task_struct 内容

1.标示符: 描述本进程的唯一标示符,用来区别其他进程。
2.状态: 任务状态,退出代码,退出信号等。
3.优先级: 相对于其他进程的优先级。
4.程序计数器: 程序中即将被执行的下一条指令的地址。
5.内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
6.上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
7.I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
8.记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息

二:如何查看进程

1.ps指令

当一个进程启动后,我们可以用ps axj 查看进程并用grep 查找目标进程

[root@hcss-ecs-178e ~]# ps axj|grep myexe
 3112  3170  3170  3081 pts/0     3170 S+    1001   0:00 ./myexe
 3171  3191  3190  3171 pts/1     3190 S+       0   0:00 grep --color=auto myexe

head -1打印第一行内容 

可以用; 或者 && 在一行中输出多个指令

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 3112  3575  3575  3081 pts/0     3575 S+    1001   0:00 ./myexe
 3171  3579  3578  3171 pts/1     3578 S+       0   0:00 grep --color=auto myexe

grep --color=auto myexe 这个进程是grep指令,因为带有myexe关键字所以被一起查找到。

可以用grep -v grep 反向过滤。

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe|grep -v grep
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 3112  3587  3587  3081 pts/0     3587 S+    1001   0:00 ./myexe

2./proc

根目录下有proc目录,它是一个虚拟文件系统,包含了系统和进程的信息。它提供了关于内核、系统状态和正在运行的进程的数据,用户可以通过读取这些文件来获取实时信息。

proc目录里面蓝色的是什么?

这些也是目录,目录名数字代表进程的PID,里面存有进程的属性。

(每个进程的PID都是唯一的,一个进程重新启动PID可能会和上一次不同)

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 3651  3780  3780  3621 pts/0     3780 S+    1001   0:00 ./myexe
 3668  3784  3783  3668 pts/1     3783 S+       0   0:00 grep --color=auto myexe
[root@hcss-ecs-178e ~]# cd /proc/3780
[root@hcss-ecs-178e 3780]# ll
total 0
dr-xr-xr-x 2 wws wws 0 Sep 20 16:44 attr
-rw-r--r-- 1 wws wws 0 Sep 20 16:44 autogroup
-r-------- 1 wws wws 0 Sep 20 16:44 auxv
-r--r--r-- 1 wws wws 0 Sep 20 16:44 cgroup
--w------- 1 wws wws 0 Sep 20 16:44 clear_refs
-r--r--r-- 1 wws wws 0 Sep 20 16:44 cmdline
-rw-r--r-- 1 wws wws 0 Sep 20 16:44 comm
-rw-r--r-- 1 wws wws 0 Sep 20 16:44 coredump_filter
-r--r--r-- 1 wws wws 0 Sep 20 16:44 cpuset
lrwxrwxrwx 1 wws wws 0 Sep 20 16:44 cwd -> /home/wws
-r-------- 1 wws wws 0 Sep 20 16:44 environ
lrwxrwxrwx 1 wws wws 0 Sep 20 16:44 exe -> /home/wws/myexe
dr-x------ 2 wws wws 0 Sep 20 16:44 fd
dr-x------ 2 wws wws 0 Sep 20 16:44 fdinfo
-rw-r--r-- 1 wws wws 0 Sep 20 16:44 gid_map
.....

exe -> /home/wws/myexe 这个路径是myexe程序所处的路径

cwd -> /home/wws 指的是当前目录,即myexe程序所处的目录

当myexe生成文件时就会放在当前当前目录下。

如果想改变当前工作路径可以在程序中使用chdir函数

#include<unistd.h> 

chdir("更改为的路径“)

成功返回0

注意如果改为根目录,生成的文件如果用户没有根目录的写权限,也是不能生成文件的。

3.top指令

是一个常用的 Linux 命令行工具,用于实时监控系统的进程和资源使用情况。它显示了系统中运行的进程信息,包括 CPU 和内存使用率。

  • M:按内存使用量排序。
  • P:按 CPU 使用率排序。
  • k:杀死一个进程(需要输入 PID)。
  • q:退出 top

三:getpid getppid 获取PID

#include <sys/types.h> 这个头文件时,通常是为了定义一些数据类型和结构,特别是在进行系统调用或处理文件和进程时。常见的类型包括:

  • pid_t:用于表示进程 ID。
  • size_t:用于表示对象的大小。
  • off_t:用于表示文件的偏移量。

gcc -o myexe code.c -g
[wws@hcss-ecs-178e ~]$ ./myexe
我的PID:4857,我的父进程PID:4289
我的PID:4857,我的父进程PID:4289
我的PID:4857,我的父进程PID:4289
我的PID:4857,我的父进程PID:4289
^C
[wws@hcss-ecs-178e ~]$ ./myexe
我的PID:4858,我的父进程PID:4289
我的PID:4858,我的父进程PID:4289
我的PID:4858,我的父进程PID:4289
我的PID:4858,我的父进程PID:4289

子进程每次重新启动pid都会变化,但父进程不会变,因为进程可以有多个子进程但自能有一个父进程。

可以看到myexe的父进程是bash,bash是什么?
bash是命令行解释器,也是一种进程。

bash为什么要创建子进程?

Bash 创建子进程来执行程序主要是为了实现进程隔离和资源管理。通过在子进程中运行命令,Bash 可以:

  1. 避免影响主进程:子进程的错误或终止不会直接影响到 Bash 本身。
  2. 简化信号处理:主进程可以更好地管理和处理来自子进程的信号。
  3. 实现并行执行:可以同时运行多个子进程,而主进程可以继续接收用户输入。

四:fork()创建子进程

1.fork()用法

fork()函数会创建一个子进程,成功给父进程返回子进程的PID,给子进程返回0.失败给父进程返回-1.

[wws@hcss-ecs-178e ~]$ ./myexe
我是父进程,我的PID5443,我的子进程PID5444
我是子进程,我的PID:5444,我的父进程PID5443
我是父进程,我的PID5443,我的子进程PID5444
我是子进程,我的PID:5444,我的父进程PID5443
我是父进程,我的PID5443,我的子进程PID5444
我是子进程,我的PID:5444,我的父进程PID5443
我是子进程,我的PID:5444,我的父进程PID5443
我是父进程,我的PID5443,我的子进程PID5444
我是父进程,我的PID5443,我的子进程PID5444
我是子进程,我的PID:5444,我的父进程PID5443
我是子进程,我的PID:5444,我的父进程PID5443
我是父进程,我的PID5443,我的子进程PID5444

2.fork()怎么创建进程

我们知道进程=内核数据结构+程序代码和数据

fork的子进程和父进程不同,它在磁盘中没有对应的可执行程序,不能加载到内存中。所以子进程的程序代码和数据会继承父进程的,task_struct也是根据父进程为模板再进行部分调整。再连入进程表中,就建立了一个子进程。

父子进程代码共享一份,但数据独享。相互独立,互不影响。

eg.代码中定义的全局变量,父进程改变了全局变量,但子进程的全局变量数据不会改变。

问题:为什么fork有两个返回值?给父子进程分别返回值
fork()函数在return 时,已经建立了子进程,父进程return一次,子进程return一次就会有两个返回值。

通常情况下,fork() 调用后,父进程和子进程会几乎同时开始执行,谁先执行取决于调度器的决定

五:进程状态

进程状态可以简单分为以下几种

在分时操作系统中,CPU在处理进程时是轮流进行处理,多个进程组成运行队列,cpu处理一段时间后,再按队列顺序继续处理下一个进程。只是cpu处理速度快,我们在使用过程中没发现。

1.运行

我们知道每个进程都有与之对应的task_struct,这些结构体通过链表的方式组成队列,连入到struct runqueue。

我们把它称作运行队列,处于运行队列的task_struct的进程状态为运行。

2.阻塞

当一个程序中出现scanf需要从外设中读数据,导致cpu不能继续处理时,该进程的task_struct就不能继续在运行队列了。就要去访问外部设备了

操作系统是如何管理外部设备的呢?
和对进程管理一样,先描述在组织。

每个外设都有对应的结构体struct device,结构体间再通过链表连接起来,进行管理。

每个struct device中都有task_struct*wait queue 等待队列,每当有进程需要获取外部数据,task_struct就要进入等待队列。

进入等待队列的task_struct的进程状态为阻塞。

3.挂起

一般当操作系统内存不足时,为了节省空间,会把处于阻塞状态的代码和数据换出到磁盘当中,等到在阻塞队列出队列的时候,再把代码和数据换入到内存。(磁盘中有专门进行进行换入换出的空间)

这种就叫阻塞挂起,当然也有运行挂起,这种状态下不能被cpu处理。

挂起就是通过时间换取空间的做法。

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): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
(interruptibl
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的
进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可
以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态

1.R状态

什么是R状态,Linux下的R状态和上面运行状态概念一样,处在运行队列里就是R状态。

运行上面程序

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 5861  6565  6565  5832 pts/2     6565 S+    1001   0:00 ./myexe
 6378  6569  6568  6378 pts/3     6568 S+       0   0:00 grep --color=auto myexe

这个程序不是死循环吗?为什么不是处于R,而是S(阻塞)?

因为printf函数是向外设打印信息,而与外设进行交互是非常慢的,所以大部分时间都是位于阻塞状态,等到外部资源就绪,进程才能被cpu再度调度,变为R.

(处于sleep时也是阻塞状态)

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 5861  7085  7085  5832 pts/2     7085 R+    1001   0:02 ./myexe
 6378  7089  7088  6378 pts/3     7088 R+       0   0:00 grep --color=auto myexe

2.S状态

S状态可以理解为阻塞状态,可以被杀死。可终断睡眠,浅睡眠。

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 5861  7283  7283  5832 pts/2     7283 S+    1001   0:00 ./myexe
 6378  7287  7286  6378 pts/3     7286 S+       0   0:00 grep --color=auto myexe

3.D状态

和S状态不同的是,处于D状态的程序不能被杀死。

为什么要有D状态呢?

在内存不足时,操作系统会杀掉睡眠的进程,来腾出空间。但如果该进程正在向磁盘写入数据,被杀死就会数据泄露。这时候就需要D状态来保证不会被系统杀掉。

4.T状态

T状态暂停状态,和阻塞状态不同的是,T状态是主动暂停,需要用户自主决定是否恢复,而阻塞状态是正在等待某些事件(如I/O操作完成、信号或资源的可用性),是被动暂停,等条件满足自动恢复。

kill

kill -9 杀死进程

kill -19 暂停进程

kill -18 继续进程

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
21487 22120 22120 21458 pts/0    22120 S+    1001   0:00 ./myexe
21844 22124 22123 21844 pts/1    22123 S+       0   0:00 grep --color=auto myexe
[root@hcss-ecs-178e ~]# kill -19 22120
[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
21487 22120 22120 21458 pts/0    21487 T     1001   0:00 ./myexe
21844 22128 22127 21844 pts/1    22127 S+       0   0:00 grep --color=auto myexe

可以看到kill -19 暂停进程后,状态从S+变为T。

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
21487 22120 22120 21458 pts/0    21487 T     1001   0:00 ./myexe
21844 22128 22127 21844 pts/1    22127 S+       0   0:00 grep --color=auto myexe
[root@hcss-ecs-178e ~]# kill -18 22120
[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
21487 22120 22120 21458 pts/0    21487 S     1001   0:00 ./myexe
21844 22132 22131 21844 pts/1    22131 S+       0   0:00 grep --color=auto myexe

可以看到kill -18 恢复进程后,状态从T变为S。

S和S+有什么不同吗?

S+指进程在前台运行,可以用Ctrl+c结束进程。

S指进程在后台运行,此时不能响应Ctrl+c,只能用kill -9 杀死进程。

5.t状态

我们在调试代码时,运行到断点停止,此时的状态就是追踪暂停t状态。

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
21487 22328 22328 21458 pts/0    22328 S+    1001   0:00 gdb ./myexe
22328 22334 22334 21458 pts/0    22328 t     1001   0:00 /home/wws/./myexe
21844 22341 22340 21844 pts/1    22340 S+       0   0:00 grep --color=auto myexe

6.Z状态

我们在代码结束时 一般会写return 0 这其实是退出码,根据退出信息来判读程序是否正常结束。

在linux下通过 echo $? 可以查看最近一次程序的退出码

[wws@hcss-ecs-178e ~]$ ls
code.c  dir1  install.sh  makefile  myexe
[wws@hcss-ecs-178e ~]$ echo $?
0

而Z状态(僵尸状态)存在的意义就是为父进程/系统提供退出信息。

当一个子进程结束时,它的代码和数据会从内存中消失,但它的task_struct从进程队列中退出,但不会从内存中消失。此时这个进程就会处于Z状态,需要由父进程回收。如果不回收,就会造成内存泄漏。

如果父进程是bash的话,子进程结束后,会执行回收机制,看不到僵尸状态。

我们可以用fork(),生成子进程。

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
27309 28139 28139 27280 pts/0    28139 S+    1001   0:00 ./myexe
28139 28140 28139 27280 pts/0    28139 S+    1001   0:00 ./myexe
27326 28144 28143 27326 pts/1    28143 S+       0   0:00 grep --color=auto myexe
[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
27309 28139 28139 27280 pts/0    28139 S+    1001   0:00 ./myexe
28139 28140 28139 27280 pts/0    28139 Z+    1001   0:00 [myexe] <defunct>
27326 28149 28148 27326 pts/1    28148 S+       0   0:00 grep --color=auto myexe

可以看到子进程从S+变成Z+,保留退出信息。此时需要父进程主动回收,不然会出现内存泄漏。

7.X状态

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

六:孤儿进程

父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程。

[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
30295 30440 30440 30266 pts/0    30440 S+    1001   0:00 ./myexe
30440 30441 30440 30266 pts/0    30440 S+    1001   0:00 ./myexe
30442 30464 30463 30442 pts/1    30463 S+       0   0:00 grep --color=auto myexe

关闭父进程

[root@hcss-ecs-178e ~]# kill 30440
[root@hcss-ecs-178e ~]# ps axj|head -1;ps axj|grep myexe
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    1 30441 30440 30266 pts/0    30295 S     1001   0:00 ./myexe
30442 30476 30475 30442 pts/1    30475 S+       0   0:00 grep --color=auto myexe

可以看到子进程的父进程改变了,被进程1领养了。

用top指令查询进程

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                     
  840 root      20   0 1295272  19320   5304 S   0.3  1.9  38:33.83 hostguard                                                                   
    1 root      20   0  125520   3308   1964 S   0.0  0.3   0:09.87 systemd         

PID 为 1 的进程通常是systemd它是系统和服务管理器,负责启动和管理系统中的其他进程和服务。

此时子进程会处于后台运行,不能通过ctrl+c停止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值