Stable Diffusion是2022年发布的深度学习文生图模型​(英语)。它主要用于根据文本的描述产生详细图像,尽管它也可以应用于其他任务,如内补绘制、外补绘制,以及在提示词​(英语)指导下产生图生图的翻译。

它是一种潜在​(粤语)扩散模型,由慕尼黑大学的CompVis研究团体开发的各种生成性人工神經网络。它是由初创公司StabilityAI,CompVis与Runway合作开发的,并得到EleutherAI和LAION​(英语)的支持。 截至2022年10月,StabilityAI筹集了1.01亿美元的资金。

Stable Diffusion的代码和模型权重已公开发布,可以在大多数配备有适度GPU的电脑硬件上运行。而以前的专有文生图模型(如DALL-E和Midjourney)只能通过云计算服务访问。

Stable Diffusion模型支持通过使用提示词来产生新的图像,描述要包含或省略的元素,以及重新绘制现有的图像,其中包含提示词中描述的新元素(该过程通常被称为“指导性图像合成”(guided image synthesis))通过使用模型的扩散去噪机制(diffusion-denoising mechanism)。此外,该模型还允许通过提示词在现有的图中进内联补绘制和外补绘制来部分更改,当与支持这种功能的用户界面使用时,其中存在许多不同的开源软件。

Stable Diffusion建议在10GB以上的VRAM下运行, 但是VRAM较少的用户可以选择以float16的精度加载权重,而不是默认的float32,以降低VRAM使用率。

由于艺术风格和构图不受著作权保护,因此通常认为使用Stable Diffusion生成艺术品图像的用户不应被视为侵犯视觉相似作品的著作权, 但是如果生成的图像中所描述的真人被使用,他们仍然受到人格权的保护,[31] 而且诸如可识别的品牌标识等知识产权仍然受到著作权保护。尽管如此,艺术家们表示担心Stable Diffusion等模型的广泛使用最终可能导致人类艺术家以及摄影师、模特、电影摄影师和演员逐渐失去与基于人工智能的竞争对手的商业可行性。

与其他公司的类似机器学习图像合成产品相比,Stable Diffusion在用户可能产生的内容类型方面明显更加宽容,例如暴力或性暴露的图像。

StabilityAI的首席执行官Emad Mostaque解决了该模型可能被用于滥用目的的担忧,他解释说:“人们有责任了解他们在操作这项技术时是否符合道德、道德和法律”,将Stable Diffusion的能力交到公众手中会使该技术在整体上提供净收益,即使有潜在的负面后果。此外,Mostaque认为,Stable Diffusion的开放可用性背后的意图是结束大公司对此类技术的控制和主导地位,他们之前只开发了封闭的人工智能系统进行图像合成。

Stable Diffusion是在LAION-5B的图片和标题对上训练的,LAION-5B是一个公开的数据集,源自从网络上抓取的Common Crawl​(英语)数据。
该模型是在亚马逊云计算服务上使用256个NVIDIA A100 GPU训练,共花费15万个GPU小时,成本为60万美元。

stable diffusion github
https://github.com/CompVis/stable-diffusion

安装stable diffusion
官方项目并不适合我们这些新手直接使用,好在有一些基于 stable-diffusion 封装的 webui 开源项目,可以通过界面交互的方式来使用 stable-diffusion,极大的降低了使用门槛

https://github.com/AUTOMATIC1111/stable-diffusion-webui

AUTOMATIC1111 的 stable-diffusion-webui 是目前功能最多最好用的,强烈推荐,下面就来介绍如何使用它。

开源项目目前的更新频率很快,会不定期的修复一些 bug 或加入一些新功能,所以建议可以时常 git pull 拉取最新代码。

安装使用参考这个:
https://juejin.cn/post/7204356282588282940

常见错误:
ImportError: Using SOCKS proxy, but the 'socksio' package is not installed. Make sure to install httpx using pip install httpx[socks].

ERROR: Could not install packages due to an OSError: Missing dependencies for SOCKS support.

“ValueError: Unable to determine SOCKS version from socks://127.0.0.1:10008/”。

解决方法:

unset all_proxy && unset ALL_PROXY
export all_proxy="socks5://127.0.0.1:10008"

