Linux网络IPC sockets

4 篇文章 0 订阅
1 篇文章 0 订阅

第一章、网络编程基础sockets


一、主机字节序和网络字节序

字节序分为大端和小端。

  • 大端字节序:正序存储,一个整数的高位(23-31bit)存储在内存的低地址处,低位字节(0-7bit)储存在高地址。网络通讯通常使用大端字节序,也称为网络字节序。
  • 小端字节序:逆序存储,一个整数的高位(23-31bit)存储在内存的高地址处,低位字节(0-7bit)储存在低地址。PC大多采用小端字节序,也称为主机字节序。

判断自己的电脑是大端还是小端代码:

#include <stdio.h>

union {
    short value;
    char ch[2];
}test;

int main()
{
    test.value = 0x0102;

    if (test.ch[0] == 1 && test.ch[1] == 2)
        printf("本机是大端字节序\n");
    else if (test.ch[0] == 2 && test.ch[1] == 1)
        printf("本机是小端字节序\n");
    else
        printf("本机字节序未知\n");
}

n->网络,h->主机	

Linux提供两种字节序之间的转换函数:

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

二、socket 地址格式

1、通用socket地址

不同格式的地址可以传递给套接字函数,被转换为通用的sockaddr地址结构。

#include <bits/socket.h>
struct sockaddr {
	sa_family_t sa_family; /* address family */
	char sa_data[14];      /* variable-length address */
};
  • sa_family :协议族(地址族)
  • sa_data: 存放地址数据

在这里插入图片描述

sa_data只有14字节,不够用时用下面这个新的socket通用地址结构体:

#include <bits/socket.h> 
struct sockaddr_storage
{
    sa_family_t sa_family;          /* address family */
    unsigned long int __ss_align;	/* Force desired alignment.  */
    char __ss_padding[128 - sizeof(__ss_align)];
};

2、专用socket地址

通用地址显然不好用,端口和IP需要复杂的位操作。
Linux为各个协议族提供了专门的socket地址结构体。

2.1、UNIX本地域协议族
#include <sys/un.h>
struct sockaddr_un
{
    sa_family_t sa_family;  /* address family */
    char sun_path[108];		/* Path name.  */
};

2.2、TCP/IP协议族IPv4
#include <netinet/in.h>
struct in_addr {
    in_addr_t s_addr;        /* IPv4 address, Network byte order */
};

struct sockaddr_in {
    sa_family_t sin_family;  /* address family */
    in_port_t sin_port;      /* port number */
    struct in_addr sin_addr; /* IPv4 address */
};

2.3、TCP/IP协议族IPv6
#include <netinet/in.h>
struct in6_addr {
    uint8_t s6_addr[16];       /* IPv6 address, Network byte order */
};

struct sockaddr_in6 {
    sa_family_t sin6_family;   /* address family */
    in_port_t sin6_port;       /* port number */
    uint32_t sin6_flowinfo;    /* traffic class and flow info */
    struct in6_addr sin6_addr; /* IPv6 address */
    uint32_t sin6_scope_id;    /* set of interfaces for scope */
};

3、IP地址转换

inet_addrinet_ntoa函数,用于在二进制地址格式和点分十进制(a.b.c.d)格式的字符串之间进行转换。这些函数仅适用于IPv4地址。

#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp); 
char *inet_ntoa(struct in_addr in);
in_addr_t inet_addr(const char *cp); // 等效inet_aton,但要谨慎使用,-1(255.255.255.255)时无效地址有问题。

要注意的是inet_ntoa是不可重入的,因为该函数内部用的是一个静态变量来储存转化结果,重复被赋值就不生效了,演示如下。

int main()
{
    struct in_addr addr1, addr2;

    inet_aton("1.2.3.4", &addr1);
    inet_aton("11.22.33.44", &addr2);

    char *sz_addr1 = inet_ntoa(addr1);
    char *sz_addr2 = inet_ntoa(addr2);

    printf("sz_addr1 = %s\n", sz_addr1);
    printf("sz_addr2 = %s\n", sz_addr2);
}
# 输出结果
sz_addr1 = 11.22.33.44
sz_addr2 = 11.22.33.44


