malloc崩溃

1. 概述

最近 merger 偶发 core dump,位置不定,但有一定规律:

  • 一般是在 RequestManager::MergeResult() 函数或其前后出现。
  • core dump 前超时较多。

core dump 的直接原因,一般是 malloc 或 free函数检测到其内部链表结构被破坏后,报错并主动 abort 进程。

这类问题一般是越界读写内存或者线程不安全导致的。

尝试过使用 valgrind 或者 tcmalloc 检测内存越界,以及代码 review,但都没有解决问题。

进一步分析 core 文件之后,定位到了问题原因。大体步骤如下:

  1. 安装 glibc 的调试信息,使我们能够查看 malloc 或者 free 函数内部的变量
  2. 了解 ptmalloc2 数据结构,找出被破坏的数据(的内存地址)
  3. 通过查看每个线程的寄存器,找出哪个线程在往上一步找到的地址上写数据

最终我们发现有一个线程在向一个已被释放的对象上写数据。

根据此线程的调用栈,就能很快定位到有问题的代码(线程不安全)。

下面是详细分析过程,供以后排查类似问题时参考。

2. core 文件分析

2.1. glibc的日志

merger core dump之后,一般可以在nohup.out里找出glibc输出的信息,如:

*** glibc detected *** /opt/esearch/0/version-138/merger05/package/merger: free(): corrupted unsorted chunks: 0x00007fb8f4009520 ***

这行日志表示当程序在释放一个值为 0x00007fb8f4009520 的指针时,free()函数检测到 ptmalloc2 内部的 unsorted chunks 数据结构已被破坏。

单纯这行日志对我们定位原因帮助不大,因为具体哪个地址上的数据被破坏,它并未给出来。

2.2. 准备调试

打开core文件查看调用栈时,会输出以下内容:

(gdb) bt
#0  0x0000003fa8032925 in raise () from /lib64/libc.so.6
#1  0x0000003fa8034105 in abort () from /lib64/libc.so.6
#2  0x0000003fa8070837 in __libc_message () from /lib64/libc.so.6
#3  0x0000003fa8076166 in malloc_printerr () from /lib64/libc.so.6
#4  0x0000003fa8078c93 in _int_free () from /lib64/libc.so.6
#5  0x00007fb94bb1a394 in deallocate (__p=<optimized out>, this=<optimized out>)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/ext/new_allocator.h:94
#6  _M_deallocate (__n=<optimized out>, __p=<optimized out>, this=<optimized out>)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:133
#7  ~_Vector_base (this=<optimized out>, __in_chrg=<optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:119
#8  ~vector (this=<optimized out>, __in_chrg=<optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:272
#9  netter::RequestManager::~RequestManager (this=0x7fb85c8cd9e0, __in_chrg=<optimized out>) at build/release64/netter/arbiter/request_manager.cpp:36
#10 0x00007fb94bb16c30 in netter::RequestManager::TimeCheck (this=0x7fb85c8cd9e0, now=1429684563654, toDele=@0x7fb93d679e57: true)
    at build/release64/netter/arbiter/request_manager.cpp:132
...

可以看出来,_int_free() 函数在执行过程中检测到了错误,调用 malloc_printerr() 打印错误并终止进程。

为了定位错误数据的位置,需要跟踪进 _int_free() 这个函数去分析。但机器上一般没有装 glibc 的调试信息,看不到 _int_free() 的源码。

gdb 加载 core 文件时会给出提示,告诉你该用什么命令、安装哪些调试信息:

Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6.x86_64 libgcc-4.4.7-4.el6.x86_64 libstdc++-4.4.7-4.el6.x86_64 zlib-1.2.3-29.el6.x86_64

如果没有 root 权限,则无法执行这个命令。变通的方法见[_安装glibc调试信息]

2.3. 开始调试

安装了调试信息后,gdb给出的调用栈如下:

(gdb) bt
#0  0x0000003fa8032925 in *__GI_raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1  0x0000003fa8034105 in *__GI_abort () at abort.c:92
#2  0x0000003fa8070837 in __libc_message (do_abort=2, fmt=0x3fa8158ac0 "*** glibc detected *** %s: %s: 0x%s ***\n") at ../sysdeps/unix/sysv/linux/libc_fatal.c:198
#3  0x0000003fa8076166 in malloc_printerr (action=3, str=0x3fa8158e48 "free(): corrupted unsorted chunks", ptr=<optimized out>) at malloc.c:6332
#4  0x0000003fa8078c93 in _int_free (av=0x7fb8f4000020, p=0x7fb8f4009510, have_lock=0) at malloc.c:4832
#5  0x00007fb94bb1a394 in deallocate (__p=<optimized out>, this=<optimized out>)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/ext/new_allocator.h:94
#6  _M_deallocate (__n=<optimized out>, __p=<optimized out>, this=<optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:133
#7  ~_Vector_base (this=<optimized out>, __in_chrg=<optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:119
#8  ~vector (this=<optimized out>, __in_chrg=<optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:272
#9  netter::RequestManager::~RequestManager (this=0x7fb85c8cd9e0, __in_chrg=<optimized out>) at build/release64/netter/arbiter/request_manager.cpp:36
#10 0x00007fb94bb16c30 in netter::RequestManager::TimeCheck (this=0x7fb85c8cd9e0, now=1429684563654, toDele=@0x7fb93d679e57: true) at build/release64/netter/arbiter/request_manager.cpp:132
...

来看看 _int_free() 中 4832 行的代码:

4819      /* Little security check which won't hurt performance: the
4820         allocator never wrapps around at the end of the address space.
4821         Therefore we can exclude some size values which might appear
4822         here by accident or by "design" from some intruder.  */
4823      if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
4824          || __builtin_expect (misaligned_chunk (p), 0))
4825        {
4826          errstr = "free(): invalid pointer";
4827        errout:
4828    #ifdef ATOMIC_FASTBINS
4829          if (have_lock || locked)
4830            (void)mutex_unlock(&av->mutex);
4831    #endif
4832          malloc_printerr (check_action, errstr, chunk2mem(p));
4833    #ifdef ATOMIC_FASTBINS
4834          if (have_lock)
4835            mutex_lock(&av->mutex);
4836    #endif
4837          return;
4838        }

结合整个 _int_free() 函数的源码分析后发现,这里是 _int_free() 函数中通用的错误处理逻辑,而不是实际检测到错误的地方。

_int_free() 中有多处错误检测,一旦检测出错误之后,就会设置 errstr 的值然后 goto errout。

从调用栈中可以看到 errstr 的值(即 malloc_printerr() 的参数 str)实为”free(): corrupted unsorted chunks”,据此可以在源码中找到实际报错的地方:

5019       /*
5020     Place the chunk in unsorted chunk list. Chunks are
5021     not placed into regular bins until after they have
5022     been given one chance to be used in malloc.
5023       */
5024
5025       bck = unsorted_chunks(av);
5026       fwd = bck->fd;
5027       if (__builtin_expect (fwd->bk != bck, 0))
5028     {
5029       errstr = "free(): corrupted unsorted chunks";
5030       goto errout;
5031     }

可见,错误的原因是:

fwd->bk != bck

其中,

  • fwd 和 bck 为指向 struct malloc_chunk 的指针,这个类型用于表示单个内存块。
  • av 为指向 struct mstate 的指针,这个类型用于管理内存块。

这块代码的含义:

  • 经过大小判断、安全检测后,_int_free()决定把释放的内存块放入一个称为 unsorted chunks 的双向链表中。
  • 从 av 中取出 unsorted chunks 双向链表,发现链表头部的前后两个节点衔接不上(后一个节点 fwd 的 bk 指针没有指向前一个节点 bck)。

变量具体的值为:

bck = (struct malloc_chunk *) 0x7fb8f4000078
fwd = (struct malloc_chunk *) 0x7fb8f40010c0
*fwd = {
  prev_size = 140432361410256,
  size = 449,
  fd = (struct malloc_chunk *) 0x7fb8f6275380,
  bk = (struct malloc_chunk *) 0x7fb800000002,
  fd_nextsize = 0x0,
  bk_nextsize = 0xffffffff
}

