Ⅰ linux:怎么查看一个进程的详细的启动时间
在Linux下,如果需要查看进程的启动时间, 常用ps aux命令,但是ps aux命令的缺陷在于只能显示时间到年、日期、或者时间, 无法具体到年月日时分秒。如果需要查看某个进程的具体启动时间, 使用 ps -p PID -o lstart, 其中,PID为某个进程的进程ID号。如下所示, 显示系统中所有httpd进程的具体启动时间。 1.for pid in $(pgrep httpd); do echo -n ${pid} ; ps -p ${pid} -o lstart | grep -v START ; done 2.301 Mon Aug 27 11:21:59 2012 3.344 Mon Aug 27 11:33:13 2012 12.27903 Wed Jun 20 22:50:47 2012
Ⅱ Handler消息机制(一):Linux的epoll机制
在linux 没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序。在linux新的内核中,有了一种替换它的机制,就是epoll。
相比select模型, poll使用链表保存文件描述符,因此没有了监视文件数量的限制 ,但其他三个缺点依然存在。
假设我们的服务器需要支持100万的并发连接,则在__FD_SETSIZE 为1024的情况下,则我们至少需要开辟1k个进程才能实现100万的并发连接。除了进程间上下文切换的时间消耗外,从内核/用户空间大量的无脑内存拷贝、数组轮询等,是系统难以承受的。因此,基于select模型的服务器程序,要达到10万级别的并发访问,是一个很难完成的任务。
由于epoll的实现机制与select/poll机制完全不同,上面所说的 select的缺点在epoll上不复存在。
设想一下如下场景:有100万个客户端同时与一个服务器进程保持着TCP连接。而每一时刻,通常只有几百上千个TCP连接是活跃的(事实上大部分场景都是这种情况)。如何实现这样的高并发?
在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll一般只能处理几千的并发连接。
epoll的设计和实现与select完全不同。epoll通过在Linux内核中申请一个简易的文件系统(文件系统一般用什么数据结构实现?B+树)。把原先的select/poll调用分成了3个部分:
1)调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
2)调用epoll_ctl向epoll对象中添加这100万个连接的套接字
3)调用epoll_wait收集发生的事件的连接
如此一来,要实现上面说是的场景,只需要在进程启动时建立一个epoll对象,然后在需要的时候向这个epoll对象中添加或者删除连接。同时,epoll_wait的效率也非常高,因为调用epoll_wait时,并没有一股脑的向操作系统复制这100万个连接的句柄数据,内核也不需要去遍历全部的连接。
当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。eventpoll结构体如下所示:
每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。
而所有 添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法 。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。
在epoll中,对于每一个事件,都会建立一个epitem结构体,如下所示:
当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。
epoll结构示意图
通过红黑树和双链表数据结构,并结合回调机制,造就了epoll的高效。
events可以是以下几个宏的集合:
EPOLLIN:触发该事件,表示对应的文件描述符上有可读数据。(包括对端SOCKET正常关闭);
EPOLLOUT:触发该事件,表示对应的文件描述符上可以写数据;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP: 表示对应的文件描述符被挂断;
EPOLLET:将EPOLL设为边缘触发(EdgeTriggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT: 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。
示例:
ET(EdgeTriggered) :高速工作模式,只支持no_block(非阻塞模式)。在此模式下,当描述符从未就绪变为就绪时,内核通过epoll告知。然后它会假设用户知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到某些操作导致那个文件描述符不再为就绪状态了。(触发模式只在数据就绪时通知一次,若数据没有读完,下一次不会通知,直到有新的就绪数据)
LT(LevelTriggered) :缺省工作方式,支持blocksocket和no_blocksocket。在LT模式下内核会告知一个文件描述符是否就绪了,然后可以对这个就绪的fd进行IO操作。如果不作任何操作,内核还是会继续通知!若数据没有读完,内核也会继续通知,直至设备数据为空为止!
1.我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符
2. 这个时候从管道的另一端被写入了2KB的数据
3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作
4. 然后我们读取了1KB的数据
5. 调用epoll_wait(2)……
ET工作模式:
如果我们在第1步将RFD添加到epoll描述符的时候使用了EPOLLET标志,在第2步执行了一个写操作,第三步epoll_wait会返回同时通知的事件会销毁。因为第4步的读取操作没有读空文件输入缓冲区内的数据,因此我们在第5步调用epoll_wait(2)完成后,是否挂起是不确定的。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
只有当read(2)或者write(2)返回EAGAIN时(认为读完)才需要挂起,等待。但这并不是说每次read()时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read()返回的读到的数据长度小于请求的数据长度时(即小于sizeof(buf)),就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。
LT工作模式:
LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll(2),并且无论后面的数据是否被使用,因此他们具有同样的职能。
当调用 epoll_wait检查是否有发生事件的连接时,只是检查 eventpoll对象中的 rdllist双向链表是否有 epitem元素而已,如果 rdllist链表不为空,则把这里的事件复制到用户态内存中,同时将事件数量返回给用户。因此,epoll_wait的效率非常高。epoll_ctl在向 epoll对象中添加、修改、删除事件时,从 rbr红黑树中查找事件也非常快,也就是说,epoll是非常高效的,它可以轻易地处理百万级别的并发连接。
1.减少用户态和内核态之间的文件句柄拷贝;
2.减少对可读可写文件句柄的遍历。
https://cloud.tencent.com/developer/information/linux%20epoll%E6%9C%BA%E5%88%B6
https://blog.csdn.net/u010657219/article/details/44061629
https://jiahao..com/s?id=1609322251459722004&wfr=spider&for=pc
Ⅲ 如何在Linux用户和内核空间中进行动态跟踪
你不记得如何在代码中插入探针点了吗? 没问题!了解如何使用uprobe和kprobe来动态插入它们吧。 基本上,程序员需要在源代码汇编指令的不同位置插入动态探针点。
探针点
探针点是一个调试语句,有助于探索软件的执行特性(即,执行流程以及当探针语句执行时软件数据结构的状态)。printk是探针语句的最简单形式,也是黑客用于内核攻击的基础工具之一。
因为它需要重新编译源代码,所以printk插入是静态的探测方法。内核代码中重要位置上还有许多其他静态跟踪点可以动态启用或禁用。 Linux内核有一些框架可以帮助程序员探测内核或用户空间应用程序,而无需重新编译源代码。Kprobe是在内核代码中插入探针点的动态方法之一,并且uprobe在用户应用程序中执行此操作。
使用uprobe跟踪用户空间
可以通过使用thesysfs接口或perf工具将uprobe跟踪点插入用户空间代码。
使用sysfs接口插入uprobe
考虑以下简单测试代码,没有打印语句,我们想在某个指令中插入探针:
[source,c\n.test.c
#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>
编译代码并找到要探测的指令地址:
# gcc -o test test.\n# objmp -d test
假设我们在ARM64平台上有以下目标代码:
0000000000400620 <func_1>: 400620\t90000080\tadr\tx0, 410000 <__FRAME_END__+0xf6f8>
并且我们想在偏移量0x620和0x644之间插入探针。执行以下命令:
# echo 'p:func_2_entry test:0x620' > /sys/kernel/debug/tracing/uprobe_event\n# echo 'p:func_1_entry test:0x644' >> /sys/kernel/debug/tracing/uprobe_event\n# echo 1 > /sys/kernel/debug/tracing/events/uprobes/enable# ./test&
在上面的第一个和第二个echo语句中,p告诉我们这是一个简单的测试。(探测器可以是简单的或返回的。)func_n_entry是我们在跟踪输出中看到的名称,名称是可选字段,如果没有提供,我们应该期待像p_test_0x644这样的名字。test 是我们要插入探针的可执行二进制文件。如果test 不在当前目录中,则需要指定path_to_test / test。
0x620或0x640是从程序启动开始的指令偏移量。请注意>>在第二个echo语句中,因为我们要再添加一个探针。所以,当我们在前两个命令中插入探针点之后,我们启用uprobe跟踪,当我们写入events/ uprobes / enable时,它将启用所有的uprobe事件。程序员还可以通过写入在该事件目录中创建的特定事件文件来启用单个事件。一旦探针点被插入和启用,每当执行探测指令时,我们可以看到一个跟踪条目。
读取跟踪文件以查看输出:
# cat /sys/kernel/debug/tracing/trac\n# tracer: no\n\n# entries-in-buffer/entries-written: 8/8\n#P:\n\n# _-----=> irqs-of\n# / _----=> need-resche\n# | / _---=> hardirq/softir\n# || / _--=> preempt-dept\n# ||| / dela\n# TASK-PID CP\n# |||| TIMESTAMP FUNCTION# | | | |||| | |
我们可以看到哪个CPU完成了什么任务,什么时候执行了探测指令。
返回探针也可以插入指令。当返回该指令的函数时,将记录一个条目:
# echo 0 > /sys/kernel/debug/tracing/events/uprobes/enabl\n# echo 'r:func_2_exit test:0x620' >> /sys/kernel/debug/tracing/uprobe_event\n# echo 'r:func_1_exit test:0x644' >> /sys/kernel/debug/tracing/uprobe_event\n# echo 1 > /sys/kernel/debug/tracing/events/uprobes/enable
这里我们使用r而不是p,所有其他参数是相同的。请注意,如果要插入新的探测点,需要禁用uprobe事件:
test-3009 [002] .... 4813.852674: func_1_entry: (0x400644)
上面的日志表明,func_1返回到地址0x4006b0,时间戳为4813.852691。
# echo 0 > /sys/kernel/debug/tracing/events/uprobes/enabl\n# echo 'p:func_2_entry test:0x630' > /sys/kernel/debug/tracing/uprobe_events count=%x\n# echo 1 > /sys/kernel/debug/tracing/events/uprobes/enabl\n# echo > /sys/kernel/debug/tracing/trace# ./test&
当执行偏移量0x630的指令时,将打印ARM64 x1寄存器的值作为count =。
输出如下所示:
test-3095 [003] .... 7918.629728: func_2_entry: (0x400630) count=0x1
使用perf插入uprobe
找到需要插入探针的指令或功能的偏移量很麻烦,而且需要知道分配给局部变量的CPU寄存器的名称更为复杂。 perf是一个有用的工具,用于帮助引导探针插入源代码中。
除了perf,还有一些其他工具,如SystemTap,DTrace和LTTng,可用于内核和用户空间跟踪;然而,perf与内核配合完美,所以它受到内核程序员的青睐。
# gcc -g -o test test.c# perf probe -x ./test func_2_entry=func_\n# perf probe -x ./test func_2_exit=func_2%retur\n# perf probe -x ./test test_15=test.c:1\n# perf probe -x ./test test_25=test.c:25 numbe\n# perf record -e probe_test:func_2_entry -e\nprobe_test:func_2_exit -e probe_test:test_15\n-e probe_test:test_25 ./test
如上所示,程序员可以将探针点直接插入函数start和return,源文件的特定行号等。可以获取打印的局部变量,并拥有许多其他选项,例如调用函数的所有实例。 perf探针用于创建探针点事件,那么在执行./testexecutable时,可以使用perf记录来探测这些事件。当创建一个perf探测点时,可以使用其他录音选项,例如perf stat,可以拥有许多后期分析选项,如perf脚本或perf报告。
使用perf脚本,上面的例子输出如下:
# perf script
使用kprobe跟踪内核空间
与uprobe一样,可以使用sysfs接口或perf工具将kprobe跟踪点插入到内核代码中。
使用sysfs接口插入kprobe
程序员可以在/proc/kallsyms中的大多数符号中插入kprobe;其他符号已被列入内核的黑名单。还有一些与kprobe插入不兼容的符号,比如kprobe_events文件中的kprobe插入将导致写入错误。 也可以在符号基础的某个偏移处插入探针,像uprobe一样,可以使用kretprobe跟踪函数的返回,局部变量的值也可以打印在跟踪输出中。
以下是如何做:
; disable all events, just to insure that we see only kprobe output in trace\n# echo 0 > /sys/kernel/debug/tracing/events/enable; disable kprobe events until probe points are inseted\n# echo 0 > /sys/kernel/debug/tracing/events/kprobes/enable; clear out all the events from kprobe_events\n to insure that we see output for; only those for which we have enabled
[root@pratyush ~\n# more /sys/kernel/debug/tracing/trace# tracer: no\n\n# entries-in-buffer/entries-written: 9037/9037\n#P:8\n# _-----=> irqs-of\n# / _----=> need-resche\n# | / _---=> hardirq/softirq#\n|| / _--=> preempt-depth#\n ||| / delay# TASK-PID CPU#\n |||| TIMESTAMP FUNCTION#\n | | | |||| | |
使用perf插入kprobe
与uprobe一样,程序员可以使用perf在内核代码中插入一个kprobe,可以直接将探针点插入到函数start和return中,源文件的特定行号等。程序员可以向-k选项提供vmlinux,也可以为-s选项提供内核源代码路径:
# perf probe -k vmlinux kfree_entry=kfre\n# perf probe -k vmlinux kfree_exit=kfree%retur\n# perf probe -s ./ kfree_mid=mm/slub.c:3408 \n# perf record -e probe:kfree_entry -e probe:kfree_exit -e probe:kfree_mid sleep 10
使用perf脚本,以上示例的输出:
关于Linux命令的介绍,看看《linux就该这么学》,具体关于这一章地址3w(dot)linuxprobe/chapter-02(dot)html
Ⅳ linux中进程 kacpid, kblockd是什么
守护进程及调度进程,以下是摘录的一些常用进程的说明:
/sbin/init 内核启动的第一个用户级进程,引导用户空间服务
[kthreadd] 内核线程管理
[migration/0] 用于进程在不同的CPU间迁移
[ksoftirqd/0] 内核调度/管理第0个CPU软中断的守护进程
[migration/1] 管理多核心
[ksoftirqd/1] 内核调度/管理第1个CPU软中断的守护进程
[events/0] 处理内核事件守护进程
[events/1] 处理内核事件守护进程
[cpuset] 在每个处理器上单独运行进程,通过文件系统实现
[khelper] 内核帮助进程
[netns] 网络仿真器,模拟网络环境
[async/mgr] 异步加密管理进程
[pm] 包管理
[sync_supers] 特权同步,将缓冲区文件强制写入硬盘
[bdi-default] JTAG调试器默认进程
[kintegrityd/0] 内核完整性检查
[kintegrityd/1] 内核完整性检查
[kblockd/0] 管理磁盘块读写
[kblockd/1] 管理磁盘块读写
[kacpid] 高级配置和电源管理接口
[kacpi_notify] acpi进程的通知进程
[kacpi_hotplug] acpi热插拔管理
[ata/0] ATA硬盘接口管理
[ata/1] ATA硬盘接口管理
[ata_aux] ATA硬盘接口管理
[khubd] 内核的usb hub
[kseriod] 内核线程
[kswapd0] 内存回收,确保系统空闲物理内存的数量在一个合适的范围
[ksmd] 作为内核中的守护进程存在,它定期执行页面扫描,识别副本页面并合并副本,释放这些页面以供它用
[aio/0] 代替用户进程管理io
[aio/1] 代替用户进程管理io
[ecryptfs-kthrea] 加密系统
[crypto/0] 提供加密解密相关函数
[crypto/1] 提供加密解密相关函数
[scsi_eh_0] scsi设备
[scsi_eh_1] scsi设备
[scsi_eh_2] scsi设备
[scsi_eh_3] scsi设备
[kpsmoused] 内核鼠标支持
[kjournald] Ext3文件系统的日志管理
[kjournald] Ext3文件系统的日志管理
[flush-1:0] 释放存储在缓存区中的数据
[flush-1:1] 释放存储在缓存区中的数据
[flush-1:2] 释放存储在缓存区中的数据
[flush-1:3] 释放存储在缓存区中的数据
[flush-1:4] 释放存储在缓存区中的数据
[flush-1:5] 释放存储在缓存区中的数据
[flush-1:6] 释放存储在缓存区中的数据
[flush-1:7] 释放存储在缓存区中的数据
[flush-1:8] 释放存储在缓存区中的数据
[flush-1:9] 释放存储在缓存区中的数据
[flush-1:10] 释放存储在缓存区中的数据
[flush-1:11] 释放存储在缓存区中的数据
[flush-1:12] 释放存储在缓存区中的数据
[flush-1:13] 释放存储在缓存区中的数据
[flush-1:14] 释放存储在缓存区中的数据
[flush-1:15] 释放存储在缓存区中的数据
[flush-8:0] 释放存储在缓存区中的数据
[kjournald] Ext3文件系统的日志管理
[loop0] 负责对loop设备进行操作
[loop1] 负责对loop设备进行操作
[loop2] 负责对loop设备进行操作
[kd] 内核拷贝线程
[ext4-dio-unwrit] Ext4文件系统相关线程
upstart-udev-bridge --daemon 一个守护进程,负责接收udev信息
udevd --daemon 一个守护进程,在向udev提交之前重新订制热插拔事件,从而避免各种各样的竞争条件
/usr/sbin/restorecond 用于给SELinux监测和重新加载正确的文件上下文
/sbin/auditd 审计守护进程
/sbin/audispd 审计调度进程
[kauditd] 内核审核守护进程
/sbin/getty -8 38400 tty4 等待用户从tty4登录
/sbin/getty -8 38400 tty5 等待用户从tty5登录
/sbin/getty -8 38400 tty2 等待用户从tty2登录
/sbin/getty -8 38400 tty3 等待用户从tty3登录
/sbin/getty -8 38400 tty6 等待用户从tty6登录
acpid -c /etc/acpi/events -s /var/run/acpid.socket 一个用户空间的服务进程,它充当Linux内核与应用程序之间通信的接口
cron 守护进程,周期地运行用户调度的任务
/sbin/getty -8 38400 tty1 等待用户从tty1登录
X :0 -br vt7 -nolisten tcp Xsever
[flush-252:0] 释放存储在缓存区中的数据
lwm 窗口管理器
fcitx 输入法
su 切换root用户
bash 终端
ps x 查看当前用户的进程
Ⅳ linux命令获取进程运行时长,不足一小时,如何将时间输出为:时分秒格式。
使用 ps命令 ,命令执行参数及运行结果如下:
#ps -A -opid,stime,etime,args
结果:
root:src# ps -A -opid,stime,etime,args
PID STIME ELAPSED COMMAND
1 09:21 08:56:14 init [3]
2 09:21 08:56:14 [migration/0]
3 09:21 08:56:14 [ksoftirqd/0]
4 09:21 08:56:14 [watchdog/0]
5 09:21 08:56:14 [events/0]
6 09:21 08:56:14 [khelper]
7 09:21 08:56:14 [kthread]
10 09:21 08:56:14 [kblockd/0]
11 09:21 08:56:14 [kacpid]
47 09:21 08:56:14 [cqueue/0]
说明:PID指的是进程ID号,STIME即启动时间,ELAPSED 即运行时间。