新函数inet_ntopinet_pton支持类似的功能,并且可以同时使用IPv4和IPv6地址。

#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

int inet_pton(int af, const char *src, void *dst); 
// inet_pton() 在成功时返回1(网络地址成功转换)。 如果SRC中不包含表示指定地址族中合法网络地址的字符串,则返回0。 如果af不包含有效的地址族,则返回-1,并将errno设置为EAFNOSUPPORT。 

inet_pton() 在成功时返回1(网络地址成功转换)。 如果SRC中不包含表示指定地址族中合法网络地址的字符串,则返回0。 如果af不包含有效的地址族,则返回-1,并将errno设置为EAFNOSUPPORT。

size指定目标存储单元的大小,下面两个宏分别用于IPv4和IPv6。

#include <netinet/in.h>
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46

三、创建socket

创建套接字,调用socket函数。

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
// Returns: file (socket) descriptor if OK, −1 on error
  • domain :域常用参数是AF_UNIX(AF_LOCAL)、AF_INET、AF_INET6。
  • type
SOCK_STREAM 流服务(常用于TCP)   
SOCK_DGRAM  传输使用UDP协议    
SOCK_RAW    数据报  
内核2.6.17之后,支持与运算下面的值
SOCK_NONBLOCK 创建的sokcet为非阻塞的   
SOCK_CLOEXEC  用fork创建子进程时在子进程中关闭该socket
  • protocol :在前两个参数的基础上指定一个具体的协议,通常为0。

四、服务端绑定bind

服务端必须将socket绑定到一个地址上,这个步骤也称为命名socket,客户端不需要绑定。
使用bind函数将地址与socket绑定起来。

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
// Returns: 0 if OK, −1 on error

bind将addr所指向的socket地址分配给为命名的sockfd文件描述符,addrlen参数表示该socket地址的长度。
返回值常见的errno是EACCESEADDRINUSE

  • EACCES:被绑定的地址是受保护的地址,仅超级用户能访问,比如绑定到(0-1024)时。
  • EADDRINUSE:被绑定的地址正在使用中,比如绑定到一个处于TIME_WAIT状态的socket地址时。

对于Internet域,如果指定特殊的IP地址INADDR_ANY(定义在<netinet/in.h>),socket端点将被绑定到系统的所有网络接口。这意味着可以从系统中安装的任何NIC网卡接收数据包。

可以使用getsockname函数来发现绑定到套接字的地址。

#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *restrict addr,
	socklen_t *restrict alenp);
// Returns: 0 if OK, −1 on error

在调用getsockname之前,设置alenp指向一个包含sockaddr缓冲区大小的整数。在返回时,该整数被设置为返回的地址的大小。如果地址不能装入所提供的缓冲区,地址将被静默地截断。如果当前没有绑定到套接字的地址,则没有定义结果。

如果套接字连接了peer,可以通过调用getpeername函数来找到peer的地址。
除了返回对等体的地址之外,getpeername函数与getsockname函数是相同的。

#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *restrict addr,
	socklen_t *restrict alenp);
// Returns: 0 if OK, −1 on error

五、服务端监听listen

socket绑定地址命名后,还不能接受客户端的链接,需要创建一个监听队列存放待处理的客户链接:

#include <sys/socket.h>
int listen(int sockfd, int backlog);
// Returns: 0 if OK, −1 on error

backlog参数提示内核监听队列的最大长度。若监听队列的长度超过该值,服务器不再受理新客户的链接,客户端将受到ECONNREFUSED错误信息。

下面显示初始化一个socket的接口,返回一个sockfd给服务端使用。