最后,到这儿就表示运行成功了
Creating model from config: /home/hesy/stable-diffusion-webui/configs/v1-inference.yaml
LatentDiffusion: Running in eps-prediction mode
DiffusionWrapper has 859.52 M params.
Applying cross attention optimization (Doggettx).
Textual inversion embeddings loaded(0):
Model loaded in 2.3s (create model: 0.5s, apply weights to model: 0.5s, apply half(): 0.4s, load VAE: 0.6s, move model to device: 0.3s).
Running on local URL: http://127.0.0.1:7860

To create a public link, set share=True in launch().

在网页打开http://127.0.0.1:7860就可以测试了。

郑静

在直来直去的网络时代,人们为何仍离不开古诗词?

https://www.ftchinese.com/story/001098917?full=y

吴晓波在年初2023年的跨年演讲中,提到2022年的全民偶像是一位中国古代诗词作者,苏东波。跌宕起伏的一年,让苏东坡的诗词,苏东坡的书画,甚至是苏东坡的人生故事,都成为了津津乐道的话题。人们似乎从来没有如此爱苏东坡,爱他的诗词,不管是别离还是思念,不管是坦途还是逆境,总能找到一句诗词贴合当下的情绪。不光是苏东坡,这些年古诗词不断被强调重视,从学生应考到全民追捧,从电视节目到商业文化运作,它已经成为一种文化符号,频繁出现在生活中。是回归,也是一场和今日生活的双向奔赴。

对大多数人来说,读诗是一件十分遥远的事情。离开了学校,离开了文科考试,诗也就离开了生活。但它又是一种根深蒂固的记忆,十分容易被唤醒。只要给点阳光和雨露,就能当春乃发生。

很多人小时候都有这样的经历,在学龄前就被大人教几首古诗,为入学打些基础。最终这些诗几乎成了肌肉记忆,久久不会忘记。等自己做父母,孩子到了牙牙学语的时候,也同样会教他们背两句“白日依山尽,黄河入海流”。学龄前的孩子会背诗,能多背诗,成为聪明好学的标志,说明这个孩子记忆力好,口齿清楚,以后读书也有望成才。家长在朋友们的表扬声中得到极大的安慰和满足,也有了继续教学的动力。

唐诗节奏感强,韵律好,像儿歌一样适合孩子朗朗上口,若是家长有心,能多讲解一下诗中的含义,确实对于孩子启蒙是非常有帮助的。中国古代讲究“书读百遍,其意自现”。还有一种说法是“熟读唐诗三百首,不会做来也会诌”。诵读,是学习的第一步,也是非常重要的一步。

2016年央视推出《中国诗词大会》,快速得将唐诗宋词再次推到大众的视线内,到今年已经播出了八季。在节目的前两季,出题的方向多在考察选手的记忆力,尤其是最后的飞花令环节,比拼的就是诗词储备量和临场发挥。所以出挑的选手大多年轻,记忆力好,又有丰富的比赛经验。几场下来,观众戏称这节目应该叫“中国诗词背诵大会”。节目播出后,带动了一场诗词热,尤其是各地的中小学,纷纷举办各种级别的诗词比赛。一来是为节目输送优质选手,二来也符合新的教材改革需要,让孩子们更多地接触古诗词,它已经成为考试中的重要考点。但背诵和记忆,终究不是诗词的全部。“知来路,明去意”,才能让这诗意走得更长远。

古诗词作为中国传统文化的重要组成部分,一度成为国学的代名词。在最火热的时期,和诗词大会一起走红的文化类节目还有很多,比如“国家宝藏”,“典籍里的中国”,“上新了,故宫”。这些节目都在不遗余力地普及中国传统文化,在娱乐中肩负起树立文化自信的任务。很多年前,文人士大夫们潜心编排的诗句,将日常万物赋予了特殊的文学意象,这些文学意象历经千百年,又总能和时下的生活找到一种对应,也许就是这种对应,让诗词在不同时代的走红都显得并不违和。唐诗宋词里的春花秋月,折柳种菊,字字句句都对应着独特的寓意。它含蓄温婉,和直来直去的网络用语完全不同,是一种久违的“文艺感”,这种文艺感即亲切又陌生。

一方面大家用诗词来注解生活,朋友圈里的二十四节气、传统节日,常常会选用一句古诗词来配图。比如立春时的“雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。” 白露时,惦念起诗经里的“蒹葭苍苍,白露为霜”。去年北京冬奥会上的二十四节气倒计时,将这份诗意定格成一张张壁纸。