(实际上 fwd 和 bck 都已被优化掉了,无法直接在 gdb 中查看它们的值。取回它们的值的方法,见[_查看被优化后的变量值]

更深层的原因可能是:

  1. fwd→bk 被篡改
  2. fwd 被篡改,所以 fwd→bk 也不正确
  3. bck 被篡改

首先可以排除(3),了解 unsorted_chunks 的逻辑之后可知,bck 指针是按一定规则通过 av 指针计算出来的,只要 av 指针正确,bck 就正确。

观察 av 指向的对象,其每个成员的值都是有意义的,比如其 bins 数组里每个指针都指向一个有效的 chunk,基本上可以据此判断av指向的是一个有效的对象,应未被篡改。

然后也可以排除(2),因为 fwd 指向的是一个有效的 chunk,因此 fwd 指针也不太可能已被篡改。

上面两次提到了“指向一个有效的chunk”,关于如何判断一个 chunk 有效,见[_chunk_的特性]

2.4. 跟踪 fwd→bk

尝试访问 fwd→bk 指向的对象,发现不可访问:

(gdb) p *(struct malloc_chunk *) 0x7fb800000002
Cannot access memory at address 0x7fb800000002

而 fwd→fd 指向的对象是可访问的,并且也是一个有效的 chunk。这说明 fwd→fd 的值是正常的。

fwd 对象的内存布局如下:

(gdb) x /10wx 0x7fb8f40010c0
0x7fb8f40010c0: 0xf5044ed0      0x00007fb8      0x000001c1      0x00000000
                |<----- prev_size ------>|      |<-----    size    ----->|

0x7fb8f40010d0: 0xf6275380      0x00007fb8      0x00000002      0x00007fb8
                |<-----     fd    ------>|      |<-----     bk     ----->|

0x7fb8f40010e0: 0x00000000      0x00000000

可以发现,bk 的高4个字节 0x00007fb8 与 fd 的高4个字节相同,应为正常值。

唯独其低四个字节 0x00000002 (地址为 0x7fb8f40010d8) 比较异常,不像指针,而像计数或者某种表示状态的值,故怀疑程序中有某些地方修改了这个位置的值。

程序要读写某个内存地址中的值时,一般会先将其地址装入寄存器中。

不过,在访问对象成员的时候可能会用”对象地址(寄存器)+成员偏移量(立即数)”的寻址方式。而在访问数组元素的时候可能用”数组首地址(寄存器)+偏移量(寄存器) * 元素大小(立即数)”的寻址方式。这两种方式下,寄存器里的地址与实际被访问的地址有或大或小的偏差。

不论如何,我们先尝试观察每个线程的寄存器,看看是否有与 0x7fb8f40010d8 相等或相近的值。

幸运的是,我们在一个线程里发现有一个寄存器 r8 的值恰好等于 0x7fb8f40010d8:

(gdb) i registers
...
rsp            0x7fb93ea7bad0   0x7fb93ea7bad0
r8             0x7fb8f40010d8   140432344355032
r9             0x550b   21771
...

该线程调用栈如下:

(gdb) bt
#0  __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1  0x0000003fa8409508 in _L_lock_854 () from /lib64/libpthread.so.0
#2  0x0000003fa84093d7 in __pthread_mutex_lock (mutex=0x7fb8f40010d8) at pthread_mutex_lock.c:61
#3  0x00007fb94bb16d54 in Lock (this=<optimized out>) at /home/esearchQa/sourceCode/trunk/sharelib/build/release64/sdk/sharelib_sdk-0.1.0-release64/include/sharelib/util/lock.h:22
#4  netter::RequestManager::HandleSubRequest (this=0x7fb8f40010d0, partitionId=...) at build/release64/netter/arbiter/request_manager.cpp:86
#5  0x00007fb94bb04232 in netter::ArbiterTcpSubRequest::handlePacket (this=0x7fb8f4006ef0, packet=0x7fb905006280, args=<optimized out>) at build/release64/netter/arbiter/arbiter_tcp_sub_request.cpp:47
#6  0x00007fb94c4a07fb in enet::Connection::handlePacket (this=0x7fb9280467e0, packet=0x7fb905006280) at build/release64/enet/connection.cpp:194
...

可以看到 #2 的参数 mutex 正好是我们要找的地址,而 #4 的参数 this 恰好是 fwd 这个 chunk 的 user memory 地址。

于是我们推断程序执行过程如下:

  1. 程序中申请了一个 RequestManager 对象,地址为 0x7fb8f40010d0
  2. 某个时刻,程序删除了这个对象。ptmalloc2 把这块内存对应的 chunk 放入它的 unsorted chunks 链表中,把 chunk 的 fd 和 bk 指针都设为有效的值。
  3. 但仍有一个线程在对这个对象进行修改(对这个对象里的一个互斥量进行加锁),从而覆盖了bk指针的一部分。
  4. 另一个线程(即报错的线程)删除另一个 RequestManager 对象,此时正在执行 RequestManager 的析构函数,尝试释放其中一个 vector 型成员的内存。
  5. ptmalloc2 尝试把这块内存放入它的 unsorted chunks 链表中,发现链表第一个节点的 bk 指针有问题,于是报错并终止了程序。

随后我们检查了 RequestManager 在什么条件下可能被删除,发现 RequestManager::HandleSubRequest() 函数在修改了 RequestManager 某个成员之后继续对该对象进行加锁以及后续操作,而该成员的改变可能导致超时检测线程删除 RequestManager 对象。

我们尝试在”修改 RequestManager 某个成员”和”继续对该对象进行加锁”这两个步骤间插入了3秒的sleep时间,重新编译并测试,每次运行单测必然会异常终止,于是基本确认了问题。

2.4.1. 内存布局示意图

+-----------------------------------+ <- fwd (0x7fb8f40010c0)
| prev_size                         |
+-----------------------------------+
| size                        |N|M|P|
+-----------------------------------+  <- mem  ->   +-----------------------------------+ <- RequestManager
| fd                                |               | vtable                            |   (0x7fb8f40010d0)
+-----------------------------------+               +-----------------------------------+ <- 0x7fb8f40010d8
| bk                                |               | int __lock     | unsigned __count |\
+-----------------------------------+               |----------------+------------------+ \
|                                   |               | int __owner    | unsigned __nusers|  \
| unused memory                     |               |----------------+------------------+  | mutexForEnqueue
|                                   |               | ...            | ...              |  /
+-----------------------------------+               +-----------------------------------+ /

当程序尝试对已释放的 RequestManager 对象加锁时,会把 __lock 设置为 2,也就导致 bk 的低四个字节被覆盖。

3. 附录

3.1. 安装glibc调试信息

3.1.1. 下载两个rpm包,放到 ~/glibc 目录下:

http://debuginfo.centos.org/6/x86_64/glibc-debuginfo-common-2.12-1.132.el6.x86_64.rpm
http://debuginfo.centos.org/6/x86_64/glibc-debuginfo-2.12-1.132.el6.x86_64.rpm

3.1.2. 安装到 ~/local 目录下:

mkdir -p ~/local
cd ~/local
rpm2cpio ~/glibc/glibc-debuginfo-common-2.12-1.132.el6.x86_64.rpm | cpio -idmv
rpm2cpio ~/glibc/glibc-debuginfo-2.12-1.132.el6.x86_64.rpm | cpio -idmv

3.1.3. 修改gdb配置

在文件 ~/.gdbinit 中增加两行,告诉gdb从哪里找到调试符号和源码文件(假设用户名为work):

set debug-file-directory /home/work/local/usr/lib/debug
dir ~/local/usr/src/debug/glibc-2.12-2-gc4ccff1/malloc

参考:

3.2. 查看被优化后的变量值

3.2.1. 查看寄存器

在gdb中,如果遇到被优化掉的变量,无法用print命令查看其值,可以首先尝试查看变量所在的寄存器。

首先用以下命令,输出当前函数的反汇编代码:

disassemble /m

输出结果可能很长,为了便于查看,可以让 gdb 输出到文件里(例如输出到 _int_free.c):

set logging file _int_free.c
set logging on
set pagination off
disassemble /m

输出结果如下(仅截取相关的一小段)

5019          /*
5020            Place the chunk in unsorted chunk list. Chunks are
5021            not placed into regular bins until after they have
5022            been given one chance to be used in malloc.
5023          */
5024
5025          bck = unsorted_chunks(av);
   0x0000003fa8078815 <+549>:   lea    0x58(%r13),%rax

5026          fwd = bck->fd;
   0x0000003fa8078819 <+553>:   mov    0x10(%rax),%rdx

5027          if (__builtin_expect (fwd->bk != bck, 0))
   0x0000003fa807881d <+557>:   cmp    0x18(%rdx),%rax
   0x0000003fa8078821 <+561>:   jne    0x3fa8078c58 <_int_free+1640>
   0x0000003fa8078c58 <+1640>:  lea    0xe01e9(%rip),%rsi        # 0x3fa8158e48

5028            {
5029              errstr = "free(): corrupted unsorted chunks";
5030              goto errout;
5031            }

可知 bck 寄存器为 rax,fwd 寄存器为 rdx。

查看其值:

(gdb) p $rax
$2 = 0
(gdb) p $rdx
$3 = 6

两个寄存器的值都不像是正常的指针,应该是被别的值覆盖了。

3.2.2. 重新计算

查看寄存器的方法行不通,再尝试另一种方法:

bck 和 fwd 的值都是从 av 这个变量里取出来的,av 的值没有被优化掉,因此我们可以在 gdb 中模拟 unsorted_chunks 的逻辑,重新计算出 bck 和 fwd。

unsorted_chunks 在源码中是一个宏:

2099 /* addressing -- note that bin_at(0) does not exist */
2100 #define bin_at(m, i) \
2101   (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2]))               \
2102          - offsetof (struct malloc_chunk, fd))
......
2201 /*
2202   Unsorted chunks
2203
2204     All remainders from chunk splits, as well as all returned chunks,
2205     are first placed in the "unsorted" bin. They are then placed
2206     in regular bins after malloc gives them ONE chance to be used before
2207     binning. So, basically, the unsorted_chunks list acts as a queue,
2208     with chunks being placed on it in free (and malloc_consolidate),
2209     and taken off (to be either used or placed in bins) in malloc.
2210
2211     The NON_MAIN_ARENA flag is never set for unsorted chunks, so it
2212     does not have to be taken into account in size comparisons.
2213 */
2214
2215 /* The otherwise unindexable 1-bin is used to hold unsorted chunks. */
2216 #define unsorted_chunks(M)          (bin_at(M, 1))

