(转) linux管道与套接字的调试工具

jackxiang 2015-2-3 09:17 | |
背景:多进程时用管道进行子进程间或子进程于线程之间通讯,出现父进程已经双close后还是出现了当有请求来时,子进程写入处理线程时管道阻塞住了,怎么办,管道调试试试看了,学习备忘下。

1)管道的调试

/proc/pid/fd目录下列出进程的管道和管道的索引号.

我们用下面的程序来看管道在/proc/pid/fd目录下的表现,如下:

#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
        pid_t pid;
        int fd[2],nbytes;
        char string[]="data from child process";
        char buf[100];
        if(pipe(fd)<0)
        {
                perror("pipe");
                exit(1);
        }
        if((pid=fork())==-1)
        {
                perror("fork");
                exit(1);
        }
        if(pid==0)
        {
                close(fd[0]);
                printf("childpid =%2d\n",getpid());
                write(fd[1],string,sizeof(string));
                close(fd[1]);
                exit(0);
        }
        else
        {
                printf("parentpid =%2d\n",getpid());
                sleep(30);
                close(fd[1]);
                nbytes=read(fd[0],buf,sizeof(buf));
                printf("Received string:%s\n",buf);
                close(fd[0]);
        }
        return 0;
}

编译:
gcc pipe.c -o pipe

运行:
./pipe
childpid =27695
parentpid = 27694

程序会在父进程执行时调用sleep(30),此时子进程已经完成对管道的写入,并关闭了子进程的管道(读/写两端).
父进程在sleep(30)秒后,从管道读取信息并输出.

我们在父进程睡眠的时候,根据它的parentpid输出,来查看它的文件描述符,如下:

ls -l /proc/27694/fd
total 0
lrwx------ 1 root root 64 Mar 18 13:06 0 -> /dev/pts/0
lrwx------ 1 root root 64 Mar 18 13:06 1 -> /dev/pts/0
lrwx------ 1 root root 64 Mar 18 13:06 2 -> /dev/pts/0
lr-x------ 1 root root 64 Mar 18 13:06 3 -> pipe:[42753]
l-wx------ 1 root root 64 Mar 18 13:06 4 -> pipe:[42753]

这里的3 -> pipe:[42753],表示管道的读取端,4 -> pipe:[42753]表示管道的写入端.

相同我们也可以根据管道的索引号得到它的进程,如下:
lsof |head -1 && lsof | grep 43046
COMMAND     PID      USER   FD      TYPE     DEVICE     SIZE       NODE NAME
pipe      27780      root    3r     FIFO        0,6               43046 pipe
pipe      27780      root    4w     FIFO        0,6               43046 pipe


———————————————实践开始—————————————————
自己实践如下,主进程没有管道,因为关闭了:
[root@test multepoolserver]# ls -l  /proc/32651/fd/
total 0
lrwx------ 1 root root 64 Feb  3 09:37 0 -> /dev/pts/7
lrwx------ 1 root root 64 Feb  3 09:37 1 -> /dev/pts/7
lrwx------ 1 root root 64 Feb  3 09:36 2 -> /dev/pts/7
lrwx------ 1 root root 64 Feb  3 09:37 5 -> socket:[1020446723]

看下子进程,先起一个,pid号32652:
[root@test multepoolserver]# ls -l  /proc/32652/fd/
total 0
lrwx------ 1 root root 64 Feb  3 09:38 0 -> /dev/pts/7
lrwx------ 1 root root 64 Feb  3 09:38 1 -> /dev/pts/7
lrwx------ 1 root root 64 Feb  3 09:36 2 -> /dev/pts/7
lrwx------ 1 root root 64 Feb  3 09:38 5 -> socket:[1020446723]
lr-x------ 1 root root 64 Feb  3 09:38 6 -> eventpoll:[1020446725]

有的可能没有*.sock,可用其它的看:
法一)netstat -nlxp

法二)cat /proc/net/unix

法三)/proc/PID号/fd/

法四)lsof /tmp/localsock    
lsof /tmp/mysqld.sock
COMMAND   PID  USER   FD   TYPE             DEVICE SIZE     NODE NAME
mysqld  15066 mysql   20u  unix 0xffff8100b983a8c0      87854212 /tmp/mysqld.sock

法五)lsof -i tcp -a -p 32651
lsof -i tcp -a -p 32651
COMMAND     PID USER   FD   TYPE     DEVICE SIZE NODE NAME
multipepo 32651 root    5u  IPv4 1020446723       TCP *:webcache (LISTEN)