另一厢,诗又和远方,神奇地匹配在一起,成为和骨感现实背离的另一种生活状态。当组合出现的时候,没有人去质疑,觉着它们天生就应该是在一起的。那应该是一个有着蓝天、白云,悠然自得的画面,可以畅想自己的未来,可以回忆精彩的过往,诗成了远方的代言人,长短句描述的一切都和身边完全不同,那是一种可望而不可及的状态。

除了婉约风月,诗词中还有励志与疗愈的成分。2022年,苏东坡成为全民追捧的网红,从出版物到短视频,苏轼的那句“一蓑烟雨任平生”不断被人引用。生活中的艰辛到了无处话凄凉的时候,诗人的竹杖芒鞋给大家开辟了另一条道理,自我心理疏导也许不能解决实际的问题,不能替代青菜和口罩,但它能带来一些勇气去面对那些糟心的状态。当我们无法改变外界现状的时候,也只剩下心理建设可以去努力了。“不思量,自难忘”,那些排列好的长短句,恰好浓缩成时下的情绪,胜过千言万语。

同时诗中那份“此心安处是吾乡”的从容淡定,也给生活带来一些希望,最起码是一种宽慰。诗词可以让人暂时脱离现实,这种阅读的愉悦感,比长篇小说或是晦涩的理论书籍来得更直接更亲近。在诗句中构建精神家园,让自己的人生变得有些不同,是每个阅读者最渴望的诗意。

今年诗词大会的亚军获得者朱彦军是一位普通的农民工,他从小热爱诗词,因为家境原因不得不放弃学业,早早外出打工。但诗词是他一生的爱好,还影响着自己的一双儿女。在节目中,他大声告白“我回去以后,一定好好听话好好读诗,好好挣钱,好好爱你”。在他的心中,诗词和家人同样重要,是他一生的爱好。

有了良好的推动力和群众基础,古诗词不出意外地被商业领域借鉴运用。在国潮热中,古诗词成为最好的广告文案,比如国风饮品,在杯子上都会印上两句诗词,让它变得与众不同,成为一杯有灵魂的奶茶。今年的诗词大会中有一场的主题是“燃”,辛弃疾、陆游、岳飞的诗词不断被提及,当人们一起背诵“三千功名尘与土,八前里路云和月”,何尝不是另一种“天涯共此时”。诗意之路漫漫,“知来意,明去意”,这场奔赴才不会错过。

apt -o Acquire::socks::proxy="socks5h://192.168.4.64:10008/" update
apt -o Acquire::http::proxy="socks5h://192.168.4.64:10008/" upgrade
apt -o Acquire::http::proxy="socks5h://192.168.4.64:10008/" install inetutils-telnet

export http_proxy="socks5://192.168.4.64:10008"
export https_proxy="socks5://192.168.4.64:10008"

unset HTTP_PROXY HTTPS_PROXY FTP_PROXY ALL_PROXY NO_PROXY

Writing an apt proxy conf file /etc/apt/apt.conf.d/proxy.conf as below.

Acquire::http::Proxy "socks5h://127.0.0.1:1080";
Acquire::https::Proxy "socks5h://127.0.0.1:1080";
Acquire::socks::Proxy "socks5h://127.0.0.1:1080";

D状态,往往是由于 I/O 资源得不到满足,而引发等待

不可中断状态,表示进程正在跟硬件交互,为了保护进程数据和硬件的一致性,系统不 允许其他进程或中断打断这个进程。进程长时间处于不可中断状态,通常表示系统有 I/O 性能问题。

僵尸进程表示进程已经退出,但它的父进程还没有回收子进程占用的资源。短暂的僵尸 状态我们通常不必理会,但进程长时间处于僵尸状态,就应该注意了,可能有应用程序 没有正常处理子进程的退出。

在内核源码 fs/proc/array.c 里,其文字定义为“ "D (disk sleep)", / 2 / ”(由此可知 D 原是Disk的打头字母),对应着 include/linux/sched.h 里的“ #define TASK_UNINTERRUPTIBLE 2 ”。举个例子,当 NFS 服务端关闭之时,若未事先 umount 相关目录,在 NFS 客户端执行 df 就会挂住整个登录会话,按 Ctrl+C 、Ctrl+Z 都无济于事。断开连接再登录,执行 ps axf 则看到刚才的 df 进程状态位已变成了 D ,kill -9 无法杀灭。正确的处理方式,是马上恢复 NFS 服务端,再度提供服务,刚才挂起的 df 进程发现了其苦苦等待的资源,便完成任务,自动消亡。若 NFS 服务端无法恢复服务,在 reboot 之前也应将 /etc/mtab 里的相关 NFS mount 项删除,以免 reboot 过程例行调用 netfs stop 时再次发生等待资源,导致系统重启过程挂起。

