linux 环境下进程什么时候会被 killed掉

linux 环境下进程什么时候会被 killed掉,第1张

OS:cent os 6.5

日志路径:/var/log/messages

服务器上跑的一个程序,发现报了Killed。查看/var/log/messages里的日志,发现以下报错:

[plain] view plain copy

Aug 11 16:28:11 kernel: Out of memory: Kill process 3080 (forward) score 559 or sacrifice child

Aug 11 16:28:11 kernel: Killed process 3080, UID 0, (forward) total-vm:1429064kB, anon-rss:1130444kB, file-rss:136kB

原来linux下也有内存OOM的处理,不同于android 杀掉报OOM的进程,linux是杀掉当前最占用内存的进程。

当Linux报OOM时,意味着整个系统的内存已经不足,如果不杀死进程的话,就会导致系统的崩溃。每个进程都会存有一个oom_score的参数,比如输出pid为988的oom_score:

[plain] view plain copy

cat /proc/988/oom_score

OOM Killer 会在系统报OOM的时候,杀死当前score最高的进程,一般情况也就是占用内存最大的进程。

解决方法:一是增加系统内存,二是优化进程,使其占用内存降低。三是可以使用oom_score_adj参数,这个参数会被计算入oom_score,可以避免进程被杀死(不推荐使用)。

Linux 允许进程查询内核以获得其父进程的 PID,或者其任何子进程的执行状态。例如,进程可以创建一个子进程来执行特定的任务,然后调用诸如 wait() 这样的一些库函数检查子进程是否终止。如果子进程已经终止,那么,它的终止代号将告诉父进程这个任务是否已成功地完成。

为了遵循这些设计原则,不允许 Linux 内核在进程一终止后就丢弃包含在进程描述符字段中的数据。只有父进程发出了与被终止的进程相关的 wait() 类系统调用之后,才允许这样做。这就是引入僵死状态的原因:尽管从技术上来说进程已死,但必须保存它的描述符,直到父进程得到通知。

如果一个进程已经终止,但是它的父进程尚未调用 wait() 或 waitpid() 对它进行清理,这时的进程状态称为僵死状态,处于僵死状态的进程称为僵尸进程(zombie process)。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。

僵尸进程是如何产生的

在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。通过ps命令查看其带有defunct的标志。僵尸进程是一个早已死亡的进程,但在进程表 (processs table)中仍占了一个位置(slot)。

但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个 进程是刚刚结束的这个进程的子进程,如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动 wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。

为了观察到僵尸进程,我们自己写一个不正常的程序,父进程 fork 出子进程,子进程终止,而父进程既不终止也不调用 wait 清理子进程:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

int i = 100

pid_t pid=fork()

if(pid <0)

{

perror("fork failed.")

exit(1)

}

if(pid >0)

{

printf("This is the parent process. My PID is %d.\n", getpid())

for(i >0i--)

{

sleep(1)

}

}

else if(pid == 0)

{

printf("This is the child process. My PID is: %d. My PPID is: %d.\n", getpid(), getppid())

}

return 0

}

把上面的代码保存到文件 zomprocdemo.c 文件中,并执行下面的命令编译:

?

1

$ gcc zomprocdemo.c -o zomprocdemo

然后运行编译出来的 zomprocdemo 程序:

?

1

$ ./zomprocdemo

请点击输入图片描述

此时子进程已经退出,但是父进程没有退出也没有通过 wait() 调用处理子进程。我们使用 ps 命令查看进程的状态:

请点击输入图片描述

上图红框中的大写字母 "Z" 说明 PID 为 112712 的进程此时处于僵死的状态。

让我们接着往下看!在结束 sleep 后父进程退出。当父进程退出后,子进程会变成孤儿进程,此时它会被一个管理进程收养。在不同的系统中,这个管理进程不太一样,早期一般是 init 进程,Ubuntu 上是 upstart,还有近来的 Systemd。但是它们都完成相同的任务,就是 wiat() 这些孤儿进程,并最终释放它们占用的系统进程表中的资源。这样,这些已经僵死的孤儿进程就彻底的被清除了。