#include <errno.h>
#include <sys/socket.h>
int init_socket(int type, const struct sockaddr *addr, socklen_t alen,
           int qlen)
{
    int fd;
    int err = 0;
    if ((fd = socket(addr->sa_family, type, 0)) < 0)
        return(-1);
    if (bind(fd, addr, alen) < 0)
        goto errout;
    if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
        if (listen(fd, qlen) < 0)
            goto errout;
    }
    return(fd);
errout:
    err = errno;
    close(fd);
    errno = err;
    return(-1);
}

六、服务端接受连接accept

一旦服务器调用了listen,所使用的套接字就可以接收连接请求。 我们使用accept函数来检索连接请求并将其转换为连接。

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
// Returns: file (socket) descriptor if OK, −1 on error

如果没有连接请求等待,accept将阻塞,直到有一个请求到达。如果sockfd处于非阻塞模式,accept将返回−1,并将errno设置为EAGAIN或
EWOULDBLOCK。

由accept返回的文件描述符是一个连接到调用connect的客户端的套接字描述符。 这个新的套接字描述符具有与原始套接字(sockfd)相同的套接字类型和地址族。 传递给accept的原始套接字与连接不相关,但仍然可用来接收其他连接请求。

如果不需要关心客户端的标识,可以将addr和len参数设置为NULL。 否则,在调用accept之前,需要将addr参数设置为一个足够大的缓冲区来保存地址,并将len所指向的整数设置为缓冲区的大小(以字节为单位)。 在返回时,accept将在缓冲区中填写客户端的地址,并更新len所指向的整数以反映地址的大小。

连接案列:


七、客户端发出连接connect

客户端使用connect函数来创建连接。

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t len);
// Returns: 0 if OK, −1 on error

使用connect指定的地址是希望与之通信的服务器的地址。如果sockfd没有绑定到一个地址,connect将为调用者绑定一个默认地址。

当尝试连接到服务器时,连接请求可能会因为几个原因而失败。为了连接请求成功,试图连接的机器必须启动并运行,服务器必须绑定到试图联系的地址,并且服务器的挂起连接队列中必须有空间。因此,应用程序必须能够处理可能由瞬态条件引起的连接错误返回。

下面是使用指数后退算法处理临时连接错误的方法。这些错误很可能发生在服务器运行在负载沉重的系统上。

#include <sys/socket.h>
#define MAXSLEEP 128
int connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen)
{
    int numsec;
    
    /* 指数后退算法。如果连接调用失败,进程会休眠一段时间。
	   然后再次尝试,每次循环都会增加延迟,最大延迟约为2分钟。*/
    for (numsec = 1; numsec <= MAXSLEEP; numsec <<= 1) {
        if (connect(sockfd, addr, alen) == 0) {
            return(0);
        }
    // 延迟重连
    if (numsec <= MAXSLEEP/2)
            sleep(numsec);
    }
    return(-1);
}

这种算法只能运行在linux上,不可移植FreeBSD,可移植版参考UNIX高级环境编程。

八、关闭连接close & shutdown

打开的文件通过调用close函数来关闭,这一点在多线程编程中尤其要注意,如果忘记了关闭,当fd的引用计数超过1024后,文件将拒绝访问,这样就造成了句柄泄露。

#include <unistd.h>
int close(int fd);
// Returns: 0 if OK, −1 on error

关闭文件还会释放进程对文件可能拥有的任何记录锁。

close并非总是立即关闭一个链接,而是将文件描述符fd的引用计数减1,只有当fd引用计数为0时,才真正的关闭链接。多进程程序中,一次fork后会将父进程中打开的socket引用计数加1,因此必须在父进程和子进程中都对该socket执行close才能关闭链接。

若一定要立即终止链接,可以直接使用shutdown调用。socket上的通信是双向的,可以使用shutdown功能在socket上禁用I/O。

#include <sys/socket.h>
int shutdown(int sockfd, int how);
// Returns: 0 if OK, −1 on error

如果howSHUT_RD,那么socket读取被禁用。
如果howSHUT_WR,那么socket写入被禁用。
如果howSHUT_RDWR,同时禁用socket读取和写入。

九、套接字数据传输