Linux进程状态:D (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态。

与TASK_INTERRUPTIBLE状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号。
绝大多数情况下,进程处在睡眠状态时,总是应该能够响应异步信号的。否则你将惊奇的发现,kill -9竟然杀不死一个正在睡眠的进程了!于是我们也很好理解,为什么ps命令看到的进程几乎不会出现TASK_UNINTERRUPTIBLE状态,而总是TASK_INTERRUPTIBLE状态。

而TASK_UNINTERRUPTIBLE状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了。(参见《linux内核异步中断浅析》)
在进程对某些硬件进行操作时(比如进程调用read系统调用对某个设备文件进行读操作,而read系统调用最终执行到对应设备驱动的代码,并与对应的物理设备进行交互),可能需要使用TASK_UNINTERRUPTIBLE状态对进程进行保护,以避免进程与设备交互的过程被打断,造成设备陷入不可控的状态。这种情况下的TASK_UNINTERRUPTIBLE状态总是非常短暂的,通过ps命令基本上不可能捕捉到。

当 iowait 升高时,进程很可能因为得不到硬件的响应,而长时间处于不可中断状态。从 ps 或者 top 命令的输出中,你可以发现它们都处于 D 状态,也就是不可中断状态 (Uninterruptible Sleep)。

top 和 ps 是最常用的查看进程状态的工具,我们就从 top 的输出开始。下面是一个 top 命令输出的示例,S 列(也就是 Status 列)表示进程的状态。从这个示例里,你可以看到 R、D、Z、S、I 等几个状态,它们分别是什么意思呢?

R 是 Running 或 Runnable 的缩写,表示进程在 CPU 的就绪队列中,正在运行或者正 在等待运行。

D 是 Disk Sleep 的缩写,也就是不可中断状态睡眠(Uninterruptible Sleep),一般 表示进程正在跟硬件交互,并且交互过程不允许被其他进程或中断打断。

Z 是 Zombie 的缩写,如果你玩过“植物大战僵尸”这款游戏,应该知道它的意思。它 表示僵尸进程,也就是进程实际上已经结束了,但是父进程还没有回收它的资源(比如进程的描述符、PID 等)。

S 是 Interruptible Sleep 的缩写,也就是可中断状态睡眠,表示进程因为等待某个事件 而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态。

I 是 Idle 的缩写,也就是空闲状态,用在不可中断睡眠的内核线程上。前面说了,硬件 交互导致的不可中断进程用 D 表示,但对某些内核线程来说,它们有可能实际上并没有 任何负载,用 Idle 正是为了区分这种情况。要注意,D 状态的进程会导致平均负载升 高, I 状态的进程却不会。

当然了,上面的示例并没有包括进程的所有状态。除了以上 5 个状态,进程还包括下面这 2 个状态。

第一个是 T 或者 t,也就是 Stopped 或 Traced 的缩写,表示进程处于暂停或者跟踪状态。

向一个进程发送 SIGSTOP 信号,它就会因响应这个信号变成暂停状态(Stopped);再 向它发送 SIGCONT 信号,进程又会恢复运行(如果进程是终端里直接启动的,则需要你 用 fg 命令,恢复到前台运行)。

而当你用调试器(如 gdb)调试一个进程时,在使用断点中断进程后,进程就会变成跟踪 状态,这其实也是一种特殊的暂停状态,只不过你可以用调试器来跟踪并按需要控制进程 的运行。

另一个是 X,也就是 Dead 的缩写,表示进程已经消亡,所以你不会在 top 或者 ps 命令 中看到它。

不同发行版本下面的ltrace还是存在一些差异,同样使用的是0.7.3版本的ltrace,在ubuntu18及以下,执行ltrace ls 会显示一大堆内容,而在ubuntu20与ubuntu22则基本无内容显示.如下:

ltrace ls

bin  boot  dev    etc  home  lib    lib32  lib64  libx32  media  mnt  opt  proc  root  run    sbin  srv  sys    tmp  usr  var
+++ exited (status 0) +++

在ubuntu18.04上运行,则会出现这样的结果.

ltrace ls
__libc_start_main(0x5661e5c0, 1, 0xfff143c4, 0x566326d0 <unfinished ...>
strrchr("ls", '/')                                                                                         = nil
setlocale(LC_ALL, "")                                                                                      = "C"
bindtextdomain("coreutils", "/usr/share/locale")                                                           = "/usr/share/locale"
textdomain("coreutils")    
...
fflush(0xf7ec7ce0)                                                                                         = 0
fclose(0xf7ec7ce0)                                                                                         = 0
+++ exited (status 0) +++

而ltrace的版本信息都是V0.7.3

ltrace -V
ltrace version 0.7.3.
Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.
This is free software; see the GNU General Public Licence
version 2 or later for copying conditions.  There is NO warranty.

这些都不是重点,重点是知道是怎么样用的就好.要让显示一致,就在ubuntu18及以下的命令运行时,使用ltrace -L 有时可以达到相同的显示效果,可以试试看 ltrace -L -x "getenv" ls -lh

ltrace -x "getenv" ls     
getenv@libc.so.6("LOCPATH")                                                                                = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_IDENTIFICATION")                                                                      = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_MEASUREMENT")                                                                         = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_TELEPHONE")                                                                           = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
...

注意事项:
ltrace 不能用于静态编译链接的程序,只能用于动态库链接的程序.他的作用是Trace library calls of a given program.如果都没有library,你可以需要的是strace.

典型的就是你想去ltrace一个go编译的程序的时候,出现下面这个东西.
Couldn't find .dynsym or .dynstr in "/proc/3596/exe"
哪很有可能就是,你搞错了要trace的对象了.

常用的使用方式:

  1. strace/ltrace 直接加上指令,如: ltrace ls -l。
  2. strace/ltrace 加上-o 選項的參數而導到某個檔案,如: ltrace -o strace.out ls -l。
  3. strace/ltrace 加上-p 選項的參數(Process ID)來觀測某個process的system call/library API,如: ltrace -p 12345。

ltrace常用的参数:

-l 只打印某个库中的调用。
-L 不打印库调用。
-o, --output=file 把输出定向到文件。
-p PID 附着在值为PID的进程号上进行ltrace。
-r 打印相对时间戳。
-s STRLEN 设置打印的字符串最大长度。
-S 显示系统调用。
-t, -tt, -ttt 打印绝对时间戳。
-T 输出每个调用过程的时间开销。
-x 显示指定的函数名,可以用透配符,个人感觉这个好使.

ltrace 把输出保存到文件

root@bf9c8c24edc5:/# ltrace -o /tmp/debug.log -x "gete*" ls
bin  boot  dev    etc  home  lib    lib32  lib64  libx32  media  mnt  opt  proc  root  run    sbin  srv  sys    tmp  usr  var
root@bf9c8c24edc5:/# cat /tmp/debug.log 
getenv@libc.so.6("LOCPATH")                                                                                = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_IDENTIFICATION")                                                                      = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_MEASUREMENT")                                                                         = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_TELEPHONE")                                                                           = nil
getenv@libc.so.6("LANG")                                                                                   = nil
getenv@libc.so.6("LC_ALL")                                                                                 = nil
getenv@libc.so.6("LC_ADDRESS")                                                                             = nil