总之,对于管道,可以在/proc/pid/fd和lsof -p pid的方式来查看INODE。
—————————————实践结束—————————————————

2)套接字的调试

调试套接字时两个最有用的工具是netstat和lsof

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define ASSERTNOERR(x, msg) do{\
        if ((x) == -1){perror(msg); exit(1);}}while(0)
#define SOCKNAME "localsock"
int main (int argc, char *argv[])
{
        int s = socket(PF_LOCAL, SOCK_STREAM, 0);
        struct sockaddr_un sa = {
                .sun_family = AF_LOCAL,
                .sun_path = SOCKNAME
        };
        int r = bind(s, (struct sockaddr *) &sa, sizeof(sa));
        ASSERTNOERR(r, "bind");
        r = listen(s, 0);
        ASSERTNOERR(r, "listen");
        struct sockaddr_un asa;
        size_t addrlen = sizeof(asa);
        int fd = accept(s, (struct sockaddr *) &asa, &addrlen);
        ASSERTNOERR(fd, "accept");
        while(1){
                char buf[32];
                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(fd, &fds);
                int r = select (fd+1, &fds, NULL, NULL, NULL);
                ASSERTNOERR(r, "select");
                int n = read(fd, buf, sizeof(buf));
                printf("server read %d bytes\n", n);
                if(n == 0)
                        break;
        }
        unlink(SOCKNAME);
        return 0;
}

编译:
gcc unix-socket.c -o unix-socket

用netstat来查看处于监听状态的UNIX socket套接字,如下:
netstat -nlxp
Active UNIX domain sockets (only servers)
Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
unix  2      [ ACC ]     STREAM     LISTENING     4899   1967/sdpd           /var/run/sdp
unix  2      [ ACC ]     STREAM     LISTENING     5534   2260/xfs            /tmp/.font-unix/fs7100
unix  2      [ ACC ]     STREAM     LISTENING     6586   2468/1              /tmp/ssh-yWORhw2468/agent.2468
unix  2      [ ACC ]     STREAM     LISTENING     8331   2879/unix-socket    /tmp/localsock
unix  2      [ ACC ]     STREAM     LISTENING     4988   2030/pcscd          /var/run/pcscd.comm
unix  2      [ ACC ]     STREAM     LISTENING     5151   2103/acpid          /var/run/acpid.socket
unix  2      [ ACC ]     STREAM     LISTENING     5412   2206/gpm            /dev/gpmctl
unix  2      [ ACC ]     STREAM     LISTENING     5631   2311/avahi-daemon:  /var/run/avahi-daemon/socket
unix  2      [ ACC ]     STREAM     LISTENING     5257   2141/cupsd          /var/run/cups/cups.sock
unix  2      [ ACC ]     STREAM     LISTENING     5677   2328/hald           @/var/run/hald/dbus-gzDLkbe0vz
unix  2      [ ACC ]     STREAM     LISTENING     5678   2328/hald           @/var/run/hald/dbus-y2AFY83XQF
unix  2      [ ACC ]     STREAM     LISTENING     4424   1755/python         /var/run/audit_events
unix  2      [ ACC ]     STREAM     LISTENING     4810   1941/dbus-daemon    /var/run/dbus/system_bus_socket

我们看到unix-socket程序产生的socket文件,如下:
unix  2      [ ACC ]     STREAM     LISTENING     8331   2879/unix-socket    /tmp/localsock

ACC:表示这个unix socket程序调用ACCEPT函数,处于接收的状态.
SOCK_STREAM:表示这个unix socket是可靠的基于连接的数据传输,基本协议保证了按照传输的顺序读取信息.
I-NODE:表示套接字的索引号,就像一个文件.

