killed 是什么意思,作为在 Fedora linux 中加载程序的响应?

What does killed mean, as response to loading a program in Fedora linux?

我有一个结构简单的汇编程序,一个text段和一个bss段。类似的程序已经由我编写了十多年。它是一个 Forth 编译器,我和小精灵一起玩header。 我已经习惯了,如果我把 elf header 搞砸了,我就无法启动程序,加载程序甚至在出现段错误之前就说 "killed"。

但现在我有一个 Fedora 版本 6 linux 的用户,他执行以下操作:

as -32 lina.s
ld a.out -N -melf_i386 -o lina
./lina

并收到消息 "killed" 和 137 作为 'echo $?'

的结果

很明显这个程序只使用官方工具,这样elf header 至少应该是有效的。 在其他系统(如我的 ubuntu 或 Debian 系统)上执行完全相同的程序会导致程序正常运行。结果程序的 objdumps 至少在段映射方面是相同的。

请告诉我这里发生了什么,我不知道如何解决这个问题。

我想强调的是,可能没有指令被执行,即 gdb 拒绝 运行 它。像这样

(gdb) run
Starting program: /home/gerard/Desktop/lina-5.1/glina32
Warning:
Cannot insert breakpoint -2.
Error accessing memory address 0x8048054: Input/output error.

(gdb)

您可以从使用 strace 开始,查看可执行文件在自行终止之前发出了哪些系统调用(如果有的话)。

查看系统调用通常会找到一条线索,说明问题出在哪里。

如果您尝试在 32 位 Linux 上 运行 64 位程序,则会出现相同的信息性消息 "killed"。所以我的解释是它是来自 shell 的消息,如果它试图加载一个程序,但不知何故没能 运行 它。

消息由bash打印,根据终止进程的信号编号。 "Killed"表示进程收到SIGKILL:

$ pgrep cat  # check that there aren't other cat processes running that you might not want to kill while doing this

$ cat         # in another terminal, pkill cat
Terminated
$ cat         # in another terminal, pkill -9 cat  (or pkill -KILL cat)
Killed
$ cat         #  pkill -QUIT cat   or hit control-\
Quit (core dumped)
$ cat         #  pkill -STOP cat  or hit control-z

[1]+  Stopped                 cat
$ fg
cat                 # pkill -BUS cat
Bus error (core dumped)
$ cat               # pkill -PWR cat
Power failure

Bash 不会为 SIGINT 打印任何内容,因为那是 control-C 发送的内容。

运行 kill -l 列出信号缩写。

$ strace cat            # then pkill -KILL cat
... dynamic library opening and mapping, etc. etc.
read(0,  <unfinished ...>)              = ?
+++ killed by SIGKILL +++
Killed

我无法用 as -32 hello.s / ld -N -melf_i386 重现您的问题来制作我的内核不会 运行 或立即接收 SIGKILL 的可执行文件。

使用 gcc -m32 -c / ld -Ngcc -m32 -E hello.S > hello.s && as -32,我得到一个打印 Hello World 的二进制文件(使用 sys_write 和 sys_exit)。

// hello.S   a simple example I had lying around
// Use  gcc -m32 -E hello.S > hello.s to create input for as -32   
#include <asm/unistd.h>
#include <syscall.h>
#define STDOUT 1

.data               # should really be .rodata
hellostr:
    .ascii "hello wolrd\n";
helloend:

.text
.globl _start

_start:
    movl $(SYS_write) , %eax  //ssize_t write(int fd, const void *buf, size_t count);
    movl $(STDOUT) , %ebx
    movl $hellostr , %ecx
    movl $(helloend-hellostr) , %edx
    int [=12=]x80

    movl $(SYS_exit), %eax //void _exit(int status);
    xorl %ebx, %ebx
    int [=12=]x80

    ret

在极少数情况下,如果在进程尝试执行新程序时发生错误,Linux 内核将向该进程发送 SIGKILL 信号,而不是 return 发出错误。该信号将导致 shell 打印“Killed”,而不是像“out of memory”这样更有用的错误消息。您创建的可执行文件的某些内容触发了一个错误,内核只能通过终止试图执行它的进程来恢复该错误。

通常 shells 通过进行两个系统调用来执行程序:forkexecve。第一个系统调用创建一个新进程,但不加载新的可执行文件。相反,fork 系统调用复制了调用它的进程。第二个系统调用加载一个新的可执行文件但不创建一个新进程。相反,进程中的程序 运行 被可执行文件中的新程序替换。

在执行execve系统调用的过程中,内核需要丢弃进程地址space之前的内容,以便用一个全新的地址完全替换它。在这一点之后,execve 系统调用不能再 return 调用它的程序的错误代码,因为该程序不再存在。如果在此之后发生阻止可执行文件加载的错误,则内核别无选择,只能终止进程。

此行为记录在 Linux execve(2) 手册页中:

In most cases where execve() fails, control returns to the original executable image, and the caller of execve() can then handle the error. However, in (rare) cases (typically caused by resource exhaustion), failure may occur past the point of no return: the original executable image has been torn down, but the new image could not be completely built. In such cases, the kernel kills the process with a SIGKILL signal.