ltrace 设置打印的长度,默认好像是32字节

ltrace -o /tmp/debug.log -x 'write'  ping -c 4 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=9.12 ms
64 bytes from 223.5.5.5: icmp_seq=2 ttl=114 time=6.52 ms
64 bytes from 223.5.5.5: icmp_seq=3 ttl=114 time=6.57 ms
64 bytes from 223.5.5.5: icmp_seq=4 ttl=114 time=6.26 ms

--- 223.5.5.5 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 6.262/7.118/9.117/1.160 ms
root@bf9c8c24edc5:/# cat /tmp/debug.log 
write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84"..., 49)                                              = 49
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_se"..., 57)                                              = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_se"..., 57)                                              = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_se"..., 57)                                              = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_se"..., 57)                                              = 57
write@libc.so.6(1, "\n", 1)                                                                                = 1
write@libc.so.6(1, "--- 223.5.5.5 ping statistics --"..., 34)                                              = 34
write@libc.so.6(1, "4 packets transmitted, 4 receive"..., 63)                                              = 63
write@libc.so.6(1, "rtt min/avg/max/mdev = 6.262/7.1"..., 50)                                              = 50
+++ exited (status 0) +++

看上面的/tmp/debug.log中,write的数据就没有显示完成,加上-S参数来显示完成试试看.