通过查看/proc/net/unix,也可以看到unix套接字的信息.
cat /proc/net/unix
Num       RefCount Protocol Flags    Type St Inode Path
d3341040: 00000002 00000000 00010000 0001 01  4899 /var/run/sdp
d2893c80: 00000002 00000000 00010000 0001 01  5534 /tmp/.font-unix/fs7100
d32dac80: 00000002 00000000 00010000 0001 01  6586 /tmp/ssh-yWORhw2468/agent.2468
d32da3c0: 00000002 00000000 00010000 0001 01  8331 /tmp/localsock
db380c80: 00000002 00000000 00010000 0001 01  4988 /var/run/pcscd.comm
db380900: 00000002 00000000 00010000 0001 01  5151 /var/run/acpid.socket
db380040: 00000002 00000000 00010000 0001 01  5412 /dev/gpmctl
d2893200: 00000002 00000000 00010000 0001 01  5631 /var/run/avahi-daemon/socket
db380580: 00000002 00000000 00010000 0001 01  5257 /var/run/cups/cups.sock
dfcf4900: 00000012 00000000 00000000 0002 01  4416 /dev/log
dfcf4e40: 00000002 00000000 00000000 0002 01  1081 @/org/kernel/udev/udevd
d1029580: 00000002 00000000 00000000 0002 01  5686 @/org/freedesktop/hal/udev_event
d1029c80: 00000002 00000000 00010000 0001 01  5677 @/var/run/hald/dbus-gzDLkbe0vz
d1029ac0: 00000002 00000000 00010000 0001 01  5678 @/var/run/hald/dbus-y2AFY83XQF
dfcf4580: 00000002 00000000 00010000 0001 01  4424 /var/run/audit_events
d3341c80: 00000002 00000000 00010000 0001 01  4810 /var/run/dbus/system_bus_socket

可以通过lsof来查看unix套接字信息,如下:

netstat -xnpl|grep localsock
unix  2      [ ACC ]     STREAM     LISTENING     8331   2879/unix-socket    /tmp/localsock

lsof /tmp/localsock              
COMMAND    PID USER   FD   TYPE     DEVICE SIZE NODE NAME
unix-sock 2879 root    3u  unix 0xd32da3c0      8331 /tmp/localsock

在这里我们可以看用户ID,文件描述符.

可以通过查看/proc/8331/fd/,来看文件描述的信息,如下:
ls -l /proc/2879/fd
total 0
lrwx------ 1 root root 64 Mar 19 01:32 0 -> /dev/pts/0
lrwx------ 1 root root 64 Mar 19 01:32 1 -> /dev/pts/0
lrwx------ 1 root root 64 Mar 19 01:32 2 -> /dev/pts/0
lrwx------ 1 root root 64 Mar 19 01:32 3 -> socket:[8331]

对于tcp/udp的SOCKET套接字,如下:

查看tcp的SOCKET套接字:
netstat -tanp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name  
tcp        0      0 127.0.0.1:2208              0.0.0.0:*                   LISTEN      2119/hpiod          
tcp        0      0 0.0.0.0:779                 0.0.0.0:*                   LISTEN      1869/rpc.statd      
tcp        0      0 0.0.0.0:111                 0.0.0.0:*                   LISTEN      1830/portmap        
tcp        0      0 0.0.0.0:23                  0.0.0.0:*                   LISTEN      2177/xinetd        
tcp        0      0 127.0.0.1:631               0.0.0.0:*                   LISTEN      2141/cupsd          
tcp        0      0 127.0.0.1:2207              0.0.0.0:*                   LISTEN      2124/python        
tcp        0      0 :::22                       :::*                        LISTEN      2160/sshd          
tcp        0      0 ::1:631                     :::*                        LISTEN      2141/cupsd          
tcp        0    148 ::ffff:192.168.27.130:22    ::ffff:192.168.27.1:2892    ESTABLISHED 2468/2

查看udp的SOCKET套接字:
netstat -uanp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name  
udp        0      0 0.0.0.0:32768               0.0.0.0:*                               2311/avahi-daemon:  
udp        0      0 0.0.0.0:773                 0.0.0.0:*                               1869/rpc.statd      
udp        0      0 0.0.0.0:776                 0.0.0.0:*                               1869/rpc.statd      
udp        0      0 0.0.0.0:68                  0.0.0.0:*                               1548/dhclient      
udp        0      0 0.0.0.0:5353                0.0.0.0:*                               2311/avahi-daemon:  
udp        0      0 0.0.0.0:111                 0.0.0.0:*                               1830/portmap        
udp        0      0 0.0.0.0:631                 0.0.0.0:*                               2141/cupsd          
udp        0      0 :::32769                    :::*                                    2311/avahi-daemon:  
udp        0      0 :::5353                     :::*                                    2311/avahi-daemon:  