我们可以写一些 gdb 命令模拟这个宏:

define bin_at
    set $retval = (struct malloc_chunk*) (((char *) &($arg0->bins[(($arg1) - 1) * 2])) - (int)&((struct malloc_chunk*)0)->fd)
    p $retval
end

define unsorted_chunk
    bin_at $arg0 1
end

这些命令可以写在当前目录的 .gdbinit 文件里,当 gdb 启动时会自动加载它。

执行命令后,即可得到 bck 和 fwd 的值:

(gdb) unsorted_chunk av
$1 = (struct malloc_chunk *) 0x7fb8f4000078
(gdb) p $1->fd
$2 = (struct malloc_chunk *) 0x7fb8f40010c0

3.3. chunk 的特性

根据内存块大小的不同,ptmalloc2 会用不同的策略来进行管理,但都统一使用 struct malloc_chunk 这种结构来描述内存块。

这里不详述具体的管理策略,仅分析 chunk 的一些特性。

chunk 主要包含了指向前后节点的两个指针,以便构成双向链表,以及供用户使用的内存。

具体定义:

struct malloc_chunk {

  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};
  1. prev_size: 前一个chunk的大小
    • 前一个 chunk 指的是内存地址上相邻的前一个 chunk,并非链表中的上一个节点。
    • 有了这个 prev_size,可以快速定位到前一个 chunk 的头部,从而便于合并内存地址相邻的两个 chunk。
    • 若前一个 chunk 已被分配出去,那么这个 chunk 的 prev_size 也会被其占用,此时 prev_size 是无效的。
  2. size: chunk 的大小。由于 chunk 大小是对齐的,所以 size 的最低3个 bit 都是0,可以用于存储一些信息:
    • prev_inuse = chunk→size & 1, 是否前一个 chunk 正在使用中(已被分配出去)
    • is_mmapped = chunk→size & 2, 此 chunk 是否是用 mmap 分配出来的
    • non_main_arena = chunk→size & 4, 此 chunk 是否不属于 main arena
    • 因此,计算 chunk 的实际大小的方法为:chunksize(p) = p→size & ~(1|2|4)
  3. fd: 指向链表中的下一个节点
  4. bk: 指向链表中的上一个节点

