先说一个刚刚发现的问题:
前两天打开测试机发现速度非常慢,top一看,发现java占用CPU 99%
查看对应pid,发现这个是新的话单采集程序gather:
#ps -ef | grep java Root 5762 1 99 14:41 pts/0 00:00:01 java -Xmx2048m -jar Gather-jar-with-dependencies.jar |
采集程序的工作是启动4个线程,不断从FTP上读取文件。查看日志,发现FTP上并没有文件,4个线程每隔一段时间去检查一下FTP,所有线程都在正常写日志。看上去一切正常。
查看进程状态:
#cat /proc/5762/status Name: java
扫描二维码关注公众号,回复:
3187592 查看本文章
![]() State: S (sleeping) …… Threads: 5689 …… |
看到threads为5689的时候,真正冒了身冷汗。只启动了4个线程,怎么会出现这么多线程呢?
由于这个进程已经运行了比较久的时间,测试目录又比较多,这个进程是从哪个目录开始的呢?
#ll /proc/9323/cwd lrwxrwxrwx 1 root root 0 Jun 8 11:51 /proc/9323/cwd -> /home/admin/work/BLTransfer/src/gather/20110509_56847_taobao-bn_2 |
到这个目录,查看版本的gather代码,发现FTP调用后在某个返回路径上没有调用close方法。
查看FTP4j源码,发现FTP在调用setAutoNoopTimeout和Login后会调用startAutoNoopTimer()新启一个线程定时处理无操作的情况。此外,在传输数据时FTPDataTransfer也会新开线程。
修改代码后,恢复正常。
PROC简介
上处查看的/proc/5762/status和/proc/9323/cwd正是proc文件系统下的文件。
与其它常见的文件系统不同的是,Proc文件系统并不是真正意义上的文件系统,它存在于内存中,并不占用磁盘空间,它包含一些结构化的目录和虚拟文件,向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。
这些虚拟文件使用查看命令查看时会返回大量信息,但文件本身的大小却会显示为0字节。此外,这些特殊文件中大多数文件的时间及日期属性通常为当前系统时间和日期。
事实上,如ps、top等很多shell命令正是从proc系统中读取信息,且更具可读性。
接下来,将详细介绍proc文件系统中每个文件的信息情况。
一、常见PROC文件含义
/proc目录下,每个进程都会有一个以PID命名的目录,存放关于这个进程的信息,同时还有许多相对固定的目录,以存放与系统相关的信息:
[admin@ccbuqa147160x proc]$ ls /proc/ 1 10 2331 ……(每个进程一个目录,略去) buddyinfo execdomains kallsyms misc stat vmstat bus fb kcore modules swaps xen cmdline filesystems keys mounts sys zoneinfo cpuinfo fs key-users net sysrq-trigger crypto ide kmsg partitions sysvipc devices interrupts loadavg schedstat tty diskstats iomem locks scsi uptime dma ioports mdstat self version driver irq meminfo slabinfo vmcore |
通过这些文件,可以查看大量系统信息,例如CPU信息、内存信息、linux版本信息等:
[admin@ccbuqa147160x ~]$ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 26 model name : Intel(R) Xeon(R) CPU E5504 @ 2.00GHz stepping : 5 cpu MHz : 2000.070 cache size : 4096 KB …… [admin@ccbuqa147160x ~]$ cat /proc/version Linux version 2.6.18-131.el5.customxen (root@cfengine2) (gcc version 4.1.2 20071124 (Red Hat 4.1.2-42)) #1 SMP Tue Sep 15 15:46:11 CST 2009 |
先来看一下进程目录的子目录:
[admin@ccbuqa147160x proc]$ ls /proc/22567 attr cmdline cpuset environ fd loginuid mem mountstats oom_score schedstat stat status wchan auxv coredump_filter cwd exe limits maps mounts oom_adj root smaps statm task |
主要文件的含义总结如下:
文件名 |
内容 |
文件名 |
内容 |
cmdline |
进程启动时的命令行参数 |
root |
指向进程根目录 |
environ |
环境变更的值 |
stat |
进程状态 |
cwd |
当前工作路径 |
status |
进程状态(友好) |
exe |
进程相关的可执行文件 |
stack |
栈记录(需CONFIG_STACKTRACE) |
fd |
文件描述符目录 |
smaps |
Maps扩展,显示每个mapping的内存 |
maps |
可执行文件和库文件图 |
mem |
进程占用的内存 |
例如:以目前的通话服务器程序ECPWORKER为例,它的当前目录和可执行文件相关信息如下:
[admin@ccbuqa147160x proc]$ ll /proc/22559/cwd /proc/22559/exe lrwxrwxrwx 1 admin admin 0 06-09 09:00 /proc/22559/cwd -> /home/admin/work/ecpworker/bin lrwxrwxrwx 1 admin admin 0 06-09 09:00 /proc/22559/exe -> /home/admin/work/ecpworker/bin/ecp-master-worker |
它所有打开的文件描述符为:
[admin@ccbuqa147160x proc]$ ll /proc/22559/fd lr-x------ 1 admin admin 64 06-09 08:58 0 -> /dev/null l-wx------ 1 admin admin 64 06-09 08:58 1 -> /home/admin/work/ecpworker/log/debug.log l-wx------ 1 admin admin 64 06-09 08:58 3 -> /home/admin/work/ecpworker/log/status/ecp_status.log l-wx------ 1 admin admin 64 06-09 08:58 4 -> /home/admin/work/ecpworker/log/error/ecp_error.log l-wx------ 1 admin admin 64 06-09 08:58 5 -> /home/admin/work/ecpworker/log/epollstatus/ecp_epoll_status.log lrwx------ 1 admin admin 64 06-09 08:58 10 -> socket:[79444512] lrwx------ 1 admin admin 64 06-09 08:58 12 -> socket:[79444514] lrwx------ 1 admin admin 64 06-09 08:58 14 -> socket:[79444516] lrwx------ 1 admin admin 64 06-09 08:58 16 -> socket:[79444518] lrwx------ 1 admin admin 64 06-09 08:58 18 -> socket:[79444520] lrwx------ 1 admin admin 64 06-09 08:58 20 -> socket:[79444522] lrwx------ 1 admin admin 64 06-09 08:58 22 -> socket:[79444524] lrwx------ 1 admin admin 64 06-09 08:58 24 -> socket:[79444526] lrwx------ 1 admin admin 64 06-09 08:58 26 -> socket:[79444528] lrwx------ 1 admin admin 64 06-09 08:58 28 -> socket:[79444530] lrwx------ 1 admin admin 64 06-09 08:58 30 -> socket:[79444532] lrwx------ 1 admin admin 64 06-09 08:58 32 -> socket:[79444534] lrwx------ 1 admin admin 64 06-09 08:58 34 -> socket:[79444536] lrwx------ 1 admin admin 64 06-09 08:58 36 -> socket:[79444538] lr-x------ 1 admin admin 64 06-09 08:58 6 -> eventpoll:[79444508] lrwx------ 1 admin admin 64 06-09 08:58 7 -> socket:[79444509] lrwx------ 1 admin admin 64 06-09 08:58 8 -> socket:[79444510] |
从中可以看出,它打开了4个日志文件,16个socket连接,以及一个eventpoll。而我们程序的设计方案,正是1个主进程开启15个等待进程,主进程通过1个socket接收各子进程消息,各子进程各开1个socket接收请求,通过epoll来调度响应。
二、文件描述符限制修改
由于我们每接收一个请求,都会新分配一块内存给它,并新开一个socket连接到电信,因此我们非常关心最大连接数。
这里需要区分一下:
1、 系统最大文件数可在/proc/sys/fs/file-max文件中查询(数值确实很大,但需要知道系统有这样一个总数限制),而目前系统使用的文件句柄数则在/proc/sys/fs/file-nr中:
[admin@ccbuqa147160x SOURCES]$ cat /proc/sys/fs/file-max 544202 [admin@ccbuqa147160x SOURCES]$ cat /proc/sys/fs/file-nr 2040 0 544202 |
2、 一个进程允许打开的最大文件数,可以用ulimit命令查看:
[admin@ccbuqa147160x SOURCES]$ ulimit –n core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 44064 max locked memory (kbytes, -l) 32 max memory size (kbytes, -m) unlimited open files (-n) 10240 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 10240 cpu time (seconds, -t) unlimited max user processes (-u) 44064 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited |
这个表中同时还包含了用户最大进程数,消息队列最大数等。
当然,也可以在每个进程的proc目录中找到更详细的值:
[admin@ccbuqa147160x proc]$ cat /proc/22559/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 10485760 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 44064 44064 processes Max open files 10240 10240 files Max locked memory 32768 32768 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 44064 44064 signals Max msgqueue size 819200 819200 bytes |
可以看出,目前,我们系统中已经将值设置为较大的10240。如果你发现你机子上的这些值为默认的1024,那么最好还是调大一些,这个值对于服务器来说很容易不够用的。通过在文件/etc/security/limits.conf中增加下面两行可以设置此值,重新登陆生效:
* soft nofile 10240
* hard nofile 20480
当然,一般来说,如果需要增大系统设置的socket最大连接数,还需要修改源文件并重新编译:
1、修改/usr/src/kernels/2.6.18-131.el5.custom-x86_64/include/linux/posix_types.h
#undef __FD_SETSIZE
#define __FD_SETSIZE 10240 原值为1024
2、修改/usr/src/kernels/2.6.18-131.el5.custom-x86_64/include/linux/limits.h
#define NR_OPEN 90240 原值为1024
#define OPEN_MAX 10240 原值为1024
3、修改/usr/src/kernels/2.6.18-131.el5.custom-x86_64/include/linux/fs.h
#define INR_OPEN 10240 原值为1024
#define NR_FILE 65536 原值为8192
#define NR_RESERVED_FILES 128 原值为10.
4、修改/usr/src/kernels/2.6.18-131.el5.custom-x86_64/include/net /tcp.h
#define TCP_LHTABLE_SIZE 128 原值为32
5、重新编译内核即可。
三、进程内存映射与动态库加载
可以使用/proc/PID/maps和/proc/PID/smaps来查看进程的内存映射,包括库文件和堆、栈等。
#cat /proc/23698/maps 00400000-00424000 r-xp ca:01 14778415 /home/admin/levy/SMSServer/trunk/bin/SMSServer 00625000-00626000 rw-p 00625000 00:00 0 1236a000-1238b000 rw-p 1236a000 00:00 0 3ce7e00000-3ce7e1c000 r-xp 00000000 ca:01 2097322 /lib64/ld-2.5.so 3ce8551000-3ce8556000 rw-p 3ce8551000 00:00 0 3ce8600000-3ce8602000 r-xp 00000000 ca:01 2097447 /lib64/libdl-2.5.so 3ce8e00000-3ce8e16000 r-xp 00000000 ca:01 2097462 /lib64/libpthread-2.5.so 3ce9017000-3ce901b000 rw-p 3ce9017000 00:00 0 3ce9600000-3ce9607000 r-xp 00000000 ca:01 2097463 /lib64/librt-2.5.so 3cfa000000-3cfa00d000 r-xp 00000000 ca:01 2097461 /lib64/libgcc_s-4.1.2-20080825.so.1 3cfac00000-3cface6000 r-xp 00000000 ca:01 10596444 /usr/lib64/libstdc++.so.6.0.8 3cfaeee000-3cfaf00000 rw-p 3cfaeee000 00:00 0 2b9ce3c77000-2b9ce3c79000 rw-p 2b9ce3c77000 00:00 0 2b9ce3c8d000-2b9ce3c8e000 rw-p 2b9ce3c8d000 00:00 0 2b9ce3c8e000-2b9ce3d7c000 r-xp ca:01 14583150 /home/admin/local/lib/libPocoNet.so.9 2b9ce41f2000-2b9ce41f3000 rw-p 2b9ce41f2000 00:00 0 2b9ce41f3000-2b9ce420f000 r-xp ca:01 14583172 /home/admin/local/lib/libPocoMySQL.so.9 2b9ce4410000-2b9ce445c000 r-xp ca:01 14583166 /home/admin/local/lib/libPocoUtil.so.9 2b9ce465e000-2b9ce4660000 rw-p 2b9ce465e000 00:00 0 2b9ce4660000-2b9ce47f4000 r-xp ca:01 14583165 /home/admin/local/lib/libPocoFoundation.so.9 2b9ce4a02000-2b9ce4a03000 rw-p 2b9ce4a02000 00:00 0 2b9ce4a03000-2b9ce4b1b000 r-xp ca:01 14583068 /home/admin/local/lib/libmysql.so.16.0.0 7fffc6e1d000-7fffc6e32000 rw-p 7ffffffea000 00:00 0 [stack] ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0 [vdso] |
以SMSSERVER为例,它详细显示了SMSSERVER运行时的库文件内存映射,以及stack的地址:
也有一些内存映射后面是空的,说明这是匿名内存映射。可以通过/proc/PID/smaps查看更详细的信息,例如查看上述stack使用情况:
#cat /proc/23698/smaps …… 7fffc6e1d000-7fffc6e32000 rw-p 7ffffffea000 00:00 0 [stack] Size: 84 kB Rss: 16 kB Shared_Clean: 0 kB Shared_Dirty: 0 kB Private_Clean: 0 kB Private_Dirty: 16 kB Swap: 0 kB …… |
其中rw-p表示权限,权限共有以下几种:
r = read
w = write
x = execute
s = shared
p = private (copy on write)
SIZE表示了这个映射的大小,RSS表示正在RAM中的数据大小。
Shared/Private_Clean/Dirty分别表示共享和私有库有效页和脏数据的大小。
这里要注意的是,即使是共享库,但如果使用者只有此一个进程时,也被统计在Private项里。
那么库文件是怎么加载进入内存的呢?这里也做一个简单的介绍。
linux下的库有两种:静态库和共享库(动态库)。
二者的不同点在于代码被载入的时刻不同。 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
一般静态库的后缀是.a,它由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表,然后用ar命令将很多.o转换成.a形成。而动态库的后缀为.so,由gcc加特定参数编译产生。
对于一个可执行文件,可以用如下命令查看它的依赖库,以短信服务器SMSSERVER为例:
那么编译程序是如何找到这些库文件,并加载进来的呢?编译器将先后搜索:
1) elf文件的DT_RPATH段(在编译时指定目录);
2) 环境变量LD_LIBRARY_PATH中指定的路径;
3) /etc/ld.so.cache文件列表;
4) /lib/,/usr/lib目录;
如果找不到则会链接失败。因此,如果出现找不到库的情况,可以:
1)在编译时-rpath指定目录;
2)修改.bash_profile文件在LD_LIBRARY_PATH中加入指定目录;
3)修改/etc/ld.so.conf加入指定目录,然后调用ldconfig重新生成/etc/ld.so.cache;
4)将动态库放入/lib/,/usr/lib两个默认路径;
四、 系统调优相关文件(/proc/sys目录)
通常而言,/proc/sys目录下的文件是可读可写的,其它目录大多只可读,。由于用户可以通过修改值来达到修改运行时系统状态的目的,我们有时也可以此来优化系统状态。当然,修改proc值只会影响当前运行状态,重启后将失效。通常可以采用echo x > filename的方法来将值直接改入文件。例如:
[admin@ccbuqa147160x ~]$ cat /proc/sys/vm/swappiness 10 [admin@ccbuqa147160x ~]$ echo 20 > /proc/sys/vm/swappiness |
下面对一些常见内核参数进行说明:
1、/proc/sys/kernel目录:
文件名 |
含义 |
默认值 |
/proc/sys/kernel/msgmax |
进程间传递消息的最大长度 |
8192 |
/proc/sys/kernel/msgmnb |
一个消息队列的最大长度 |
16384 |
/proc/sys/kernel/msgmni |
消息队列的最大数目 |
16 |
/proc/sys/kernel/shmall |
共享内存可使用总量 |
2097152 |
/proc/sys/kernel/shmmax |
内核所允许的最大共享内存段的大小 |
|
/proc/sys/kernel/threads-max |
内核所能使用的线程的最大数目 |
2048 |
/proc/sys/kernel/sem |
4个值,分别表示每个信号集的最大信号量数目,系统范围内的最大信号量总数目,每个信号发生时的最大系统操作数目,系统范围内的最大信号集总数目 |
|
2、/proc/sys/vm目录
文件名 |
含义 |
备注 |
/proc/sys/vm/swapiness |
系统进行交换行为的程度,数值(0-100)越高,越可能发生磁盘交换 |
60 |
/proc/sys/vm/page-cluster |
写一次到swap区的时候写入的页面数量,0表示1页,1表示2页,2表示4页 |
3 |
/proc/sys/vm/nr_pdflush_threads |
当前正在运行的pdflush进程数量,在I/O负载高的情况下,内核会自动增加更多的pdflush进程。Pdflush用于页面置换时将数据写回磁盘 |
|
/proc/sys/vm/min_free_kbytes |
强制Linux VM最低保留多少空闲内存 |
|
/proc/sys/vm/dirty_writeback_centisecs |
pdflush进程周期性间隔多久把脏数据写回磁盘 |
500ms |
/proc/sys/vm/dirty_ratio |
如果进程产生的脏数据到达系统整体内存的百分比,此时进程自行把脏数据写回磁盘 |
40 |
/proc/sys/vm/dirty_expire_centisecs |
如果脏数据在内存中驻留时间超过该值,pdflush进程在下一次将这些数据写回磁盘 |
3000ms |
/proc/sys/vm/dirty_background_ratio |
脏数据到达系统整体内存的百分比,此时触发pdflush进程把脏数据写回磁盘 |
10 |
3、/proc/sys/net目录
文件名 |
含义 |
默认值 |
/proc/sys/net/core/message_burst |
写新的警告消息所需的时间,在这个时间内系统接收到的其它警告消息会被丢弃。这用于防止某些企图用消息“淹没”系统的人所使用的拒绝服务(Denial of Service)攻击。 |
50(5秒) |
/proc/sys/net/core/netdev_max_backlog |
在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目 |
300 |
/proc/sys/net/core/optmem_max |
每个套接字所允许的最大缓冲区大小 |
10240 |
/proc/sys/net/core/rmem_default |
接收套接字缓冲区大小的缺省值 |
110592 |
/proc/sys/net/core/rmem_max |
接收套接字缓冲区大小的最大值 |
131071 |
/proc/sys/net/core/wmem_default |
发送套接字缓冲区大小的缺省值 |
110592 |
/proc/sys/net/core/wmem_max |
发送套接字缓冲区大小的最大值 |
|
4、/proc/sys/net/ipv4目录
涉及TCP/IP相关参数特别多,这里只列举一些常见的参数:
文件 |
含义 |
默认值 |
/proc/sys/net/ipv4/ip_forward |
是否打开IP转发 |
0禁止 1转发 |
/proc/sys/net/ipv4/ip_default_ttl |
一个数据报的生存周期(Time To Live),即最多经过多少路由器 |
64 |
/proc/sys/net/ipv4/ip_no_pmtu_disc |
在全局范围内关闭路径MTU探测功能 |
0 |
/proc/sys/net/ipv4/route/min_pmtu |
最小路径MTU的大小 |
552 |
/proc/sys/net/ipv4/ipfrag_time |
一个IP分段在内存中保留多少秒 |
30 |
/proc/sys/net/ipv4/tcp_syn_retries |
本机向外发起TCP SYN连接超时重传的次数; 该值仅仅针对外出的连接,对于进来的连接由tcp_retries1控制 |
5 |
/proc/sys/net/ipv4/tcp_keepalive_probes |
丢弃TCP连接前,进行最大TCP保持连接侦测的次数 |
9 |
/proc/sys/net/ipv4/tcp_retries2 |
放弃在已经建立通讯状态下的一个TCP数据包前进行重传的次数 |
15 |
/proc/sys/net/ipv4/tcp_fin_timeout |
对于本端断开的socket连接,TCP保持在FIN-WAIT-2状态的时间 |
60 |
/proc/sys/net/ipv4/tcp_max_tw_buckets |
系统在同时所处理的最大timewait sockets 数目。纯粹为了抵御那些简单的 DoS 攻击 |
180000 |
/proc/sys/net/ipv4/tcp_tw_reuse |
是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接 |
0 |
/proc/sys/net/ipv4/tcp_max_syn_backlog |
对于那些依然还未获得客户端确认的连接请求,需要保存在队列中最大数目 |
1024 |
/proc/sys/net/ipv4/tcp_window_scaling |
设置tcp/ip会话的滑动窗口大小是否可变 |
1 |
/proc/sys/net/ipv4/tcp_reordering |
TCP流中重排序的数据报最大数量 |
3 |
5、常用网络优化策略:
1)优化系统套接字缓冲区
net.core.rmem_max=16777216
net.core.wmem_max=16777216
2)优化TCP接收/发送缓冲区
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
3)优化网络设备接收队列
net.core.netdev_max_backlog=3000
4)关闭路由相关功能
net.ipv4.conf.lo.accept_source_route=0
net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.eth0.accept_source_route=0
net.ipv4.conf.default.accept_source_route=0
net.ipv4.conf.lo.accept_redirects=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.eth0.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.lo.secure_redirects=0
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.eth0.secure_redirects=0
net.ipv4.conf.default.secure_redirects=0
net.ipv4.conf.lo.send_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.eth0.send_redirects=0
net.ipv4.conf.default.send_redirects=0
5)优化TCP协议栈
打开TCP SYN cookie选项,有助于保护服务器免受SyncFlood攻击。
net.ipv4.tcp_syncookies=1
打开TIME-WAIT套接字重用功能,对于存在大量连接的Web服务器非常有效。
net.ipv4.tcp_tw_recyle=1
net.ipv4.tcp_tw_reuse=1
减少处于FIN-WAIT-2连接状态的时间,使系统可以处理更多的连接。
net.ipv4.tcp_fin_timeout=30
减少TCP KeepAlive连接侦测的时间,使系统可以处理更多的连接。
net.ipv4.tcp_keepalive_time=1800
增加TCP SYN队列长度,使系统可以处理更多的并发连接。
net.ipv4.tcp_max_syn_backlog=8192
五、自定义PROC文件和管理
如果你想自定义一个Proc项,可以采用如下方法:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) |
其中,第一个参数为需要创建的文件名,第二个参数为权限设置,第三个参数为父节点名字。如果为空,则创建在/proc/目录下。create_proc_entry实际工作是生成proc_dir_entry,并将它注册到文件系统,链接上默认的文件操作集file_operation。
当然,如果只是想要创建一个只读的 proc 文件,可以采用 create_proc_read_entry() 这个接口。这个接口其实就是给 proc_dir_entry 多赋了两个值,其中 read_proc 是一个函数指针, data 是 read_proc 调用时传给它一个参数。
static inline struct proc_dir_entry *create_proc_read_entry(const char *name,mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void * data) |
此外还有创建目录、删除proc项、读文件、写文件等方法,它们的定义如下:
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent) void remove_proc_entry(const char *name, struct proc_dir_entry *parent) static ssize_t proc_file_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) static ssize_t proc_file_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos) |
proc_file_read为了读到/proc系统中的数据,需要做如下三步:
1.申请内存页 2.读取数据填充到此内存页 3.把数据从内核空间拷贝到用户空间。
proc 文件的读需要自己提供一个 read_proc_t 类型的函数放在 proc_dir_entry 结构中供 proc_file_read() 函数调用。同样,写也需要提供write_proc_t类型的函数。它们的定义分别是:
typedef int (read_proc_t)(char *page, char **start, off_t off, int count,int *eof, void *data); typedef int (write_proc_t)(struct file *file, const char __user *buffer, unsigned long count, void *data); |
后记
/proc文件系统包含了系统信息和内核参数,内容非常丰富,有许多参数的理解和修改需要对linux原理、进程调度、内存管理等有很深的了解,才可以对其做出更好的分析和优化。本文提到的均为较常见的参数,更详细的说明可以查看linux帮助文档和网站。
感兴趣的同学可以一起讨论。如有错误,请指正。
谢谢大家。