1、TCP数据读写

对文件的读写read和write同样使用socket,但socket提供了6个专用接口函数,增强了对socket数据的读写。

TCP读写系统调用如下:

#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
// Returns: number of bytes sent if OK, −1 on error

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
// Returns: length of message in bytes,
// 0 if no messages are available and peer has done an orderly shutdown,
// or −1 on error

1、send试图发送一个大于协议支持的最大消息,则send将失败,errno设置为EMSGSIZE。使用字节流协议,send将阻塞,直到传输完全部数据。send主要关注flags,如下图。
2、recv的flags参数通常设置为0,详情见下图。recv成功时读取的长度可能小于期望的长度len,因此需要多次调用recv,才能读取到完整的数据。recv返回0,意味着通信对方已经关闭连接了。

flags参数如下:
在这里插入图片描述
发送紧急数据会截断普通数据,案列如下,首先是客户端

int main(int argc, char *argv[])
{
    if (argc <= 2)
    {
        DBG_PRINT("main parameter error");
        return -1;
    }
    // 打印运行程序名
    DBG_PRINT("parameter right, run procedure %s", basename(argv[0]));

    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in addr = {};
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &addr.sin_addr);
    addr.sin_port = htons(port);
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    assert(sockfd >= 0);

    if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(addr)) < 0)
        printf("connetc failed\n");
    else {
        const char* oob_data = "abcd";    // 带外数据(紧急,快速数据)
        const char* normal_data = "1234"; // 普通数据
        send(sockfd, normal_data, strlen(normal_data), 0);
        send(sockfd, oob_data, strlen(oob_data), MSG_OOB);
        send(sockfd, normal_data, strlen(normal_data), 0);
    }
    close(sockfd);
}

服务端:

int main(int argc, char *argv[])
{
    if (argc <= 2)
    {
        DBG_PRINT("main parameter error");
        return -1;
    }
    // 打印运行程序名
    DBG_PRINT("parameter right, run procedure %s", basename(argv[0]));

    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in addr = {};
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &addr.sin_addr);
    addr.sin_port = htons(port);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bind(sockfd, (const struct sockaddr *)&addr, sizeof(addr));

    listen(sockfd, 5);

    struct sockaddr_in client;
    socklen_t client_addr_len = sizeof(client);

    int connfd = accept(sockfd, (struct sockaddr *)&addr, &client_addr_len);
    if (connfd < 0) {
        DBG_PRINT("error is %d", errno);
    }
    else {
        char buff[1024] = {};
        int ret = recv(connfd, buff, 1024 - 1, 0);
        printf("got %d bytes of normal data: %s\n", ret, buff);

        bzero(buff, 1024);
        ret = recv(connfd, buff, 1024 - 1, MSG_OOB);
        printf("got %d bytes of oob data: %s\n", ret, buff);

        bzero(buff, 1024);
        ret = recv(connfd, buff, 1024 - 1, 0);
        printf("got %d bytes of normal data: %s\n", ret, buff);

        close(connfd);
    }
    close(sockfd);
}
# 如果连续发送3个普通数据,服务端打印结果应该是这样的
got 8 bytes of normal data: 1234abcd1234
got 0 bytes of normal data: 
got 0 bytes of normal data: 

# 本案列运行后,服务端打印如下数据,d就是OOB,截断了正常数据的收发
got 7 bytes of normal data: 1234abc
got 1 bytes of oob data: d
got 4 bytes of normal data: 1234

&esmp;

2、UDP数据读写

UDP读写系统调用如下:

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags,
	struct sockaddr *restrict addr, socklen_t *restrict addrlen);
// Returns: length of message in bytes,
// 0 if no messages are available and peer has done an orderly shutdown,
// or −1 on error
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags,
	const struct sockaddr *destaddr, socklen_t destlen);
//Returns: number of bytes sent if OK, −1 on error

&esmp;

3、TCP和UDP通用的数据读写