ltrace -o /tmp/debug.log -x 'write' -s 64  ping -c 4 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.27 ms
64 bytes from 223.5.5.5: icmp_seq=2 ttl=114 time=6.09 ms
64 bytes from 223.5.5.5: icmp_seq=3 ttl=114 time=6.33 ms
64 bytes from 223.5.5.5: icmp_seq=4 ttl=114 time=6.52 ms

--- 223.5.5.5 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3006ms
rtt min/avg/max/mdev = 6.091/6.300/6.519/0.152 ms
root@bf9c8c24edc5:/# cat /tmp/debug.log 
write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)                               = 49
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.27 ms\n", 57)                       = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=2 ttl=114 time=6.09 ms\n", 57)                       = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=3 ttl=114 time=6.33 ms\n", 57)                       = 57
write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=4 ttl=114 time=6.52 ms\n", 57)                       = 57
write@libc.so.6(1, "\n", 1)                                                                                = 1
write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                                              = 34
write@libc.so.6(1, "4 packets transmitted, 4 received, 0% packet loss, time 3006ms\n", 63)                 = 63
write@libc.so.6(1, "rtt min/avg/max/mdev = 6.091/6.300/6.519/0.152 ms\n", 50)                              = 50
+++ exited (status 0) +++

ltrace 加上时间
时间参数就三个,-t, -tt , -ttt,-t就只有时分秒,-tt时分秒带微秒,-ttt就是unixtime带微秒.看结果.

root@bf9c8c24edc5:/# ltrace -o /tmp/debug1.log -x 'write' -s 64 -t  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=7.61 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.613/7.613/7.613/0.000 ms
root@bf9c8c24edc5:/# ltrace -o /tmp/debug2.log -x 'write' -s 64 -tt  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.22 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.220/6.220/6.220/0.000 ms
root@bf9c8c24edc5:/# ltrace -o /tmp/debug3.log -x 'write' -s 64 -ttt  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.25 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.253/6.253/6.253/0.000 ms
root@bf9c8c24edc5:/# cat /tmp/debug1.log 
02:34:59 write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)                      = 49
02:34:59 write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=7.61 ms\n", 57)              = 57
02:34:59 write@libc.so.6(1, "\n", 1)                                                                       = 1
02:34:59 write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                                     = 34
02:34:59 write@libc.so.6(1, "1 packets transmitted, 1 received, 0% packet loss, time 0ms\n", 60)           = 60
02:34:59 write@libc.so.6(1, "rtt min/avg/max/mdev = 7.613/7.613/7.613/0.000 ms\n", 50)                     = 50
02:34:59 +++ exited (status 0) +++
root@bf9c8c24edc5:/# cat /tmp/debug2.log 
02:35:04.164310 write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)               = 49
02:35:04.170973 write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.22 ms\n", 57)       = 57
02:35:04.171221 write@libc.so.6(1, "\n", 1)                                                                = 1
02:35:04.171343 write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                              = 34
02:35:04.171504 write@libc.so.6(1, "1 packets transmitted, 1 received, 0% packet loss, time 0ms\n", 60)    = 60
02:35:04.171697 write@libc.so.6(1, "rtt min/avg/max/mdev = 6.220/6.220/6.220/0.000 ms\n", 50)              = 50
02:35:04.172049 +++ exited (status 0) +++
root@bf9c8c24edc5:/# cat /tmp/debug3.log 
1687142111.348984 write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)             = 49
1687142111.355684 write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.25 ms\n", 57)     = 57
1687142111.355885 write@libc.so.6(1, "\n", 1)                                                              = 1
1687142111.355985 write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                            = 34
1687142111.356126 write@libc.so.6(1, "1 packets transmitted, 1 received, 0% packet loss, time 0ms\n", 60)  = 60
1687142111.356314 write@libc.so.6(1, "rtt min/avg/max/mdev = 6.253/6.253/6.253/0.000 ms\n", 50)            = 50
1687142111.356666 +++ exited (status 0) +++

ltrace 显示每个调用所花的绝对时间就加上-T参数,结果在最右边尖括号内.