僵尸进程的危害

在进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号 PID,退出状态 the termination status of the process,运行时间 the amount of CPU time taken by the process 等)。直到父进程通过 wait / waitpid 来取时才释放。

如果进程不调用 wait / waitpid 的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。

如何处理僵尸进程

僵尸进程的产生是因为父进程没有 wait() 子进程。所以如果我们自己写程序的话一定要在父进程中通过 wait() 来避免僵尸进程的产生。

当系统中出现了僵尸进程时,我们是无法通过 kill 命令把它清除掉的。但是我们可以杀死它的父进程,让它变成孤儿进程,并进一步被系统中管理孤儿进程的进程收养并清理。

下面的 demo 中,父进程通过 wait() 等待子进程结束:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

#include <sys/types.h>

#include <sys/wait.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

pid_t pid

pid = fork()

if (pid <0)

{

perror("fork failed")

exit(1)

}

if (pid == 0) {

int i

for (i = 3i >0i--)

{

printf("This is the child\n")

sleep(1)

}

// exit with code 3 for test.

exit(3)

}

else

{

int stat_val

wait(&stat_val)

if (WIFEXITED(stat_val))

{

printf("Child exited with code %d\n", WEXITSTATUS(stat_val))

}

}

return 0

}

demo 中父进程不仅等待子进程结束,还通过 WEXITSTATUS 宏取到了子进程的 exit code。

首先要弄明确一个问题,就是我们平时说的Service防杀死,其实防止是Service所在的进程被杀死,而不是Service这个组件,因为Android 只杀死进程,而不是组件。

当我们说进程优先级的时候是以 activity、service 这样的组件来说的,这些组件的优先级是在进程的级别上,不是组件级别上。只要一个组件的状态发生变化,就会影响进程的优先级;比如:启动一个前台服务,那么就会将该服务所在的整个进程变为前台进程。

弄清楚了这个问题后,下面我们首先了解一下进程的优先级。

前台进程 >可见进程 >服务进程 >后台进程 >空进程。

所谓进程防杀死,就是做到进程尽量不被系统杀死,并不能保证100%存活,因为受到内存,手机厂商的限制等。上面提到进程优先级,优先级越高越不容易被杀死,所以要想防止进程被杀死,就要提高进程的优先级。

QQ采取在锁屏的时候启动一个1个像素的Activity,当用户解锁以后将这个Activity结束掉,同时把自己的核心服务再开启一次。下面我们就简单模拟一下。

封装一个工具类,专门监听锁屏事件

启动一个Service专门管理Activity

在应用启动的时候启动该服务

添加权限

双进程守护,可以防止单个进程杀死,同时可以防止第三方的软件清理掉。一个进程被杀死,另外一个进程又被他启动。相互监听启动,因为杀进程是一个一个杀的。本质是和杀进程时间赛跑。

Android 5.0系统以后,Google为了优化Android系统,提高使用流畅度以及延长电池续航,加入了在应用后台/锁屏时,系统会回收应用,同时自动销毁应用拉起的Service的机制。同时为了满足在特定条件下需要执行某些任务的需求,google在全新一代 *** 作系统上,采取了Job (jobservice &JobInfo)的方式,即每个需要后台的业务处理为一个job,通过系统管理job,来提高资源的利用率,从而提高性能,节省电源。这样又能满足APP开发的要求,又能满足系统性能的要求。

把任务加到系统调度队列中,当到达任务窗口期的时候就会执行,我们可以在这个任务里面启动我们的进程。这样可以做到将近杀不死的进程。

vip视频


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8238312.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-14
下一篇 2023-04-14

发表评论

登录后才能评论

评论列表(0条)

保存