#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
// Returns: length of message in bytes,
// 0 if no messages are available and peer has done an orderly shutdown,
// or −1 on error

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
// Returns: number of bytes sent if OK, −1 on error

九、socket选项 (重点)

以下函数用于获取和设置socket文件描述符属性:

#include <sys/socket.h>
int getsockopt(int sockfd, int level, int option, void *restrict val, socklen_t *restrict lenp);
int setsockopt(int sockfd, int level, int option, const void *val, socklen_t len);
// Returns: 0 if OK, −1 on error

level 要操作的是什么协议,socket通常是SOL_SOCKET
option 选项,如下表
val 和 len 是该option选项对应的值,和该值对应的长度,如下表

在这里插入图片描述

1、SO_REUSEADDR 重用本地地址

当服务器终止时,尝试立即重新启动它。通常,TCP的实现会阻止绑定相同的地址,直到超时过期(TIME_WAIT),这通常是几分钟的时间。幸运的是,SO_REUSEADDR套接字选项可以绕过这个限制。

注意服务端端要在监听之前设置,某些选项客户端要在connect之前设置,以下是代码清单。

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    int reuse = 1;
    // 在bind和listen之前设置
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
	
	bind(sockfd, (const struct sockaddr *)&addr, sizeof(addr));
    listen(sockfd, 5);

2、SO_RCVLOWAT和SO_SNDLOWAT

它们一般被I/O复用函数用来判断socket是否可读可写。

第二章、I/O复用

当我们从一个描述符读取并写入到另一个描述符时,我们可以在循环中使用阻塞I/O,例如

while ((n = read(STDIN_FILENO, buf, BUFSIZ)) > 0)
	if (write(STDOUT_FILENO, buf, n) != n)
		err_sys("write error");

我们一次又一次地看到这种阻塞I/O的形式。 如果我们需要读两个描述符呢? 在这种情况下,我们不能在任何一个描述符上进行阻塞读取,因为数据可能出现在一个描述符上,而我们在另一个描述符上的读取被阻塞。 处理这种情况需要一种不同的技术 。

更好的技术是使用I/O多路复用。 为此,我们构建一个感兴趣的描述符列表(通常不止一个描述符),并调用一个函数,直到其中一个描述符准备好进行I/O时才返回。 三个函数-poll、pselect和select -允许我们执行I/O多路复用。 从这些函数返回时,我们被告知哪些描述符已经准备好进行I/O。

一、select

传递给select的参数告诉内核我们感兴趣的描述符。

  • 我们对每个描述符感兴趣的条件是什么。 (我们想要从给定的描述符中读取吗? 我们要写入给定的描述符吗? 我们是否对给定描述符的异常条件感兴趣?)
  • 我们想等多久。 (我们可以永远等待,等待固定的时间,或者根本不等待。) 在select返回时,内核告诉我们。
  • 已经准备好的描述符的总数。
  • 哪些描述符可以满足这三种条件(读、写或异常条件)

在select返回的时候,内核告诉我们已经准备好的描述符的总数;
有了这些返回信息,我们可以调用相应的I/O函数(通常是读或写),并且知道函数不会阻塞。

#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);
// Returns: count of ready descriptors, 0 on timeout, −1 on error

nfds:指定被监听的文件描述符的总数,通常是connfd+1。
readfds, writefds, exceptfds:描述符可以满足这三种条件(读、写或异常条件)
使用如下宏操作fd_set 结构体的位:

#include <sys/select.h>
int  FD_ISSET(int fd, fd_set *set);    // 测试fdset的位fd是否被设置 
// Returns: nonzero if fd is in set, 0 otherwise            
void FD_CLR(int fd, fd_set *set);      // 清除fdset的位fd
void FD_SET(int fd, fd_set *set);      // 设置fdset的位fd
void FD_ZERO(fd_set *set);             // 清除fdset的所有位

timeout:指定超时时间,timeval 结构体如下:

struct timeval
{
  __time_t tv_sec;		    /* 秒   Seconds.  */
  __suseconds_t tv_usec;	/* 微秒 Microseconds.  */
};

