本文是作者在学习网络编程的时候,收集整理的常用的函数方法方便以后查阅,但是并没有写关于网络编程的思想和解析,所以并不适合纯小白学习。
作者水平有限,所以写的有点垃圾,还请各位大佬高抬贵手o((>ω< ))o。
一、TCP和UDP的常用函数
在使用 TCP(传输控制协议)进行套接字通信时,以下是一些常用的函数,以及它们的参数和返回值的作用。这些函数在 C 语言中使用,但 C++ 中也可以使用,只是可能会使用 C++ 标准库的封装。使用前需要添加头文件:
#include <sys/types.h> // 通用数据类型
#include <sys/socket.h> // 套接字相关函数和数据结构
#include <netinet/in.h> // IPv4 地址结构
#include <arpa/inet.h> // IP 地址转换函数(使用这个包含了上面三个,所以加了这个上面就可以不加了)
#include <unistd.h> // 提供 close 函数
1.socket() 函数: 创建一个套接字。
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
返回:一个非负整数套接字描述符,或者 -1 表示出错。
-
domain:协议族,例如 AF_INET 表示 IPv4。
domain domain参数 作用 AF_UNSPEC 未指定协议族 AF_UNIX
或AF_LOCAL
UNIX 域协议族 AF_INET IPv4 协议族 AF_INET6 IPv6 协议族 AF_NETLINK Linux 内核与用户空间通信 AF_X25 X.25 协议族 AF_AX25 AX.25 协议族 AF_ATMPVC ATM PVC 协议族 AF_PACKE 用于底层网络访问 AF_BLUETOOTH 蓝牙协议族 AF_CAN Controller Area Network(CAN)协议族 AF_RDS Reliable Datagram Sockets(RDS)协议族 AF_PPPOX Point-to-Point Protocol over Ethernet(PPPoE)协议族 AF_XDP 高性能网络数据包处理 AF_SECURITY Linux 安全模块协议族 -
type:套接字类型,如 SOCK_STREAM 表示 TCP。
套接字类型 作用 SOCK_STREAM 流套接字,用于可靠的、面向连接的通信,如 TCP SOCK_DGRAM 数据报套接字,用于无连接、不可靠的通信,如 UDP SOCK_SEQPACKET 用于提供顺序数据包的顺序化、可靠的通信 SOCK_RAW 用于原始套接字,允许用户处理底层协议头,通常需要管理员权限 SOCK_RDM 用于可靠数据报传输 不同的domain,会使即使相同的type有着不同的效果
协议簇 套接字类型 作用 AF_INET
(IPv4) 和AF_INET6
(IPv6)SOCK_STREAM 流套接字,用于可靠的、面向连接的通信,如 TCP AF_INET
(IPv4) 和AF_INET6
(IPv6)SOCK_DGRAM 数据报套接字,用于无连接、不可靠的通信,如 UDP AF_UNIX
或AF_LOCAL
SOCK_STREAM 用于 UNIX 域流套接字 AF_UNIX
或AF_LOCAL
SOCK_DGRAM 用于 UNIX 域数据报套接字 -
protocol:具体的协议,通常为 0,表示根据 domain 和 type 自动选择合适的协议。
协议 作用 IPPROTO_IP IP 协议,用于通用的网络通信 IPPROTO_TCP TCP 协议,用于可靠的、面向连接的通信 IPPROTO_UDP UDP 协议,用于无连接、不可靠的通信 IPPROTO_IPV6 IPv6 协议 IPPROTO_ICMP ICMP 协议,用于网络控制消息,如 ping IPPROTO_ICMPV6 IPv6 的 ICMP 协议 IPPROTO_OSPF OSPF 协议,用于开放最短路径优先路由 IPPROTO_ROUTING 路由信息协议 IPPROTO_PIM PIM 协议,用于组播路由 IPPROTO_GRE 通用路由封装协议
2.bind() 函数: 将套接字绑定到特定的 IP 地址和端口号。
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回:0 表示成功,-1 表示出错。
这里addr实则是类型struct sockaddr_in的指针,这是因为c语言的历史原因,所以每次传参数的时候都需要强制转化成struct sockaddr的指针,下面需要传递sockaddr*的函数也是同理。
#include <netinet/in.h>
struct sockaddr_in {
sa_family_t sin_family; // 协议族(Address Family)
in_port_t sin_port; // 端口号(网络字节序) 实为uint16_t
struct in_addr sin_addr; // IPv4 地址结构
unsigned char sin_zero[8]; // 不使用,填充使结构体大小和 sockaddr一致
};
struct in_addr
{
in_addr_t s_addr;//IPV4 地址结构 实为uint32_t
};
-
socket:套接字描述符。
-
addr
:指向sockaddr
结构体的指针,表示要绑定的地址。 -
addrlen
:sockaddr
结构体的大小。
3.listen() 函数: 将套接字设置为监听状态,等待连接请求。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
返回:0 表示成功,-1 表示出错。
-
sockfd
:套接字描述符。 -
backlog
:等待连接队列的最大长度。
4.accept() 函数: 接受连接请求并创建新的套接字用于通信。
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回:新的套接字描述符,或 -1 表示出错。
sockfd
:监听套接字描述符。addr
:用于存储连接方的地址信息。addrlen
:输入时指定addr
缓冲区的大小,输出时为实际地址长度。
5.connect() 函数: 建立与远程服务器的连接。
#include<sys/socket.h>
#include<sys/types.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回:0 表示成功,-1 表示出错。
sockfd
:套接字描述符。addr
:服务器的地址信息。addrlen
:sockaddr
结构体的大小。
6.send、write、sendto、sendmsg、sendmsg函数: 发送数据。
send()函数
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
返回:发送的字节数,或 -1 表示出错。
-
sockfd
:套接字描述符。 -
buf
:发送缓冲区的指针。 -
len
:发送数据的字节数。 -
flags
:可选标志,一般填0即可。 -
flags常用标志 可选标志 作用 MSG_DONTROUTE 不使用路由表来选择目标地址,直接发送数据到目标主机 MSG_OOB 发送带外数据(Out-of-Band data),通常用于紧急数据传输 MSG_NOSIGNAL 在发送失败时不产生 SIGPIPE
信号。通常在处理已关闭连接时使用MSG_CONFIRM 用于发送数据确认,通常在数据包丢失时重新发送 MSG_MORE 指示后续数据将被发送,但是不触发实际发送操作,适用于发送多个数据片段 MSG_WAITALL 要求 recv
或recvfrom
函数等待直到接收到指定数量的数据MSG_DONTWAIT 非阻塞发送,即使发送缓冲区不足,也立即返回
write()函数
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
返回值:
如果成功写入,返回实际写入的字节数(可能小于 count
)。
如果出现错误,返回 -1,并设置 errno
来指示错误类型。
errno
定义在<errno.h>
头文件中,是一个整数类型(通常是int
或者errno_t
)的全局变量,通常被用于指示函数调用发生错误时的错误类型。在 C 和 C++ 编程中,当系统调用或库函数发生错误时,它们通常会设置errno
的值以表示错误的类型,从而使程序能够根据错误类型采取适当的处理措施。常见的errno常量:
errno常见的错误类型常量 常量 作用 EACCES 权限不足,无法访问文件或资源 EAGAIN 资源暂时不可用,通常与非阻塞操作有关 EBADF 无效的文件描述符 EINVAL 无效的参数 EMFILE 打开的文件描述符数达到系统限制 ENFILE 系统打开的文件数达到系统限制 ENOENT 文件或路径不存在 ENOMEM 内存不足 ENOSPC 没有足够的空间来执行操作 ENOTTY 不是终端设备 EPERM 操作被拒绝,通常与权限有关 EPIPE 管道或套接字连接已经被关闭 ESRCH 没有找到与给定标识符相匹配的进程
fd
:文件描述符(或套接字),指定要写入的目标。buf
:要写入的数据的指针。count
:要写入的字节数。
sendto()函数
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
返回值:
如果成功发送数据,返回实际发送的字节数。
如果出现错误,返回 -1,并设置 errno
来指示错误类型。
sockfd
:套接字文件描述符。buf
:要发送的数据的指针。len
:要发送的字节数。flags
:控制发送的标志位,通常可以设置为 0。dest_addr
:目标地址的sockaddr
结构指针。addrlen
:目标地址结构的大小。
sendmsg()函数
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
返回值:
如果成功发送数据,返回实际发送的字节数。
如果出现错误,返回 -1,并设置 errno
来指示错误类型。
sockfd
:套接字文件描述符。msg
:指向msghdr
结构的指针,包含要发送的数据和控制信息。flags
:控制发送的标志位,通常可以设置为 0。msgh
dr
结构定义如下:-
struct msghdr { void *msg_name; // 指向目标地址的指针 socklen_t msg_namelen; // 目标地址的大小 struct iovec *msg_iov; // 数据缓冲区数组的指针 size_t msg_iovlen; // 数据缓冲区数组的元素数量 void *msg_control; // 控制信息的指针 socklen_t msg_controllen; // 控制信息的大小 int msg_flags; // 用于接收控制标志 }; struct iovec { void *iov_base; // 数据缓冲区的起始地址 size_t iov_len; // 数据缓冲区的长度 };
常见的控制信息:
struct cmsghdr
:这是一个通用的控制消息头部结构,它被用于封装各种类型的控制信息。SCM_RIGHTS
:用于传递文件描述符(文件句柄)的控制信息。包括在struct cmsghdr
的cmsg_type
字段中,通过struct cmsg_rights
结构体传递文件描述符。struct cmsg_rights { int fd; };
SCM_CREDENTIALS
:用于传递进程凭证(用户 ID、组 ID 等)的控制信息。包括在struct cmsghdr
的cmsg_type
字段中,通过struct cmsg_cred
结构体传递进程凭证。struct cmsg_cred { struct ucred creds; }; struct ucred { pid_t pid; uid_t uid; gid_t gid; };
- 注意:
struct cmsghdr
结构的cmsg_len
字段指示整个控制消息的长度,可以根据不同的控制消息类型进行调整。
-
温馨提示:如果发送的消息太长,不能以原子形式通过底层协议,则返回EMSGSIZE。并且不发送这个消息。
7.recv、recvfrom、read函数: 接收数据。
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
返回:接收的字节数,0 表示连接关闭,-1 表示出错。
sockfd
:套接字描述符。buf
:接收缓冲区的指针。len
:缓冲区大小。
flags
:可选标志,一般填0即可。部分与send的可选标志相似。
可选标志 | 作用 |
---|---|
MSG_PEEK | 预览数据,但不从输入队列中删除数据。通常用于查看下一个数据包而不实际接收它 |
MSG_WAITALL | 要求 recv 函数等待,直到接收到指定数量的字节。如果没有足够的数据可用,会一直等待直到指定数量的数据到达 |
MSG_OOB | 接收带外数据(Out-of-Band data),通常用于紧急数据传输 |
MSG_TRUNC | 如果接收到的数据比缓冲区大,则截断数据而不报告错误 |
MSG_DONTWAIT | 非阻塞接收,即使没有数据可用,也立即返回 |
MSG_ERRQUEUE | 接收错误消息,例如 ICMP 错误消息,通常在套接字设置为接收错误消息时使用 |
MSG_CMSG_CLOEXEC | 设置传递辅助数据的文件描述符为 CLOEXEC (子进程关闭) |
MSG_CTRUNC | 接收的控制消息被截断 |
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
返回值:
如果成功接收数据,返回实际接收的字节数。
如果连接被关闭,返回 0。
如果出现错误,返回 -1,并设置 errno
来指示错误类型。
sockfd
:套接字文件描述符。buf
:接收数据的缓冲区指针。len
:缓冲区的长度。flags
:控制接收的标志位,通常可以设置为 0。src_addr
:用于存储发送者地址信息的sockaddr
结构指针。如果不需要这个信息,可以传递NULL
。addrlen
:传入的是src_addr
结构体的大小(以字节为单位),传出时表示实际的发送者地址长度。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
返回值:
如果成功读取数据,返回实际读取的字节数。
如果已到达文件末尾,返回 0。
如果出现错误,返回 -1,并设置 errno
来指示错误类型。
fd
:文件描述符,指定要读取的文件或套接字。buf
:用于存储读取数据的缓冲区指针。count
:要读取的字节数。
8.close() 函数: 关闭套接字。
#include <unistd.h>
int close(int sockfd);
返回:0 表示成功,-1 表示出错。
sockfd
:套接字描述符。
9.shutdown()函数:关闭套接字的一个或两个方向的通信。
#include <sys/socket.h>
int shutdown(int sockfd, int how);
返回值:
如果成功关闭,返回 0。
如果出现错误,返回 -1,并设置 errno
来指示错误类型。
sockfd
:套接字文件描述符。how
:关闭的方式,可以是以下值之一:SHUT_RD
:关闭读取方向,不再接收数据。SHUT_WR
:关闭写入方向,不再发送数据。SHUT_RDWR
:同时关闭读取和写入方向。
10.getsockname()、getpeername()函数
#include <sys/types.h>
#include <sys/socket.h>
//sockfd 套接字的本地地址信息填充到 addr 缓冲区中,以及更新 addrlen 的值。这样,你就可以在 addr //缓冲区中找到套接字的本地地址信息,包括 IP 地址和端口号等。
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//函数的作用是将 sockfd 套接字的远程对等方地址信息填充到 addr 缓冲区中,以及更新 addrlen 的值。 //这样,你就可以在 addr 缓冲区中找到已连接套接字的远程对等方地址信息,包括远程 IP 地址和端口号等。
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:
getsockname函数:
如果函数成功执行,返回值为0,表示成功获取了本地套接字的地址信息。
如果函数执行失败,返回值为-1,表示发生了错误,可以通过查看 errno
变量来获取具体的错误信息。
-
sockfd
:是一个套接字描述符,它指定了要获取本地地址信息的套接字。 -
addr
:是一个指向struct sockaddr
结构体的指针,用于接收本地地址信息。 -
addrlen
:是一个指向socklen_t
类型的指针,指定了addr
缓冲区的大小。当函数成功执行后,addrlen
会被设置为addr
缓冲区的实际大小。
getpeername函数:
如果函数成功执行,返回值为0,表示成功获取了远程对等方的套接字的地址信息。
如果函数执行失败,返回值为-1,表示发生了错误,可以通过查看 errno
变量来获取具体的错误信息。
-
sockfd
:是一个套接字描述符,它指定了要获取远程对等方信息的套接字。 -
addr
:是一个指向struct sockaddr
结构体的指针,用于接收远程对等方信息。 -
addrlen
:是一个指向socklen_t
类型的指针,指定了addr
缓冲区的大小。当函数成功执行后,addrlen
会被设置为addr
缓冲区的实际大小。
二、常见的本地字节序和网络字节序的转化函数
1.htons、ntohs、htonl、ntohl函数
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);//将主机字节序的 16 位整数转换为网络字节序
uint32_t htonl(uint32_t hostlong);//将主机字节序的 32 位整数转换为网络字节序
uint16_t ntohs(uint16_t netshort);//将网络字节序的 16 位整数转换为主机字节序
uint32_t ntohl(uint32_t netlong);//将网络字节序的 32 位整数转换为主机字节序
hostshort
,要转换的 16 位主机字节序整数hostlong
,要转换的 32 位主机字节序整数netshort
,要转换的 16 位网络字节序整数netlong
,要转换的 32 位网络字节序整数
记忆技巧:
h:host--本地主机 n:net--网络 s:short--unsigned short类型
l:long--unsigned long类型
2.inet_addr、inet_aton、inet_ntop函数
#include<arpa/inet.h>
/*将点分十进制 IPv4 地址字符串转换为网络字节序表示的 32 位整数 IP 地址。适用于 IPv4 地址转换。如果转换失败(无效的 IP 地址格式),返回 INADDR_NONE(通常为 0xFFFFFFFF,-1)*/
in_addr_t inet_addr(const char *cp);
/*将点分十进制 IPv4 地址字符串转换为网络字节序表示的 IP 地址,并保存在 inp 指向的结构体中。适用于 IPv4 地址转换。如果转换成功,返回非零值,否则返回零*/
int inet_aton(const char *cp, struct in_addr *inp);
/*将网络字节序表示的 IP 地址转换为点分十进制 IP 地址字符串,并存储在提供的缓冲区中。适用于 IPv4 和 IPv6 地址转换。如果转换成功,返回指向 dst 缓冲区的指针;如果发生错误,返回 NULL*/
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);//
cp
,一个指向点分十进制 IPv4 地址字符串的指针。inp
:一个指向struct in_addr
结构体的指针,用于保存转换后的 IP 地址。
这里的struct in_addr结构体,就是前面提到的struct sockaddr_in里面的成员sin_addr的类型
af
:地址族,可以是AF_INET
(IPv4)或AF_INET6
(IPv6)。src
:一个指向网络字节序表示的 IP 地址的指针。dst
:一个用于存储转换后的 IP 地址字符串的缓冲区。size
:缓冲区dst
的大小。
三、设置套接字为阻塞态或非阻塞态
#include<fcntl.h>
/*设置套接字为阻塞态*/
int flag=fcntl(socket_fd,F_GETFL,0);//获取当前文件描述符标志
flag|=O_NONBLOCK;//设置为非阻塞标志
fcntl(socket_fd,F_SETFL,flag);//应用新文件描述符标志
/*设置套接字为非阻塞*/
int flag=fcntl(socket_fd,F_GETFL,0);//获取当前文件描述符标志
flag&=~O_NONBLOCK;//设置为阻塞标志
fcntl(socket_fd,F_SETFL,flag);//应用新文件描述符标志
1.fcntl()函数:用于对文件描述符进行各种控制操作的系统调用
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */);//这里的"... /* arg */"表示 fcntl 函数的第三个参数,该参数是一个可变参数。
返回值:根据操作的不同,返回值也不同。
成功:根据操作不同而有所不同。例如,对于设置文件状态标志,返回 0;对于获取文件状态标志,返回当前标志值;其他情况可能有不同的返回值。失败:返回 -1,并设置 errno
来表示错误类型。
fd
:文件描述符,表示要操作的文件或套接字。cmd
:控制命令,指定要执行的操作。arg
:根据cmd
的不同,可能需要传入不同类型的参数。具体参数类型和含义取决于cmd
。
控制命令常量 | 作用 |
---|---|
F_DUPFD | 复制文件描述符 |
F_GETFD | 获取文件描述符标志 |
F_SETFD | 设置文件描述符标志 |
F_GETFL | 获取文件状态标志 |
F_SETFL | 设置文件状态标志 |
F_GETLK | 获取文件锁 |
F_SETLK | 设置文件锁 |
F_SETLKW | 设置文件锁,如果锁不可用则等待 |
- 对于
F_DUPFD
,arg
可能是要创建的新文件描述符的最小值。- 对于
F_GETFD
和F_SETFD
,arg
可能是文件描述符标志的值。- 对于
F_GETFL
和F_SETFL
,arg
可能是文件状态标志的值。- 对于
F_GETLK
、F_SETLK
和F_SETLKW
,arg
可能是指向struct flock
结构体的指针。
2. O_NONBLOCK
O_NONBLOCK
是一个文件状态标志(file status flag),用于设置文件描述符为非阻塞模式。在网络编程中,这个标志常用于设置套接字(socket)为非阻塞模式,使得读写操作不会阻塞等待数据的到来或写入的完成。
除了 O_NONBLOCK
外,还有一些类似的文件状态标志,它们用于设置文件描述符的不同属性。以下是一些常见的文件状态标志:
文件状态标志 | 作用 |
---|---|
O_RDONLY | 以只读模式打开文件 |
O_WRONLY | 以只写模式打开文件 |
O_RDWR | 以读写模式打开文件 |
O_APPEND | 在写入文件时追加到文件末尾 |
O_CREAT | 如果文件不存在,则创建文件 |
O_TRUNC | 如果文件存在,将文件截断为空 |
O_EXCL | 与 O_CREAT 一起使用,如果文件已经存在,返回错误 |
O_SYNC | 每次写操作都需要等待物理 I/O 完成 |
这些标志可以像上面设置套接字为非阻塞和阻塞态那样,通过逻辑 OR 运算组合在一起,用于打开文件或设置文件描述符的属性。
四、setsockopt、getsockopt函数
1.setsockopt()函数:设置套接字选项
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
返回值:如果设置成功,返回 0;如果设置失败,返回 -1,并设置 errno
。
sockfd
:套接字文件描述符。level
:选项所属的协议层,通常是SOL_SOCKET
(套接字选项)或特定的协议级别(如IPPROTO_TCP
、IPPROTO_IP
等)。
协议 | 作用 |
---|---|
SOL_SOCKET | 套接字选项,用于设置套接字相关的选项 |
IPPROTO_TCP | TCP 协议级别,用于设置 TCP 协议相关的选项 |
IPPROTO_IP | IP 协议级别,用于设置 IP 协议相关的选项 |
IPPROTO_IPV6 | IPv6 协议级别,用于设置 IPv6 协议相关的选项 |
IPPROTO_UDP | UDP 协议级别,用于设置 UDP 协议相关的选项 |
IPPROTO_ICMP | ICMP 协议级别,用于设置 ICMP 协议相关的选项 |
optname
:要设置的选项的名称。
协议 | 选项的名称 | 作用 |
---|---|---|
SOL_SOCKET | SO_REUSEADDR | 允许多个套接字绑定到相同的 IP 地址和端口 |
SO_KEEPALIVE | 启用或禁用 TCP keep-alive 机制 | |
SO_LINGER | 控制关闭套接字时的行为 | |
SO_RCVBUF | 设置接收缓冲区大小 | |
SO_SNDBUF | 设置发送缓冲区大小 | |
SO_RCVTIMEO | 设置接收超时时间 | |
SO_SNDTIMEO | 设置发送超时时间 | |
IPPROTO_TCP | TCP_NODELAY | 禁用 Nagle 算法,降低数据传输延迟 |
IPPROTO_IP | IP_TTL | 设置 IP 包的 TTL(Time-to-Live)值 |
IPPROTO_IPV6 | IPV6_V6ONLY | 限制套接字仅接受 IPv6 连接 |
IPPROTO_UDP | UDP_CORK | 设置套接字发送缓冲区策略,用于合并小数据包 |
IPPROTO_ICMP | ICMP_FILTER | 设置 ICMP 包过滤规则 |
optval
:一个指向要设置的选项值的指针。如果该指针指向的值为
对于大多数选项,
optval
是一个指向某种特定数据类型的指针。这个指针可以指向一个特定的数据值,用于指定选项的新值。例如,对于整数选项,optval
可能是指向int
类型的指针,用于指定整数选项的新值。对于一些特殊的选项,
optval
可能需要指向一个特定的数据结构,而不仅仅是一个单一的数据值。例如,对于SO_LINGER
选项,optval
可能需要指向一个struct linger
结构体的指针,该结构体包含了控制关闭套接字时的行为的信息。对于一些二进制选项,你可能需要设置
optval
为特定的二进制值,用于启用或禁用选项。例如,对于SO_REUSEADDR
选项,你可以设置optval
为一个int
类型的值,1
表示启用选项,0
表示禁用选项。
SO_REUSEADDR
:
optval
值:int
类型的值,通常是0
或1
。- 含义:
1
表示启用地址重用,允许多个套接字绑定到同一个地址;0
表示禁用。
SO_KEEPALIVE
:
optval
值:int
类型的值,通常是0
或1
。- 含义:
1
表示启用 TCP keep-alive 机制;0
表示禁用。
SO_LINGER
:
optval
值:struct linger
结构体的指针。- 含义:
struct linger
结构体包含两个成员,l_onoff
控制是否启用SO_LINGER
,l_linger
控制关闭延迟的时间。
SO_RCVBUF
和SO_SNDBUF
:
optval
值:int
类型的值,表示缓冲区大小。- 含义:设置接收或发送缓冲区的大小。
SO_RCVTIMEO
和SO_SNDTIMEO
:
optval
值:struct timeval
结构体的指针。- 含义:
struct timeval
结构体包含两个成员,tv_sec
表示秒数,tv_usec
表示微秒数,用于设置接收或发送超时时间。
optlen
:optval
的长度。
2.getsockopt()函数
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
返回值:如果获取成功,返回 0;如果获取失败,返回 -1,并设置 errno
。
sockfd
:套接字文件描述符。level
:选项所属的协议层,通常是SOL_SOCKET
(套接字选项)或特定的协议级别(如IPPROTO_TCP
、IPPROTO_IP
等)。optname
:要获取的选项的名称。optval
:一个指向用于存储选项值的缓冲区的指针。optlen
:指向一个整数的指针,表示optval
缓冲区的大小。在调用时,它应该是optval
缓冲区大小的初始值,函数会更新它为实际读取的选项值的大小。
五、select、poll、epoll
1.select
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
返回值:-1表示失败,0超时到达,大于0返回就绪文件描述符的总数。
nfds
:最大文件描述符值加 1。readfds
:用于检查读事件的文件描述符集合。
fd_set
是一个宏,通常定义为一个数组,用于存储位信息,表示每个文件描述符的状态。typedef struct fd_set { uint32_t fds_bits[FD_SETSIZE / 32]; } fd_set;
使用时只需要像其他类型一样去声明变量,然后把指针传给select函数就行。
要想修改fd_set的值可以使用以下函数去修改:
void FD_CLR(int fd, fd_set*set);//将参数文件描述符fd对应标志设置为0
int FD_ISSET(int fd, fd_set *set);//判断fd对应的标志为是0还是1,标志位位是0返回0,是1返回1
int FD_SET(int fd, fd_set*set);//将参数文件描述符fd对应的标志位设置为1
void FD_ZERO(fd_set*set);//将fd_set1024个标志位全部设置为0
使用
FD_SET
宏将你希望监听的文件描述符对应的标志位设置为 1。这将在fd_set
结构中对应的位上设置为 1。传入select函数后,在select
函数返回后,你可以使用FD_ISSET
宏来检查特定文件描述符的标志位是否被设置为 1,如果返回的结果是真,说明对应的文件描述符可以进行相应的操作,下面FD_SET同上。
writefds
:用于检查写事件的文件描述符集合。exceptfds
:用于检查异常事件的文件描述符集合。
这里的异常事件又常常叫带外数据。
带外数据(Out-of-Band Data)在网络编程中,指的是一种特殊的数据传输方式,这种方式不依赖于常规的数据流或数据包。带外数据通常用于传输那些需要立即注意或处理的信息,例如网络中断、紧急事件等。
带外数据的传输不依赖于网络协议栈的正常处理流程,它可以直接发送到目标设备,无需排队等待。因此,带外数据通常被用于处理网络中断或紧急情况,例如重新启动网络设备、更新软件等。
在编程中,带外数据可以通过特殊的系统调用或库函数进行传输和处理。不同的操作系统和编程语言可能提供不同的接口和方法来操作带外数据。
timeout
:超时时间。
传入参数结构体指针为NULL:永久阻塞,直到检测到文件描述符有变化
结构体的值为-tv_sec==0,tv_usec==0:不阻塞
-tv_sec>0,tv_usec>0:对应的阻塞时间
struct timeval
{
long tv_sec;//秒
long tv_usec;//毫秒
};
2.poll
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
返回值:-1表示失败,0表示无事发生,正整数表示有多少个文件描述符已经准备好进行读或写操作。
fds
:struct pollfd
数组,每个元素包含文件描述符和事件。
struct pollfd
{
int fd; // 要监视的文件描述符
short events; // 感兴趣的事件
short revents; // 返回的事件
};
事件 | 常值 | 作为evenets的值 | 作为evenets的值 | 说明 |
---|---|---|---|---|
读时间 | POLLIN | √ | √ | 普通或优先带数据可读 |
POLLRDNORM | √ | √ | 普通数据可读 | |
POLLRDBAND | √ | √ | 优先带数据可读 | |
POLLPRI | √ | √ | 高优先带数据可读 | |
写事件 | POLLOUT | √ | √ | 普通或优先带数据可写 |
POLLWRNORM | √ | √ | 普通数据可写 | |
POLLWRBAND | √ | √ | 优先带数据可写 | |
错误事件 | POLLERR | √ | 发生错误 | |
POLLHUP | √ | 发生挂起 | ||
POLLNVAL | √ | 描述不是打开的文件 |
nfds
:数组中的文件描述符数量。timeout
:超时时间。timeout为0:不阻塞,-1:阻塞,当描述符变化时,解除阻塞,>0,阻塞时长。
3.epoll
#include <sys/epoll.h>
int epoll_create(int size);//创建 epoll 实例
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);//注册、修改或删除文件描述符
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);//等待事件发生:
epoll_create:
返回值:返回一个非负的文件描述符,用于标识创建的 epoll
实例。
size
:表示你希望内核为epoll
实例分配的大小。这个参数在大多数情况下可以被忽略,可以传入任意值。
epoll_ctl:
返回值:在操作成功时返回 0,表示操作已成功执行。在操作失败时,返回 -1,并设置全局变量 errno
来指示错误类型。
epfd
:之前创建的epoll
实例的文件描述符。op
:表示操作类型,可以是以下值之一:EPOLL_CTL_ADD
:注册文件描述符。EPOLL_CTL_MOD
:修改文件描述符的监视事件。EPOLL_CTL_DEL
:删除文件描述符的监视。
fd
:要注册、修改或删除的文件描述符。event
:指向一个struct epoll_event
结构,用于描述要监视的事件。
struct epoll_event
{
uint32_t events; // 表示对文件描述符的事件
epoll_data_t data; // 与文件描述符或相关的数据
};
typedef union epoll_data
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data_t;
epoll事件 | 作用 |
---|---|
EPOLLIN | 可读事件。表示文件描述符已经准备好读取操作,可以从中读取数据 |
EPOLLOUT | 可写事件。表示文件描述符已经准备好写入操作,可以向其中写入数据 |
EPOLLET | 边缘触发模式。表示使用边缘触发模式,只有在文件描述符状态发生变化时才会触发事件,而不是数据就绪 |
EPOLLPRI | 有紧急数据可读事件。用于表示带外数据到达,通常在 TCP 连接中用于紧急数据的传递 |
EPOLLERR | 错误事件。表示文件描述符发生错误,通常需要通过 errno 来获取具体错误信息 |
EPOLLHUP | 挂起事件。表示文件描述符被挂起,例如连接关闭,或者管道被破裂 |
EPOLLONESHOT | 只监听一次事件。当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。 |
注意:这里是没有EPOLLLT这个常量的,如果你想要从边缘触发改为水平触发的话只能设置把events的值覆盖掉,或去掉events里面的EPOLLET这个值。
在啰嗦一句:结构体的epoll事件都针对你设置那个结构体里面保存的文件描述符,即使是EPOLLET、EPOLLONESHOT也是。
epoll_wait:
返回值:成功返回变化文件描述符的个数,失败为-1
epfd
:之前创建的epoll
实例的文件描述符。events
:指向一个struct epoll_event
数组,用于存储发生的事件。其中data.fd是保存的是文件描述或与其相关的数据maxevents
:events
数组的大小,表示最多可以存储多少个事件。timeout
:等待事件的超时时间,单位是毫秒。0:是不阻塞,-1:阻塞直到fd数据变化解除阻塞。>0:阻塞的时间。
六、 高级I/O函数
1.pipe()和socketpair():创建无名管道
pipe函数用于创建半双工的匿名管道,以实现进程间的通信。
#include<unistd.h>
int pipe(int fd[2]);
返回值:成功时返回0,失败返回值为-1并设置errno
- fd[2]:int数组,函数成功调用时,fd将会把一对文件描述符填入数组,fd[1]用于写入数据,fd[0]用于读出数据。
注意:如果fd[1]引用次数为0,针对该管道的读端文件描述符的read操作将返回0,fd[0]引用次数为0,针对该管道的读端文件描述符的write操作将返回0。
匿名(无名)通道只能用于有亲缘关系的进程之间。
socketpair函数用于创建一对无名的、相互连接的套接子。这对套接子会自动建立连接,且连接是全双工的,即两个描述符既可以读也可以写。
#include<sys/types.h>
#include<sys.socket.h>
int socketpair(int domain,int type,int protocol,int fd[2]);
返回值:成功时返回0,失败返回值为-1并设置errno
socketpair()函数前三个参数与socket系统调用完全相同。
- domain:协议族,必须为AF_LOCAL。
- type:类型,既可以是SOCK_STREAM,又可以是SOCK_DGRAM。当参数指定为SOCK_STREAM时,得到的结果称为流管道,它与一般管道的区别是留管道是全双工的,即两个描述符即可读有可写。
- protocol:只能是0。
- fd[0]和fd[1]:用于保存创建的套接字对,其中fd[0]和fd[1]分别是两个套接字的文件描述符。
注意:socketpair函数和pipe函数类似,只能在具有亲缘关系的进程间通信。
2.mkfifo():创建有名管道
使用mkfifo()函数创建命名管道(也称为有名管道)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int mkfifo(const char *pathname, mode_t mode);
返回值:成功,函数返回0;否则,返回-1并设置errno以指示错误。
pathname
:要创建的命名管道的路径名,可以是一个文件路径,但必须是唯一的。mode
:权限模式,用于设置管道文件的访问权限。
宏名 | 对应8进制的值 | 作用 |
---|---|---|
S_IRUSR | 0o400 | 表示拥有者有读取权限 |
S_IWUSR | 0o200 | 表示拥有者有写入权限 |
S_IXUSR | 0o100 | 表示拥有者有执行权限 |
S_IRGRP | 0o040 | 表示与拥有者同组的用户有读取权限 |
S_IWGRP | 0o020 | 表示与拥有者同组的用户有写入权限 |
S_IXGRP | 0o010 | 表示与拥有者同组的用户有执行权限 |
S_IROTH | 0o004 | 表示其他用户有读取权限 |
S_IWOTH | 0o002 | 表示其他用户有写入权限 |
S_IXOTH | 0o001 | 表示其他用户有执行权限 |
注意:这个函数是真的会创建一个文件在系统上,然后通过这个文件来实现通信,之后你也可以访问这个文件。
3. dup()和dup2():用于文件描述符重定向
dup()创建一个新的文件描述符,新文件描述符和原文件描述符指向相同的文件、管道或者网络连接。
#include<unistd.h>
int dup(int oldfd);
返回值:成功返回新的文件描述符;否则返回-1并设置errno。
- oldfd: 已存在的文件描述符。
dup2()会使第二个文件描述符指向与第一个文件描述符指向相同的文件、管道或者网络连接。,如果第二个文件描述符已经打开,则先关闭它。
#include<unistd.h>
int dup2(int oldfd, int newfd);
返回值:成功返回newfd;否则返回-1并设置errno。
oldfd:已存在的文件描述符。
newfd:想要重定向的文件描述符。
注意:通过dup和dup2创建的文件描述符并不继承源文件描述符的属性。
4.readv()和writev()函数
readv函数将数据从文件描述符读到分散的内存块中,即分散读;writev函数将多块分散的内存数据一并写入文件描述符中。它们相当于recvmsg和sendmsg函数。
#include<sys/uio.h>
ssize_t readv(int fd, const struct iovec*vector,int count);
ssize_t writev(int fd,const struct iovec*vector,int count);
返回值:成功返回实际读/写的字节数,如果发生错误则返回-1,并设置全局变量errno
- 'fd':文件描述符。这通常是你打开一个文件或者设备(例如,一个文件、网络套接字等)时得到的。它代表了要进行操作的文件或设备。
- 'vector':一个指向iovec结构体的指针,iovec结构体包含了数据缓冲区的信息。iovec结构体通常包含两个成员:'base'(指向数据的指针)和'len'(数据长度),可以看上面recvmsg函数有介绍这个结构体。
- 'count':iovec结构体的数量。这意味着readv函数最多可以处理多少个数据缓冲区。
5.mmap()和munmap()函数:内存映射
mmap函数用于申请一段内存空间。我们可以将这段内存作为进程间的通信的共享内存,也可以将文件直接映射到该内存中。mumap则是释放由mmap函数创建的这段内存空间。 在使用mmap函数前需要先打开已经存在的文件,由于mmap函数内存映射只能是内存页大小的整数倍,为了避免映射大小和文件大小不一致而出现的问题,可以使用ftruncate()函数来设置文件的大小或者使用lseek()函数来改变文件指针。如果你只需要访问在映射区域范围内的内存,就可以不使用这些函数。但如果你需要修改文件的大小或者移动文件指针,就需要使用这些函数来实现。
#include<sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);
返回值:mmap成功时,返回映射区域的起始地址,失败时,返回MAP_FAILED
(通常是(void *) -1
)。
munmap成功时,成功时,返回0
。失败时,返回-1
,并设置全局变量errno
以指示错误原因
addr
:映射的地址。通常设置为NULL
,让系统自动分配。length
:映射的长度,以字节为单位。prot
:映射区域的保护方式。可以是以下值的组合:(可读)、(可写)、(可执行)和(不可访问)。-
prot的四种取值 prot值 作用 PROT_READ
表示映射的这一段可读 PROT_WRITE
表示映射的这一段可写 PROT_EXEC
表示映射的这一段可执行 PROT_NONE
表示映射的这一段不可访问 flags
:映射区域的标志。可以是以下取值。
flag值 | 作用 |
---|---|
MAP_SHARED | 在进程间共享映射区域 |
MAP_PRIVATE | 创建一个私有的写时复制映射 |
MAP_FIXED | 使用指定的地址进行映射,如果地址不可用,则操作失败 |
MAP_ANONYMOUS | 创建一个匿名的映射区域,不与任何文件关联 |
MAP_POPULATE | 预先加载整个映射区域到物理内存中,以避免页面错误的开销 |
MAP_NONBLOCK | 仅在MAP_POPULATE标志指定时才有效。它表示在预加载过程中如果出现页面错误,不应该阻塞进程 |
MAP_GROWSDOWN | 用于栈映射,表示映射区域可以向下扩展 |
MAP_DENYWRITE | 允许对映射区域进行读取和写入操作,但禁止执行写入操作 |
MAP_EXECUTABLE | 允许对映射区域进行读取、写入和执行操作 |
MAP_LOCKED | 将映射区域的页面锁定在物理内存中,以防止它们被交换出去 |
MAP_NORESERVE | 不预留交换空间,即不保证映射区域能够在物理内存中保留 |
MAP_POPULATE_FORCE_PAGEALLOC | 强制为整个映射区域分配物理页面,即使某些页面可能不会被使用 |
MAP_POPULATE_FORCE_SWAP | 强制将整个映射区域交换到磁盘上,即使某些页面可能不会被使用 |
MAP_POPULATE_FORCE_SWAP_PGIN | 强制将整个映射区域交换到磁盘上,并且在预加载过程中使用页面错误处理机制 |
MAP_POPULATE_FORCE_SWAP_PGOUT | 强制将整个映射区域交换到磁盘上,并且在预加载过程中使用页面错误处理机制,同时在后续使用中也将页面交换出去 |
MAP_POPULATE_FORCE_PGIN | 强制为整个映射区域分配物理页面,并且在预加载过程中使用页面错误处理机制 |
MAP_POPULATE_FORCE_PGOUT | 强制为整个映射区域分配物理页面,并且在预加载过程中使用页面错误处理机制,同时在后续使用中也将页面交换出去 |
fd
:要映射的文件描述符。如果映射的是匿名内存,则设置为-1
。offset
:映射的偏移量。通常设置为0
。
6.sendfile()函数:用于两个文件描述符传递数据的“零拷贝”函数 (常用于网络中)
sendfile函数在两个文件描述符之间直接传递数据,零拷贝是指在用户空间与内核空间中零拷贝。而非真正的零拷贝。
#include<sys/sendfile.h>
ssize_t sendfile(int out_fd,int in_fd,off_t*offset,size_t count);
返回值:成功返回实际写入的字节数,如果发生错误则返回-1,并设置全局变量errno
out_fd
是你要写入数据的文件描述符。在某些情况下,这可能是网络套接字的文件描述符,这样你就可以将数据发送到网络上的另一个系统。in_fd
是你要读取数据的文件描述符。它必须指向一个真实的文件,而不能是套接字或管道。这是因为sendfile函数需要直接访问文件的数据,而不是通过其他方式(如套接字)进行传输。offset
参数允许你在文件中选择一个特定的位置开始读取数据。如果你不关心这个位置,可以将这个参数设置为NULL,此时函数会使用文件的默认起始位置。count
参数指定了你想要传输的字节数。这个数值不能超过你的进程允许的最大传输量。
7.splice()函数:用于两个文件描述符(其中必须有一个管道的文件描述符)之间移动数据,也是“零拷贝”操作
#include<fcntl.h>
ssize_t splice(int fd_in,loff_t*off_in,int fd_out,loff_t*off_out,size_t len,
unsigned int flags);
返回值:成功返回移动字节数据的数据,失败返回-1并设置errno。
fd_in
:输入文件描述符,指定要读取数据的文件。off_in
:输入文件的偏移量,指定从文件的哪个位置开始读取数据。如果设置为NULL
,则从文件的当前位置开始读取。fd_out
:输出文件描述符,指定要将数据写入的文件。off_out
:输出文件的偏移量,指定将数据写入到文件的哪个位置。如果设置为NULL
,则写入到文件的当前位置。len
:要传输的数据长度,以字节为单位。flags
:标志位,用于控制splice()
函数的行为。可以是以下值的组合:
flags值 | 作用 |
---|---|
SPLICE_F_MOVE | 将输入文件的数据移动到输出文件,而不是复制,只是给内核一个提示,据说在内核2.16.21后,它实际上没有任何效果了 |
SPLICE_F_NONBLOCK | 非阻塞模式,如果操作不能立即完成,则返回错误 |
SPLICE_F_MORE | 表示还有更多的数据传输操作 |
注意:
- 在使用splice函数时,fd_in和fd_out必须至少有一个是管道文件标识符
splice()
函数可以用于任何类型的文件描述符,包括管道、套接字、普通文件等。- 如果输入文件描述符和输出文件描述符引用的是同一个文件,那么数据将被复制到该文件的另一个位置。
8.tee()函数 :用于两个管道文件描述符之间复制数据,也是“零拷贝”操作
所以tee()函数不会消耗数据,源文件描述符上的数据仍然可以用于后续的读操作
#include<fcntl.h>
ssize_t tee(int fd_in,int fd_out,size_t len,unsigned int flags);
返回值:成功返回移动字节数据的数据,失败返回-1并设置errno。
tee函数的参数与splice相同只是fd_int,fd_out必须是管道文件描述符。
fd_in
:输入文件描述符,指定要读取数据的文件。fd_out
:输出文件描述符,指定要将数据写入的文件。len
:要传输的数据长度,以字节为单位。flags
:标志位,用于控制tee()
函数的行为,见splice函数中的flag的参数。
七、信号
1.kill()函数:向其他进程发送信号
#include<sys/types.h>
#include<signal.h>
int kill(pid_t pid,int sig);
函数返回值:成功时(至少发送了一个信号),返回0。失败时,返回-1,并设置errno
为相应的错误码
errno | 作用 |
---|---|
EINVAL | 无效的信号 |
EPERM | 该进程没有权限发送信号给任何一个目标进程 |
ESRCH | 目标进程或进程组不存在 |
-
pid:
指定要发送信号的进程ID。
注意事项:
pid > 0
:发送信号给指定的进程。
pid = 0
:发送信号给调用kill函数进程属于同一个进程组的所有进程。
pid < 0
:信号sig
将发送给进程组-pid
中的每一个进程。
pid = -1
:发送给有权限发送的系统中所有进程。
sig:
指定要发送的信号编号或信号名。
宏名 | 数值 | 作用 |
---|---|---|
SIGHUP | 1 | 表示终端连接断开或者控制进程终止 |
SIGINT | 2 | 表示程序由控制台(Ctrl+C)强制结束 |
SIGQUIT | 3 | 表示程序由控制台(Ctrl+\)强制结束并生成core文件 |
SIGILL | 4 | 表示程序由于非法操作(例如:数组越界,除数为0等)异常结束 |
SIGTRAP | 5 | 表示程序由于断点指令或跟踪陷阱异常结束 |
SIGABRT | 6 | 表示进程收到一个中止信号(中止进程的运行) |
SIGIOT | 6与SIGABRT相同 | 表示程序由于I/O操作异常结束 |
SIGBUS | 7 | 表示程序由于访问非法的内存地址异常结束 |
SIGFPE | 8 | 表示程序由于浮点异常异常结束 |
SIGKILL | 9 | 表示程序强制结束(如:调用系统函数kill发出的强制结束信号) |
SIGUSR1 | 10 | 用户自定义的信号类型 |
SIGSEGV | 11 | 表示程序由于段错误(例如:访问非法的内存地址)异常结束 |
SIGUSR2 | 12 | 用户自定义的信号类型 |
SIGPIPE | 13 | 表示程序尝试写入一个关闭的管道 |
SIGALRM | 14 | 表示定时器到期 |
SIGTERM | 15 | 表示进程收到一个终止信号(终止进程的运行) |
2.signal()和sigaction()函数:为一个信号设置处理函数
#include<signal.h>
//_sighandler_t类型其实是(void*)(int)函数指针
_sighandler_t signal(int sig,_sighandler_t __handler);
函数返回值:成功返回一个指向先前处理这个信号的函数的指针,或者是信号sig对应的默认处理函数指针SIG_DEF。失败时返回SIG_ERR,并设置errno。
sig:
指定要处理的信号类型。它可以取除了SIGKILL
和SIGSTOP
外的任何一种信号。__handler:
是一个函数指针,指向处理该信号的函数。这个函数的原型必须是一个返回值为void参数为int
的函数。如果__handler
的值为SIG_IGN
,则表示忽略该信号;如果__handler
的值为SIG_DFL
,则表示采用系统默认的信号处理方式。
sigaction函数是比signal函数使用更灵活更健壮的使用接口。
#include<signal.h>
int sigaction(int sig,const struct sigaction*act,struct sigaction*oldact);
函数返回值:成功,返回值为0,失败时返回-1,并设置全局变量errno。
sig:
指定要处理的信号类型。它可以取除了SIGKILL
和SIGSTOP
外的任何一种信号。act:
是一个指向sigaction
结构体的指针,用于设置信号的处理方式。如果act
的值为NULL
,则不会改变信号的处理方式。
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); };
- void (*sa_handler)(int):此参数和signal()的参数handler相同,代表新的信号处理函数。当接收到指定的信号时,系统会调用该函数来处理信号。该函数的参数是一个整数,表示接收到的信号的编号。如果sa_handler的值为SIG_IGN,则表示忽略接收到的信号;如果sa_handler的值为SIG_DFL,则表示采用系统默认的信号处理方式。
- void (*sa_sigaction)(int, siginfo_t *, void *):另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。当sa_flags成员的值包含了SA_SIGINFO标志时,系统将使用sa_sigaction函数作为信号处理函数,否则使用sa_handler作为信号处理函数。
- sigset_t sa_mask:信号阻塞集合,表示进程在执行信号处理时需要阻塞的信号,从信号处理函数返回时,这些被阻塞的信号将恢复为原先值。sa_mask成员指定了一个信号集合,在调用sa_handler所指向的信号处理函数之前,该信号集合所包含的信号会加入到进程的信号屏蔽集中,这些信号会在处理函数执行期间被进程所阻塞,从而防止处理函数还未运行结束时就被接收到的情况。
- int sa_flags:标志位集合,用于修改信号处理过程的行为。可以是以下几个值的组合:SA_NOCLDSTOP、SA_NOCLDWAIT、SA_SIGINFO、SA_NODEFER、SA_RESETHAND和SA_RESTART。
- void (*sa_restorer)(void):已经废弃的数据域,不应该被使用
oldact:
是一个指向sigaction
结构体的指针,用于保存先前处理这个信号的函数的指针。如果oldact
的值为NULL
,则不会返回先前处理这个信号的函数的指针。
3.信号集函数
Linux使用数据结构sigset_t来表示一组信号。
#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;
typedef __sigset_t sigset_t;
用来设置、修改、删除和查询信号集函数:
#include<signal.h>
int sigemptyset(sigset_t* set)//清除信号集
int sigfillset(sigset_t *set);//在信号集中设置所有信号
int sigaddset(sigset_t *set, int signum);//将信号signum添加至信号集中
int sigdelset(sigset_t *set, int signum);//将信号signum从信号集中删除
int sigismember(const sigset_t *set, int signum);//测试signum是否在信号集中