为什么刮会冻结?
Why does scraping freeze?
我是运行一个python代码来做一些网络抓取,这意味着代码偶尔会写入(追加)数据到文本文件。有时代码会冻结,但 Shell 中不会显示任何错误消息。
我想知道这更有可能是因为 raspbian 系统不可靠还是因为我的代码有一些隐藏的问题。
一个very good rule of thumb是错误总是在你自己的代码而不是在系统。特别是,如果系统的其余部分是 运行ning(即您可以使用另一个控制台),那么挂起几乎可以肯定是您的程序的错误。
在 shell 中 运行ning 你的进程,尝试按 Ctrl+C 来询问您的进程停止,或 Ctrl+\ 完全退出。您应该会收到一条错误消息,显示您的程序在您终止时所在的位置。假设您的程序是
x = 0
def bar():
return x * 2
def foo():
x = 11
while bar() < 100:
x += 1
foo()
(你能发现错误吗?)我现在 运行 将其与 python program.py
连接,但它挂起并且没有终止。按 Ctrl+C 得到:
^CTraceback (most recent call last):
File "python.py", line 9, in <module>
foo()
File "python.py", line 6, in foo
while bar() < 100:
File "python.py", line 3, in bar
return x * 2
KeyboardInterrupt
^C
是Ctrl+C的直观表示,可以忽略。其余的是一个 stack trace,它显示了当我们 运行 运行它时程序做了什么。为了获得良好的效果,请多次中断您的程序并比较堆栈跟踪以查看它在 "hanging".
时通常在做什么
此外,使用更多调试输出(可能由新开关切换)扩展您的程序,这样您就不需要首先中断它。一个好主意是在连接到网络之前和之后始终输出一些内容。
由于你在网络上,另一端也可能静静地死掉,你的程序可能会等待一段时间来确认发生了什么(而不是因为接收不良或网络高度拥塞而导致速度变慢)网络)。你可以用一个低值调用socket.setdefaulttimeout
,让你的程序提前退出,而不是等待对方说些什么。
您还可以使用各种工具来辅助调试。例如,键入 htop
(sudo apt-get install -y htop
一次安装,如果您还没有安装它,或者 top
也可以)以查看您的程序的进展情况。查看 CPU 负载因子(在最顶部)和列出程序的位置。
说它看起来像这样:
尽管按 CPU 排序(按 F6 排序),我们的程序甚至没有出现在这里,并且 htop 是唯一使用大量 CPU 无论如何。这意味着我们的程序(如果它是 运行ning)卡在 system call 中,即已将控制权交给操作系统。但是由于操作系统也没有使用太多 CPU(它的 CPU 使用被映射为红色),看起来我们正在等待什么!
另一方面,htop 输出我的样子:
您会注意到 program.py
功能突出。这也不是因为我们的程序以任何方式强调操作系统,因为栏基本上都是绿色的!
那么您可能想要调查程序的当前状态,而不是仅仅查看聚合值或终止它。工具很多,但让我们看两个:
strace 实用程序(同样,使用 sudo apt-get install -y strace
安装一次)可以显示程序进行的系统调用。这适用于 每个 程序,而不仅仅是 Python 程序。 运行 它在我们的简单示例程序中产生:
$ strace -o log python program.py
execve("/usr/bin/python", ["python", "program.py"], [/* 47 vars */]) = 0
brk(0) = 0x218f000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbb72742000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
* snip about 1000 lines*
read(3, "x = 0\ndef bar():\n return x * "..., 4096) = 101
lseek(3, 101, SEEK_SET) = 101
brk(0x291d000) = 0x291d000
read(3, "", 4096) = 0
brk(0x2914000) = 0x2914000
close(3) = 0
munmap(0x7f7b9d294000, 4096) = 0
如果你愿意,你也可以 运行 它作为 strace -o logfile python program.py
将输出写入 ./logfile
以便你可以在另一个 shell 中检查它,例如使用文本编辑器。
我们在这里看到 Python 需要 很多 系统调用才刚刚开始,但我们的程序根本不进行系统调用!我们看到,因为最后的系统调用是 Python 读取源代码文件。使用不同的程序,输出可能看起来像
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("93.184.216.34")}, 16) = 0
recvfrom(3,
重要的是最后一行,未完成,表明此时由操作系统控制。操作系统在做什么?好吧,它正在调用 recvfrom
,或者正在等待数据。由于它只是在等待,实际上并没有做太多事情,因此 htop 只会显示可忽略不计的红色条。如果操作系统有错误,htop 现在会显示很多红色。
现在,我们如何知道哪个系统调用来自哪个 Python 语句?为此,我们需要一个 Python 调试器。您的 IDE 可能有一个集成的,但在紧要关头,内置的 pdb 可以工作。让我们 运行 它在我们的(新)程序中:
$ pdb program2.py
> /home/phihag/tmp/Whosebug/program2.py(1)<module>()
-> import socket
(Pdb) next
> /home/phihag/tmp/Whosebug/program2.py(3)<module>()
-> c = socket.create_connection(('example.net', 80))
(Pdb) n
> /home/phihag/tmp/Whosebug/program2.py(4)<module>()
-> while True:
(Pdb) n
> /home/phihag/tmp/Whosebug/program2.py(5)<module>()
-> print(c.recv(1024))
(Pdb) n
使用next
和step
(或简称n
和s
)单步执行程序(对于大型程序,您很可能需要continue
和一个断点)。键入 help pdb
以查看 pdb 帮助信息。在这种情况下,我们看到程序当前挂起的行是第 5 行(print(c.recv(1024))
)。
现在,如果您不明白为什么您的程序挂起,上述调试工具应该为您提供大量信息来创建一个minimal, complete, verifiable example。
一旦你确认那个也挂了,请随意ask a Whosebug question。
我是运行一个python代码来做一些网络抓取,这意味着代码偶尔会写入(追加)数据到文本文件。有时代码会冻结,但 Shell 中不会显示任何错误消息。 我想知道这更有可能是因为 raspbian 系统不可靠还是因为我的代码有一些隐藏的问题。
一个very good rule of thumb是错误总是在你自己的代码而不是在系统。特别是,如果系统的其余部分是 运行ning(即您可以使用另一个控制台),那么挂起几乎可以肯定是您的程序的错误。
在 shell 中 运行ning 你的进程,尝试按 Ctrl+C 来询问您的进程停止,或 Ctrl+\ 完全退出。您应该会收到一条错误消息,显示您的程序在您终止时所在的位置。假设您的程序是
x = 0
def bar():
return x * 2
def foo():
x = 11
while bar() < 100:
x += 1
foo()
(你能发现错误吗?)我现在 运行 将其与 python program.py
连接,但它挂起并且没有终止。按 Ctrl+C 得到:
^CTraceback (most recent call last):
File "python.py", line 9, in <module>
foo()
File "python.py", line 6, in foo
while bar() < 100:
File "python.py", line 3, in bar
return x * 2
KeyboardInterrupt
^C
是Ctrl+C的直观表示,可以忽略。其余的是一个 stack trace,它显示了当我们 运行 运行它时程序做了什么。为了获得良好的效果,请多次中断您的程序并比较堆栈跟踪以查看它在 "hanging".
此外,使用更多调试输出(可能由新开关切换)扩展您的程序,这样您就不需要首先中断它。一个好主意是在连接到网络之前和之后始终输出一些内容。
由于你在网络上,另一端也可能静静地死掉,你的程序可能会等待一段时间来确认发生了什么(而不是因为接收不良或网络高度拥塞而导致速度变慢)网络)。你可以用一个低值调用socket.setdefaulttimeout
,让你的程序提前退出,而不是等待对方说些什么。
您还可以使用各种工具来辅助调试。例如,键入 htop
(sudo apt-get install -y htop
一次安装,如果您还没有安装它,或者 top
也可以)以查看您的程序的进展情况。查看 CPU 负载因子(在最顶部)和列出程序的位置。
说它看起来像这样:
尽管按 CPU 排序(按 F6 排序),我们的程序甚至没有出现在这里,并且 htop 是唯一使用大量 CPU 无论如何。这意味着我们的程序(如果它是 运行ning)卡在 system call 中,即已将控制权交给操作系统。但是由于操作系统也没有使用太多 CPU(它的 CPU 使用被映射为红色),看起来我们正在等待什么!
另一方面,htop 输出我的样子:
您会注意到 program.py
功能突出。这也不是因为我们的程序以任何方式强调操作系统,因为栏基本上都是绿色的!
那么您可能想要调查程序的当前状态,而不是仅仅查看聚合值或终止它。工具很多,但让我们看两个:
strace 实用程序(同样,使用 sudo apt-get install -y strace
安装一次)可以显示程序进行的系统调用。这适用于 每个 程序,而不仅仅是 Python 程序。 运行 它在我们的简单示例程序中产生:
$ strace -o log python program.py
execve("/usr/bin/python", ["python", "program.py"], [/* 47 vars */]) = 0
brk(0) = 0x218f000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbb72742000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
* snip about 1000 lines*
read(3, "x = 0\ndef bar():\n return x * "..., 4096) = 101
lseek(3, 101, SEEK_SET) = 101
brk(0x291d000) = 0x291d000
read(3, "", 4096) = 0
brk(0x2914000) = 0x2914000
close(3) = 0
munmap(0x7f7b9d294000, 4096) = 0
如果你愿意,你也可以 运行 它作为 strace -o logfile python program.py
将输出写入 ./logfile
以便你可以在另一个 shell 中检查它,例如使用文本编辑器。
我们在这里看到 Python 需要 很多 系统调用才刚刚开始,但我们的程序根本不进行系统调用!我们看到,因为最后的系统调用是 Python 读取源代码文件。使用不同的程序,输出可能看起来像
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("93.184.216.34")}, 16) = 0
recvfrom(3,
重要的是最后一行,未完成,表明此时由操作系统控制。操作系统在做什么?好吧,它正在调用 recvfrom
,或者正在等待数据。由于它只是在等待,实际上并没有做太多事情,因此 htop 只会显示可忽略不计的红色条。如果操作系统有错误,htop 现在会显示很多红色。
现在,我们如何知道哪个系统调用来自哪个 Python 语句?为此,我们需要一个 Python 调试器。您的 IDE 可能有一个集成的,但在紧要关头,内置的 pdb 可以工作。让我们 运行 它在我们的(新)程序中:
$ pdb program2.py
> /home/phihag/tmp/Whosebug/program2.py(1)<module>()
-> import socket
(Pdb) next
> /home/phihag/tmp/Whosebug/program2.py(3)<module>()
-> c = socket.create_connection(('example.net', 80))
(Pdb) n
> /home/phihag/tmp/Whosebug/program2.py(4)<module>()
-> while True:
(Pdb) n
> /home/phihag/tmp/Whosebug/program2.py(5)<module>()
-> print(c.recv(1024))
(Pdb) n
使用next
和step
(或简称n
和s
)单步执行程序(对于大型程序,您很可能需要continue
和一个断点)。键入 help pdb
以查看 pdb 帮助信息。在这种情况下,我们看到程序当前挂起的行是第 5 行(print(c.recv(1024))
)。
现在,如果您不明白为什么您的程序挂起,上述调试工具应该为您提供大量信息来创建一个minimal, complete, verifiable example。
一旦你确认那个也挂了,请随意ask a Whosebug question。