通过lsof来查看tcp的信息.
lsof -n -i tcp
COMMAND    PID USER   FD   TYPE DEVICE SIZE NODE NAME
portmap   1830  rpc    4u  IPv4   4533       TCP *:sunrpc (LISTEN)
rpc.statd 1869 root    7u  IPv4   4615       TCP *:779 (LISTEN)
hpiod     2119 root    0u  IPv4   5185       TCP 127.0.0.1:2208 (LISTEN)
python    2124 root    4u  IPv4   5201       TCP 127.0.0.1:2207 (LISTEN)
cupsd     2141 root    3u  IPv6   5255       TCP [::1]:ipp (LISTEN)
cupsd     2141 root    4u  IPv4   5256       TCP 127.0.0.1:ipp (LISTEN)
sshd      2160 root    3u  IPv6   5303       TCP *:ssh (LISTEN)
xinetd    2177 root    5u  IPv4   5416       TCP *:telnet (LISTEN)
sshd      2468 root    3u  IPv6   6547       TCP 192.168.27.130:ssh->192.168.27.1:snifferdata (ESTABLISHED)

通过lsof来查看udp的信息.
lsof -n -i udp
COMMAND    PID  USER   FD   TYPE DEVICE SIZE NODE NAME
dhclient  1548  root    4u  IPv4   4027       UDP *:bootpc
portmap   1830   rpc    3u  IPv4   4532       UDP *:sunrpc
rpc.statd 1869  root    3u  IPv4   4612       UDP *:wpages
rpc.statd 1869  root    6u  IPv4   4602       UDP *:notify
cupsd     2141  root    6u  IPv4   5259       UDP *:ipp
avahi-dae 2311 avahi   13u  IPv4   5635       UDP *:mdns
avahi-dae 2311 avahi   14u  IPv6   5636       UDP *:mdns
avahi-dae 2311 avahi   15u  IPv4   5637       UDP *:filenet-tms
avahi-dae 2311 avahi   16u  IPv6   5638       UDP *:filenet-rpc


在lsof的输出可以看到文件描述符及设备的信息,另外还有用户信息.


最后我们来看一下管道和套接字的INODE,每个文件都有自己的INODE,但由于VFS(虚拟文件系统)使所有的文件系统都通用,使tmpfs/procfs下的文件像驻留在磁盘上的文件一样.
INODE保存着文件的数据结构,文件系统中每个对象有一个唯一的INODE.
INODE对于没有文件名的对象,包括socket和管道是非常有用的.
TCP/UDP套接字的INODE,可以用下面的命令来查看:
lsof -i tcp -a -p 2468
COMMAND  PID USER   FD   TYPE DEVICE SIZE NODE NAME
sshd    2468 root    3u  IPv6   6547       TCP 192.168.27.130:ssh->192.168.27.1:snifferdata (ESTABLISHED)

其它的DEIVCE就是它的INODE,NODE一栏只显示TCP

下面是UDP套接字的例子:
lsof -i udp -a -p 1869
COMMAND    PID USER   FD   TYPE DEVICE SIZE NODE NAME
rpc.statd 1869 root    3u  IPv4   4612       UDP *:wpages
rpc.statd 1869 root    6u  IPv4   4602       UDP *:notify

对于UNIX的本地套接字,可以直接用netstat来查看,如下:
netstat -xnp
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
unix  17     [ ]         DGRAM                    4416   1774/syslogd        /dev/log
unix  2      [ ]         DGRAM                    1081   385/udevd           @/org/kernel/udev/udevd
unix  2      [ ]         DGRAM                    5686   2328/hald           @/org/freedesktop/hal/udev_event

而对于管道,可以在/proc/pid/fd和lsof -p pid的方式来查看INODE

摘自:http://www.linuxidc.com/Linux/2012-07/66415.htm

附录,实践总结:
有的可能没有*.sock,可用其它的看:
法一)netstat -nlxp

法二)cat /proc/net/unix

法三)/proc/PID号/fd/

法四)lsof /tmp/localsock    
lsof /tmp/mysqld.sock
COMMAND   PID  USER   FD   TYPE             DEVICE SIZE     NODE NAME
mysqld  15066 mysql   20u  unix 0xffff8100b983a8c0      87854212 /tmp/mysqld.sock

法五)lsof -i tcp -a -p 32651
lsof -i tcp -a -p 32651
COMMAND     PID USER   FD   TYPE     DEVICE SIZE NODE NAME
multipepo 32651 root    5u  IPv4 1020446723       TCP *:webcache (LISTEN)

法六)而对于管道,可以在/proc/pid/fd和lsof -p pid的方式来查看INODE。

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/7784/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2015-2-3 09:27
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]