fd 和 bk 等成员,仅在 此 chunk 为空闲时有效。

当 chunk 被分配之后,fd 和 fd 之后的域,以及下一个chunk 的 prev_size 域,都会成为本 chunk 中用户可用内存的一部分。

当 malloc 内部分配出适当大小的chunk之后,返回的指针实际等于 &chunk→fd。

未分配的chunk:

+-----------------------------------+ <- chunk
| prev_size                         |
+-----------------------------------+
| size                        |N|M|P|
+-----------------------------------+
| fd                                |
+-----------------------------------+
| bk                                |
+-----------------------------------+
|                                   |
| unused memory                     |
|                                   |
+-----------------------------------+ <- next chunk
| prev_size                         |
+-----------------------------------+
| size                        |N|M|P| (P = 0)
+-----------------------------------+
| ...                               |
+-----------------------------------+

已分配的chunk:

+-----------------------------------+ <- chunk
| prev_size                         |
+-----------------------------------+
| size                        |N|M|P|
+-----------------------------------+ <- mem
|/fd|
+-----------------------------------+
|/bk|
+-----------------------------------+
|///|
|///|
|///|
+-----------------------------------+ <- next chunk
|/prev_size/|
+-----------------------------------+
| size                        |N|M|P| (P = 1)
+-----------------------------------+
| ...                               |
+-----------------------------------+