ltrace -o /tmp/debug.log -x 'write' -s 64 -ttt -T  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.34 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.343/6.343/6.343/0.000 ms
root@bf9c8c24edc5:/# cat /tmp/debug.log 
1687142203.255801 write@libc.so.6(1, "PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.\n", 49)             = 49 <0.000193>
1687142203.262657 write@libc.so.6(1, "64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=6.34 ms\n", 57)     = 57 <0.000247>
1687142203.262946 write@libc.so.6(1, "\n", 1)                                                              = 1 <0.000103>
1687142203.263084 write@libc.so.6(1, "--- 223.5.5.5 ping statistics ---\n", 34)                            = 34 <0.000124>
1687142203.263241 write@libc.so.6(1, "1 packets transmitted, 1 received, 0% packet loss, time 0ms\n", 60)  = 60 <0.000158>
1687142203.263425 write@libc.so.6(1, "rtt min/avg/max/mdev = 6.343/6.343/6.343/0.000 ms\n", 50)            = 50 <0.000142>
1687142203.263779 +++ exited (status 0) +++

ltrace 显示系统调用就加-S参数

ltrace -o /tmp/debug.log -x 'write' -s 64 -ttt -T -S  ping -c 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=5.94 ms

--- 223.5.5.5 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 5.940/5.940/5.940/0.000 ms
root@bf9c8c24edc5:/# cat /tmp/debug.log 
1687142356.444336 SYS_brk(0)                                                                               = 0x56108d657000 <0.000140>
1687142356.444611 SYS_arch_prctl(0x3001, 0x7ffea0f00a60, 0x7ff8bd4862d0, 13)                               = -22 <0.000116>
1687142356.444924 SYS_access("/etc/ld.so.preload", 04)                                                     = -2 <0.000296>
1687142356.445362 SYS_openat(0xffffff9c, 0x7ff8bd48fb80, 0x80000, 0)                                       = 3 <0.000114>
1687142356.445548 SYS_fstat(3, 0x7ffea0effc60)                                                             = 0 <0.000045>
1687142356.445637 SYS_mmap(0, 6320, 1, 2)                                                                  = 0x7ff8bd468000 <0.000075>
1687142356.445778 SYS_close(3)                                                                             = 0 <0.000065>
...

ltrace 跟踪原理
ltrace的功能是能够跟踪进程的库函数调用,它是如何实现的呢?

ltrace其实也是基于ptrace。我们知道,ptrace能够主要是用来跟踪系统调用,那么它是如何跟踪库函数呢?

首先ltrace打开elf文件,对其进行分析。在elf文件中,出于动态连接的需要,需要在elf文件中保存函数的符号,供连接器使用。具体格式,大家可以参考elf文件的格式。

这样ltrace就能够获得该文件中,所有系统调用的符号,以及对应的执行指令。然后,ltrace将该指令所对应的4个字节,替换成断点。其实现大家可以参考Playing with ptrace, Part II。

这样在进程执行到相应的库函数后,就可以通知到了ltrace,ltrace将对应的库函数打印出来之后,继续执行子进程。

实际上ltrace与strace使用的技术大体相同,但ltrace在对支持fork和clone方面,不如strace。strace在收到frok和clone等系统调用后,做了相应的处理,而ltrace没有。

ltrace 与 strace 的异同
ltrace能够跟踪进程的库函数调用,它会显现出哪个库函数被调用,而strace则是跟踪程序的每个系统调用.ltrace比strace更费时.

strace -o /tmp/debug.log  -s 64 -ttt -T -e 'write'  echo "hello world"
hello world
root@bf9c8c24edc5:/# cat /tmp/debug.log 
1687142834.793675 write(1, "hello world\n", 12) = 12 <0.000010>
1687142834.793840 +++ exited with 0 +++
root@bf9c8c24edc5:/# 
root@bf9c8c24edc5:/# 
root@bf9c8c24edc5:/# ltrace -o /tmp/debug.log  -s 64 -ttt -T -x 'write' echo "hello world"
hello world
root@bf9c8c24edc5:/# cat /tmp/debug.log 
1687142847.965263 write@libc.so.6(1, "hello world\n", 12)                                                  = 12 <0.000124>
1687142847.965520 +++ exited (status 0) +++

最后:
使用ltrace最好不要在生产系统上面搞,会阻塞住当前线程,如果是多线程程序,可能出现各种奇怪的问题.
参考:
https://www.cnblogs.com/machangwei-8/p/10388938.html
https://zhuanlan.zhihu.com/p/107063011
https://blog.csdn.net/fantasy_ARM9/article/details/115892633
https://man7.org/linux/man-pages/man1/ltrace.1.html