timeout=NULL:select将一直阻塞,直到某个文件描述符就绪。
tv_sec0 || tv_usec=0:select立即返回。
select在等待期间,若程序接受到信号,则select立即返回-1,error为EINTR


以下是一个select用法小案列:

int main(int argc, char *argv[])
{
    if (argc <= 2)
    {
        DBG_PRINT("main parameter error");
        return -1;
    }
    // 打印运行程序名
    DBG_PRINT("parameter right, run procedure %s", basename(argv[0]));

    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in addr = {};
    addr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &addr.sin_addr);
    addr.sin_port = htons(port);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bind(sockfd, (const struct sockaddr *)&addr, sizeof(addr));

    listen(sockfd, 5);

    struct sockaddr_in client;
    socklen_t client_addr_len = sizeof(client);

    int connfd = accept(sockfd, (struct sockaddr *)&addr, &client_addr_len);
    if (connfd < 0) {
        DBG_PRINT("error is %d", errno);
        close(sockfd);
    }

    char buf[1024] = {};
    fd_set read_fds;
    fd_set exception_fds;
    FD_ZERO(read_fds);
    FD_ZERO(exception_fds);

    int ret = -1;
    while (1) {
        memset(buf, 0, sizeof(buf));

        // 每次调用select前都要重新在fd_set变量中设置文件描述符connfd
        FD_SET(connfd, &read_fds);
        FD_SET(connfd, &exception_fds);
        ret = select(connfd + 1, &read_fds, NULL, &exception_fds, NULL);
        if (ret < 0)
            break;

        // 对于可读事件,普通的recv
        if (FD_ISSET(connfd, &read_fds)) {
            ret = recv(connfd, buf, sizeof(buf) - 1, 0);
            if (ret < 0)
                break;
        }
        // 对于异常事件,采用带MSG_OOB标志的recv
        else if (FD_ISSET(connfd, &read_fds)) {
            ret = recv(connfd, buf, sizeof(buf) - 1, SG_OOB);
            if (ret < 0)
                break;
        }
    }

    // 释放句柄
    close(connfd);
    close(sockfd);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中,IPC(Inter-Process Communication,进程间通信)是一种机制,让操作系统内的不同进程之间交换数据和同步活动。Linux支持多种类型的IPC,包括但不限于: 1. **共享内存(Shared Memory)**: 使用`shm_open()`、`mmap()`等函数创建和访问共享内存区域。 2. **信号量(Semaphores)**: 通过`semctl()`、`semop()`命令管理和同步对公共资源的访问。 3. **消息队列(Message Queues)**: 使用`mq_open()`、`mq_send()`和`mq_recv()`等函数在不同进程间传递消息。 4. **命名管道(FIFO)**: 也称为“套接字管道”(Unix Domain Sockets),通过`mkfifo()`创建,`open()`和`read/write()`操作来进行进程间的通信。 5. **信号(Signals)**: 程序之间的简单通知方式,通过发送和接收信号实现通信。 要查看Linux系统的IPC资源,你可以使用以下几种工具: - **`ipcs`**:显示当前系统中的IPC状态,比如所有进程使用的信号量、消息队列、共享内存等。 ```sh ipcs -l ``` - **`msgstat` 和 `msgrcv`**:查看消息队列的信息,如队列大小和内容。 ```sh msgstat [queue_name] msgrcv [-q queue_name] -t type -f format -s size -i id ``` - **`semstat` 和 `semctl`**:查看信号量的相关信息。 ```sh semstat [Semaphore_ID] semctl [Semaphore_ID] cmd option arg [...] ``` - **`cat /proc/pid/maps`**:查看进程映射的内存,其中包括可能存在的共享内存段。 - **`lsmod` 和 `/sys/module`**:查看内核模块,它们可能包含一些私有的IPC机制。 注意,对于某些类型(如FIFO和套接字),实际的通信行为可以通过`lsof`命令查看哪些进程正与之交互。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值