一些有用的结论:

  1. 要判断一个空闲 chunk 是否有效,可以看其 size 是否与 next chunk 的 prev_size 相等。(nextchunk = (struct malloc_chunk *) ( ( (char *) chunk ) + chunksize(chunk) ) )
  2. 因为一个 chunk 的已分配状态下的用户可用内存与未分配状态下的 fd 和 bk 指针有所重叠,当程序在修改一个已释放的对象时有可能覆盖掉 fd 或 bk 指针。
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
主要功能: 1.读取配置文件程序 2.启动进程 3.监控进程,查看进程是否退出或者崩溃 4.若进程退出或者崩溃,重启程序。 5.支持sleep功能 6.进程若连续崩溃NUM_MAX次就进行相应的睡眠周期struct proc_struct proc: struct proc_ struct [mp: if(array) return 0 ∥切换到目录rse chdirldiri ifdp= opendir(dir}=NuLL}开日录/proc,矢败返回0,成功把描述指针返回给d return o 〃将φpro文件夹的描述符指针传递给reεddir,读取文件夹内容,循环赋值给结构体di while ((dirp= readdir(dp))= NULLY char data 301 ∥取文件名称赋值给数组daa(其中包含有进程的名称(pid sprintf(data, "s", dirp->d_name); ∥是否是由字符09组成的字符串,即得到所有进程的pid f((IsDigit(data)) prac =(struct proc_struct )4 malloc(sizeof(struct proc_struct) tmp proc: prac->pid =a: oi(dirp->d_name): It(proc tind( proc. array)) free( tmp); closedir(dp cturn proc_find 两个参数分别是两个进程描述的结构体指针 李比较两个进程pd是否相等 李*相等返回1,不相等返回0 幸率球事容球家草事家事球峰率享事球摩率球享享溶事*事卷寒球套事塞容寒/ int proc find( struct prcc_struct* src, struct proc- struct* dest) char buffer[40%6]. ps cmd[20] It fd. I sprintf(buffer, "ed/star", sre->pid); fd = open(butter, O_RDONLY) if(fd==-1) rerurn 0 memset(buffer, wO, sizeof(buffer)) len= read(fd, bufter, sizeof(bufter )-1) close(ld) if(l return 0: p= butter: p= strrchr(p, C) narq=strrchr(p, )) n=q-p-1 if (len >= sizeof, srt->name)) len= sizeof(src->name)-1 p+ l, len src->namelen]=0; =日 turn(strcmp( src->name, dest dest->name)==0)? 1: 0- 条善参数aay:让程结构体指针;参数sie进程列表数组aray的大小ie:配置文件路径 从配置文件得到指定的程序列表,将对应进程的信息填充到aray数组中 羋执行成功返回进程个数,执行失败返回0 int get_ proc( struct proc_struct array, int size, char file intnRet=o if(! array I‖(si 0)l‖fhle myprinttf"invalid parameterin retun o char line[4096]; FILE fp= fopen(file, T"); if(fp) printf("open file cs fail\n", file) return U memset(line, 0, 4095); while(fgets(lire, 4095, tp)&& nRet size) memcpy(void s)[(&arraylnRet )->cmdline), (void")line, strlen(line)-2 ) tmp= strrchr(line, / ) Lmp += I: memcpy((&array inRet))->name, tmp, strlen(tmp)- 2) nRet++ ); return(nReL); 康棒串串浓凉率旅浓串底率卖毒志着旅浓浓准溶房表 装 startProc *卷参数proc:要启动的进的结构体描述指针 启动程序 执行成功返回1,子进程退出 宗塞家康家家家家家家家家宋家家聚家苯家球察塞家塞家家容家塞家家家家室家家察家家家聚家聚寒撑家装家掌建察家家室事 int startProc (struct proc_ struct* proc, struct proc _struct*must_run_ proc int mIst_run_size static inti=d if( proc)return 0 if(strlen(proc->cmdline I<=0) return 0; int pid= forko: 〃进程内部返回值为0,返回给父进程的为自己的pid inta〓 if(pid pid= fork( ifpd≡0 execl(char")proc->cmdline,(char")prDc->name,NULL); ∥exit: It(o): sleep42片 waiL(NULL) sleep( I: if(i== must run size -1) if(check proc(&must run proc[i])==0) startProc( &mtust_run_proeli], must_run_prce, must_run_size); el if(i== must run size-11 i= else 1++ start Proc( &must_run_proclil, must_run_ proc, mustrun_ _size); !**幸幸串率幸米幸*家*幸毕零*幸幸半字幸字华米*幸半孝率非幸零幸学幸幸车 3a*8*daemon init 幸*启动配置文件当中的需要守护的程序 执行成功返回1,中途出错,返回-1 长界零家墨军零家零率家三哮零座零率零零容岸军零罕型率零零零零牢察座察零零零零季球军零容零 int moniter_ run(struct proc_struct"must_run_proc, int proc_ size) nti=0: for(i=0; i< must_run_size: i ++) ∥监控程序是否正在运行 if(check_ proc(&(must un_ proc[il))<=o) ∥厘新片动程序 startProc(&' must run procli]), must run proc, proc size return I: 幸*事率事率率**率**字幸学摩*率*幸幸学幸半*率幸字****幸中*幸学幸 春*着*信号处理函数 exit_proc 翥安全结束监控的程序 4来没有返回值 告参毒萨响幸帝称昨嗜幸古称索点响卷南都南请南幸难布际本啪昨青市南动南香请非市赤南本 void exit_ proc(int ar InL I struct proc struct proc for(i=0; i< must run_ Size: i++) proc=&(must_run_proc[i]): kill(proc->pid, SIGTERM); exit flag=I exit(o): void exit_proc(int pid 要main L.获取程序列表2启动进程3.监控进程,若程序岀或崩溃,重新启动程序4.收到退 出信号,安全结束监控程序 成功返回1,失败返回0 零牢容容家容字家容容察*禁容容字哮零常字容容容家察容牢容零容容容容容牢字家客字容牢容零容*字容客字容容字容家容容字岩 static void run moniter( void data) 读取程序列表 must_ run _size get proc(must_run_proc, sIZE, data if(rmust run Sizc <=1) return o struct sigaction act, oldact act,sa handler= exit_proc act. sa flags =SA ONESHOT sigaction(SIGTERM, &act, NULL) sigaction(SIGINT, &act, NULL) sigaction(sIGHUP, &act, NULL); 检测并启动未启动的程序 moniter_ run(must run proc, must run slze) eturn null int creat and run moniter(char * file) 开线程: pthread_t moniter_ thread if(pthread_create(&moniter_thread, NULL, run_moniter, file)==0) printf("thread create Ok, check thread start \n") return printf( thread check create Errin"): return -I 零零零零享享事职增零半非寥零享半容零摩率率零享剩率容半半享零半率零半率零率辱寒零享 要 IsDigit 参茶爹数a字符串的地址 *判断字符串是否有纯数字字符组成 春客是纯数字返回1,不是纯数字返回0 喜非要串思率串串串串家串润串串串串串串毒毒喜串串最率毒串串踪串率串串非球毒串妆串串毒串串影零串串毒事串 static int IsDigit[char aD) Int size ∥得到当前字符串的长度 size= strlen(a: ∥若字符串长度为0,则直接返回0:即宇符串为空则返回0: if(size ==0) return 0; ∥循环遍历整个字符串 forli=0; i< Size; i++) ∥如果字符小于字符0,或者大于字符9,则返回0 if(ai]<ol ai>9) retum ∥走到这一步说明字符串由字符09组成,返回1 return l; 主进程源文件:man,c main.c #include"process, h Include <stdio. h> include <stdlib h> 甲 include< unistd. I> 甲 nclude< signal> 却 nclude <sys/ ypes,h include <sys/stat. h> 甲 include< tenth> int main(void) creat_and_run_moniter("proclistini") while(l) sleep(D) turn 以上内容是程序全部源文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值