Linux - 网络套接字
一、网络编程
1)地址结构
1. IP地址结构
-
struct in_addr
:是用于表示 IPv4 地址 的结构体,定义在头文件<netinet/in.h>
中。它的主要作用是存储一个 32 位的 IPv4 地址,通常与struct sockaddr_in
一起使用。struct in_addr {uint32_t s_addr; // IP 地址,使用网络字节序存储 };
s_addr
- 类型:
uint32_t
(32 位无符号整数)。 - 用于存储 IPv4 地址,以 网络字节序(大端序)表示。
- 可以通过函数(如
inet_pton()
或inet_addr()
)将人类可读的字符串形式的 IP 地址(如"192.168.1.1"
)转换为s_addr
。
- 类型:
2. 套接字地址
-
struct sockaddr_in
:是一个用于描述 IPv4 套接字地址的结构体,常用于网络编程中与套接字函数(如bind()
、connect()
等)交互。它是struct sockaddr
的具体实现之一,专用于 IPv4 协议族。struct sockaddr_in {sa_family_t sin_family; // 地址族(Address Family),AF_INET 表示 IPv4in_port_t sin_port; // 16位的端口号,网络字节序struct in_addr sin_addr; // IP 地址(网络字节序)unsigned char sin_zero[8]; // 填充字段,保持与 struct sockaddr 的大小一致 };
-
sin_family
-
描述地址族(protocol family)。
-
对于 IPv4 地址,
sin_family
的值为AF_INET
。
-
-
sin_port
-
用于存储端口号(16 位)。
-
必须是 网络字节序,可以通过
htons()
函数将主机字节序转换为网络字节序。
-
-
sin_addr
-
表示 IPv4 地址。
-
类型为
struct in_addr
,实际上是一个 32 位整数。 -
常用的值:
INADDR_ANY
:绑定到本地所有可用接口。INADDR_LOOPBACK
:表示回环地址(127.0.0.1)。
-
-
sin_zero
-
保留字段,不做实际使用。
-
用于确保
struct sockaddr_in
的大小与struct sockaddr
相同。 -
通常用
memset()
将其置为 0。
-
-
2)地址转换
1.字符串转In_addr
-
inet_addr()
:是一个用于将 点分十进制的 IPv4 地址字符串 转换为 32 位网络字节序的整数(in_addr_t
类型)的函数。in_addr_t inet_addr(const char *cp)
in_addr_t addr = inet_addr("192.168.1.1");
-
cp
:指向一个点分十进制表示的 IPv4 地址字符串(如"192.168.1.1"
)。 -
返回值:
-
成功:返回转换后的 32 位网络字节序的 IPv4 地址(
in_addr_t
类型)。 -
失败:如果
cp
是无效的 IPv4 地址字符串,返回INADDR_NONE
(通常是0xFFFFFFFF
)。
-
-
-
inet_aton()
:将点分十进制格式的 IPv4 地址转换为网络字节顺序的二进制形式。int inet_aton(const char *cp, struct in_addr *inp)
struct in_addr addr; inet_aton("192.168.1.1", &addr)
-
cp
:指向以字符串形式表示的 IPv4 地址(如"192.168.1.1"
)。 -
inp
:指向struct in_addr
的指针,struct in_addr
是一个包含 IPv4 地址的结构体。 -
返回值:如果转换成功,返回 1;如果失败,返回 0。
-
-
inet_pton()
:处理 IPv4 和 IPv6 地址,将它们从点分十进制或标准文本形式转换为网络字节顺序的二进制表示。int inet_pton(int af, const char *src, void *dst)
struct in_addr addr; inet_pton(AF_INET, "192.168.1.1", &addr)
af
:地址族,通常是AF_INET
(IPv4)或AF_INET6
(IPv6)。src
:指向源 IP 地址的字符串(如"192.168.1.1"
或"::1"
)。dst
:指向目标内存区域的指针,用来存储转换后的二进制地址。对于 IPv4 是struct in_addr
,对于 IPv6 是struct in6_addr
。- 返回值:成功时返回 1,失败时返回 0,传入无效地址族时返回 -1。
函数 | 支持的协议 | 输入格式 | 返回类型 | 错误返回值 |
---|---|---|---|---|
inet_aton | IPv4 | 点分十进制 IP 地址 | int | 0(失败) |
inet_addr | IPv4 | 点分十进制 IP 地址 | in_addr_t | INADDR_NONE (失败) |
inet_pton | IPv4、IPv6 | 点分十进制或标准文本 | 二进制地址 | 0(失败),-1(错误) |
2. In_addr转字符串
-
inet_ntoa()
:将网络字节顺序的 IPv4 地址(即in_addr
类型)转换为点分十进制的字符串格式(例如:“192.168.1.1”)。char *inet_ntoa(struct in_addr in)
struct in_addr addr; addr.s_addr = htonl(0xC0A80101); // 192.168.1.1 char *ip = inet_ntoa(addr); printf("IP Address: %s\n", ip); // 输出 "192.168.1.1"
-
in
:类型为struct in_addr
,它包含了一个网络字节顺序的 IPv4 地址。 -
**返回值:**返回一个指向字符数组的指针,字符数组中是点分十进制格式的字符串表示。需要注意的是,这个返回值是一个静态缓冲区的指针,因此在多线程环境中使用时需要小心。
-
-
inet_ntop()
:一个更通用的函数,支持同时处理 IPv4 和 IPv6 地址,并将它们转换为点分十进制或标准文本表示的字符串格式。const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)
struct in_addr addr; addr.s_addr = htonl(0xC0A80101); // 192.168.1.1 char str[INET_ADDRSTRLEN]; if (inet_ntop(AF_INET, &addr, str, sizeof(str))) {printf("IP Address: %s\n", str); // 输出 "192.168.1.1" }
af
:地址族,指定地址的类型,通常为AF_INET
(IPv4)或AF_INET6
(IPv6)。src
:指向源 IP 地址的指针,它是以网络字节顺序存储的二进制地址。对于 IPv4 是struct in_addr
,对于 IPv6 是struct in6_addr
。dst
:指向缓冲区的指针,存储转换后的字符串。size
:dst
缓冲区的大小,确保它足够容纳转换后的字符串(IPv4 地址需要 16 字节,IPv6 地址需要 46 字节)。- 返回值:成功时,返回指向目标缓冲区的指针;如果失败,返回
NULL
。
2. 字节序转换
-
htons()
:是一个用于将主机字节序(Host Byte Order)的 16 位整数 转换为网络字节序(Network Byte Order)的函数。uint16_t htons(uint16_t hostshort)
-
hostshort
:要转换的 16 位主机字节序整数(一般是端口号)。 -
返回值:返回 转换为网络字节序的 16 位整数。
-
3)套接字接口
1. 创建套接字
-
int socket()
:是在网络编程中创建套接字(socket)的一种方式。具体来说,socket
是一个系统调用函数,用于创建一个新的套接字,用于在应用程序和操作系统的网络协议栈之间进行数据传输。int socket(int domain, int type, int protocol)
// 创建一个流式套接字(使用 IPv4 协议) int sockfd = socket(AF_INET, SOCK_STREAM, 0);
-
domain
(协议族):-
AF_INET
:IPv4 地址族。 -
AF_INET6
:IPv6 地址族。 -
AF_UNIX
:本地通信的 Unix 域套接字(用于进程间通信)。
-
-
type
(套接字类型):-
SOCK_STREAM
:流式套接字,通常用于 TCP 协议,提供面向连接的可靠通信。 -
SOCK_DGRAM
:数据报套接字,通常用于 UDP 协议,提供无连接的非可靠通信。
-
-
protocol
(协议):-
一般情况下为
0
,让操作系统自动选择与给定协议族和类型匹配的默认协议。对于SOCK_STREAM
,默认选择的是TCP
;对于SOCK_DGRAM
,默认选择的是UDP
。 -
如果指定具体的协议,则需要使用对应协议的编号,例如,
IPPROTO_TCP
或IPPROTO_UDP
。
-
-
返回值:
-
成功时,
socket()
返回一个整数值,表示新创建的套接字的文件描述符。 -
失败时,返回
-1
,并且可以通过errno
获取错误码。
-
-
2. 绑定套接字
-
bind()
:是一个系统调用函数,用于将套接字(socket)与本地地址(如 IP 地址和端口号)进行绑定。在服务器端,通常会使用bind()
来指定监听端口和 IP 地址,以便接收来自客户端的连接请求。int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); string ip = "192.168.0.1"; struct sockaddr_in local; bzero(&local, sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port_); local.sin_addr.s_addr = inet_addr(ip.c_str()) int n = bind(sockfd, (const struct sockaddr*)&local, sizeof(local));
-
sockfd
:传入的套接字文件描述符,通常由socket()
创建。 -
addr
:一个指向struct sockaddr
结构体的指针,表示要绑定的本地地址。具体的结构体类型(如struct sockaddr_in
或struct sockaddr_in6
)取决于协议族(如 IPv4 或 IPv6)。 -
addrlen
:addr
结构体的长度。通常使用sizeof(struct sockaddr_in)
来获取。 -
返回值:
-
成功时,返回
0
。 -
失败时,返回
-1
,并设置errno
,可以通过perror()
或strerror()
来查看错误信息。
-
-
3. 监听
-
listen()
:用于在服务器端创建一个侦听套接字(listening socket),它让套接字处于等待客户端连接的状态。通常在网络编程中,服务器会使用listen
来监听特定端口上的连接请求。int listen(int sockfd, int backlog)
// 进入监听状态,最多 3 个连接请求排队 int server_fd; listen(server_fd, 3)
-
sockfd
:要进行监听的套接字描述符,它通常由socket()
创建,类型是SOCK_STREAM
(TCP协议)或SOCK_DGRAM
(UDP协议)。在 TCP 的场景中,sockfd
是一个连接端点,用来监听客户端的连接请求。 -
backlog
:最大连接请求队列的长度,指定当服务器应用程序无法及时接受客户端连接时,操作系统内核允许的最大排队连接数。如果这个队列满了,后续的连接请求会被拒绝,或者客户端会收到一个连接失败的错误。通常该值设置为 5 到 128 之间。- 当该连接队列已满,服务端会将连接放到一个临时的半连接队列中,并将连接设置为SYN_RCVD状态,这个半连接队列不会长时间存在,当一定时间没有响应,会直接丢弃,如果被丢弃后客户端发送了数据,那么客户端和服务端会先重新进行三次握手再发送数据。
-
-
返回值:成功时返回 0。出错时返回 -1,并设置 errno 以指示错误。
4. 接受
-
accept()
:是服务器端套接字函数,用于接受一个客户端的连接请求。当客户端请求连接时,服务器通过调用accept()
来接受连接,并返回一个新的套接字,用于与客户端进行数据交换。accept()
是一个阻塞调用,意味着服务器会在调用此函数时等待,直到有客户端连接到来。int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
// 等待客户端连接请求 int server_fd, new_socket; struct sockaddr_in address; int addrlen = sizeof(address); address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; // 绑定所有接口 address.sin_port = htons(8080); // 监听端口 8080 new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
-
sockfd
:这是由socket()
创建并通过bind()
和listen()
函数设置为监听状态的套接字描述符。 -
addr
:一个指向struct sockaddr
结构体的指针,用于存储客户端的地址信息。通常会传入struct sockaddr_in
,它包含客户端的 IP 地址和端口号。 -
addrlen
:这是一个指向socklen_t
类型的指针,用于表示传入的addr
缓存的大小。在调用accept()
时,addrlen
会被更新为客户端地址结构的实际大小。 -
返回值:
-
成功时,返回一个新的套接字描述符(
new_socket
),用于与客户端进行通信。 -
失败时,返回
-1
,并设置errno
来表示错误原因。
-
-
5. 连接
-
connect()
:是客户端用于建立与服务器的连接的函数。在客户端调用connect()
后,它会尝试与指定的服务器(通过 IP 地址和端口号)建立连接。此函数通常是客户端程序的一部分,在发起通信之前,客户端需要先与服务器建立一个连接。int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
int sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr))
-
sockfd
:客户端通过socket()
创建的套接字描述符。 -
addr
:一个指向struct sockaddr
结构体的指针,包含目标服务器的 IP 地址和端口号。通常会使用struct sockaddr_in
来指定目标服务器的地址信息。 -
addrlen
:地址结构的长度(通常是sizeof(struct sockaddr_in)
)。 -
返回值:成功时返回 0。出错时返回 -1,并设置 errno 以指示错误。
-
函数 | TCP | UDP |
---|---|---|
socket() | 使用 | 使用 |
bind() | 服务器使用 | 客户端和服务器都可以使用 |
listen() | 服务器使用 | 不使用 |
accept() | 服务器使用 | 不使用 |
connect() | 客户端使用 | 客户端和服务器都可以使用 |
6. 设置套接字
-
setsockopt()
:用于设置套接字选项的函数,可以用于配置套接字的行为和特性。通过这个函数,你可以设置多种套接字选项,比如设置缓冲区大小、超时、协议相关的设置等。int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
// 创建 TCP 套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); int optval = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))
-
sockfd:套接字描述符,是通过
socket()
函数创建的。 -
level:协议层级,通常是
SOL_SOCKET
,表示对套接字层进行操作,也可以是其他协议特定的层级(例如IPPROTO_TCP
或IPPROTO_UDP
)。 -
optname:要设置的选项名称。
SO_REUSEADDR
:允许重用本地地址和端口。在关闭一个已绑定的套接字后,能允许快速重新绑定到相同的地址和端口。SO_RCVBUF
:设置接收缓冲区的大小。SO_RCVBUF
:设置发送缓冲区的大小。SO_KEEPALIVE
:启用或禁用连接保持活动。SO_BROADCAST
:允许发送广播。SO_LINGER
:设置linger
选项,用来指定连接在关闭时的延迟行为。
-
optval:指向存储选项值的缓冲区。具体内容和类型取决于选项。
-
optlen:
optval
缓冲区的大小。 -
返回值:
-
成功返回
0
。 -
失败返回
-1
,并且设置errno
来指示错误原因。
-
-
4)传输数据
1. 接受数据
-
recvfrom()
是一个用于接收数据的函数,通常在基于 UDP 的无连接套接字通信中使用。它可以接收来自特定发送者或任意发送者的数据,并且可以选择性地获取发送方的地址信息。ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen );
-
sockfd
- 接收数据的套接字文件描述符。
- 必须是一个已经绑定到本地地址(使用
bind
)的套接字。
-
buf
- 接收数据的缓冲区指针。
- 函数会将接收到的数据存入该缓冲区。
-
len
buf
缓冲区的大小(以字节为单位)。- 接收数据时,如果数据大于缓冲区,数据会被截断。
-
flags
- 控制行为的标志位,可以为以下值的组合:
0
:默认行为。MSG_PEEK
:仅查看数据而不从队列中移除。MSG_WAITALL
:等待接收到足够数据(填满缓冲区)。MSG_DONTWAIT
:非阻塞模式(无数据时立即返回)。
- 控制行为的标志位,可以为以下值的组合:
-
src_addr
- 指向
struct sockaddr
类型的结构体,用于存储发送方的地址信息(可为NULL
)。 - 如果不关心发送方地址,可设置为
NULL
。
- 指向
-
addrlen
- 指向
socklen_t
的指针,用于存储src_addr
的大小。 - 在调用前,需初始化为
src_addr
缓冲区的大小;返回后,表示实际存储的地址大小。 - 如果
src_addr
为NULL
,此参数也应为NULL
。
- 指向
-
返回值
- 成功:返回接收到的字节数。
- 失败:返回
-1
,并设置errno
以指示错误原因。
-
2. 发送数据
-
sendto()
:用于 UDP 套接字的发送数据的函数,它允许向指定的目标地址发送数据。对于 UDP,它是一个无连接的协议,因此你需要明确地指定目标地址和端口。ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); const char* message = "Hello, UDP!"; // 2. 设置目标地址结构 struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(12345); // 目标端口 dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 目标IP地址 ssize_t sent_len = sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
-
sockfd
:发送数据的套接字描述符,通常是通过socket()
创建的,适用于无连接的 UDP 套接字。 -
buf
:指向存储要发送数据的缓冲区的指针。 -
len
:缓冲区的长度,指定要发送的数据的字节数。 -
flags
:控制发送操作的标志,可以是以下值之一(可以组合使用):MSG_CONFIRM
:请求对方确认收到数据(适用于可靠传输协议)。MSG_DONTROUTE
:不走路由表,直接发送。MSG_NOSIGNAL
:如果发送数据时产生了信号,则不触发信号。
-
dest_addr
:指向sockaddr
结构体的指针,包含目标主机的地址信息,通常是sockaddr_in
(对于 IPv4)或sockaddr_in6
(对于 IPv6)。 -
addrlen
:目标地址的长度,通常是sizeof(struct sockaddr_in)
或sizeof(struct sockaddr_in6)
。 -
返回值:
-
成功时,返回发送的字节数。
-
失败时,返回 -1,并设置
errno
以指示错误。
-
-
二、守护进程化
1)进程相关标识符
1. PID(Process ID,进程ID)
- 每个正在运行的进程都会分配一个唯一的进程标识符(PID),用于区分不同的进程。
- PID 是操作系统内部用来管理和调度进程的关键标识符。当我们执行命令或启动程序时,操作系统会分配一个 PID 来标识该进程。
2. PPID(Parent Process ID,父进程ID)
- 每个进程都有一个父进程,也就是创建它的进程。PPID 就是父进程的 PID。
- PPID 表示当前进程的父进程,用于构建进程树。通过 PPID 可以追踪到某个进程是由哪个父进程创建的。
3. PGID(Process Group ID,进程组ID)
- 进程组是一个或多个进程的集合,它们共享一个进程组 ID。PGID 是进程组的标识符。
- 进程组在 Unix 系统中用于信号的发送。例如,可以向整个进程组发送一个信号,而不仅仅是单个进程。
4. SID(Session ID,会话ID)
- 会话是一组进程的集合,它通常由一个登录会话或某个程序的启动进程所创建。SID 是会话的标识符。
- 会话用于管理进程的生命周期,特别是在控制终端的上下文中。例如,登录系统后,所有的进程都属于同一个会话。
5. TGID(Thread Group ID,线程组ID)
- 线程组是指多个线程共享相同的进程 ID 和资源。TGID 是线程组的标识符。
- 在 Linux 系统中,线程和进程都被视为任务。线程组 ID 用于标识一组共享资源的线程。
6. UID(User ID,用户ID)
- UID 是操作系统用于标识用户的唯一标识符。
- 操作系统使用 UID 来管理文件的访问权限、资源分配等。每个用户都有一个唯一的 UID,通常在多用户系统中,UID 用于控制每个用户的操作权限。
进程组与任务的关系
- 任务包含进程:在 Linux 中,任务通常是指单个的进程或线程。每个进程都有一个唯一的
PID
,并且是操作系统调度的基本单位。- 进程组包含任务(进程):多个进程可以组成一个进程组。进程组由一个或多个任务(进程)组成,这些任务共享相同的 PGID。一个进程属于一个进程组,而进程组内的所有进程会共享一些属性和资源(如信号的发送)。
- 进程组 ID:每个进程组都有一个唯一的 PGID,它通常是该进程组内某个进程的
PID
。这个PID
一般是该组中的第一个进程的PID
。虽然进程组中的每个进程都有自己的 PID,但它们共享一个进程组 ID。
2)Session
在 Linux 中,前台进程和后台进程的区分主要是通过进程的输入/输出交互方式来确定的,具体来说是基于终端控制和进程的执行方式。下面我会详细讲解如何区分前台进程和后台进程,以及相关的操作方法。
1. 前台进程(Foreground Process)
前台进程是指直接与用户交互的进程,通常在用户的终端窗口中执行,并且会占用该终端的输入输出。
- 特征:
- 前台进程在启动后,会占用当前的终端,并与用户进行交互。
- 用户可以直接向前台进程发送输入并获取其输出。
- 一旦前台进程结束,终端控制权会归还给用户。
- 如何识别:
- 在命令行中,运行一个进程时,默认情况下,它是前台进程。例如,运行
vim
编辑器,或者执行一个python
脚本,这些都会是前台进程。
- 在命令行中,运行一个进程时,默认情况下,它是前台进程。例如,运行
- 管理前台进程:
- 如果你在执行一个命令时,你的终端会被该进程“占用”,你只能看到该进程的输出,而无法执行其他命令。
- 可以使用 Ctrl+C 来终止当前前台进程。
2. 后台进程(Background Process)
后台进程是指在系统后台运行的进程,它不会占用当前终端的输入输出流。用户可以在终端继续执行其他命令,而后台进程在不干扰用户操作的情况下继续执行。
-
特征:
- 后台进程不会占用终端的输入输出,用户可以在终端继续执行其他命令。
- 通常使用一个符号
&
将命令放到后台运行,或者使用一些专门的命令(如nohup
、disown
)将进程移到后台。 - 后台进程的输出通常会显示在终端中,除非使用输出重定向将其发送到文件。
-
如何识别:
-
在命令行中,使用符号
&
将一个命令放到后台执行,例如:long_running_command &
-
该命令将在后台运行,并且会立即返回到命令行,用户可以继续输入其他命令。
-
-
管理后台进程:
-
使用
jobs
命令查看当前的后台进程:
jobs
输出类似:
[1]+ 1234 running long_running_command &
-
使用
fg
将后台进程带到前台:fg %1
这将把进程 id
1234
,任务号1
的任务带到前台运行。 -
使用
bg
将暂停的进程继续在后台运行:bg %1
-
使用
kill
命令终止后台进程:kill %1
-
3. 如何从前台切换到后台
如果你已经在前台运行了一个进程,但希望将其切换到后台,可以使用以下方法:
-
暂停前台进程并将其移至后台:
-
在执行的前台进程中,按下 Ctrl+Z 将进程暂停。
-
然后使用
bg
命令将其发送到后台继续运行:bg
-
-
将后台进程移动到后台执行:
-
使用
disown
命令来彻底把后台进程从当前 shell 会话中“脱离”出来,防止它在 shell 会话关闭时被杀死:disown %1
-
4. 使用 nohup
命令启动后台进程
启动一个即使在关闭终端或退出会话后仍然继续运行的后台进程,可以使用 nohup
命令。
-
nohup
:该命令使进程在你退出终端后仍然运行,并且它会将标准输出和标准错误重定向到文件nohup.out
。nohup long_running_command &
这样,无论你是否退出终端,
long_running_command
都会继续运行。
3)守护进程
守护进程(Daemon) 是在后台运行的进程,它通常不与任何终端交互,并且在系统启动时或系统运行过程中自动启动。守护进程通常用于处理系统级任务,如日志记录、网络服务、定时任务等。常见的守护进程包括 web 服务器(如 Apache)、数据库服务(如 MySQL)等。
守护进程化(Daemonization)指的是将一个普通的进程转变为守护进程的过程。这个过程通常包括一系列的步骤,以确保进程能够在后台独立运行,并在必要时能够安全退出。守护进程话步骤如下:
-
忽略控制终端的信号:
signal(SIGCLD, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGSTOP, SIG_IGN);
-
守护进程通常不希望接收到来自控制终端(例如键盘输入或终端关闭)的信号。可以使用
signal()
或sigaction()
来忽略这些信号。 -
特别是,守护进程可能需要忽略
SIGHUP
信号,这个信号通常在终端关闭时发送。
-
-
创建一个新的会话:
if(fork() > 0){exit(0); } setsid();
-
使用
fork()
创建一个子进程,确保父进程终止。这样,子进程就不再是终端的控制进程(父进程)。 -
调用
setsid()
将子进程创建为一个新的会话,并成为该会话的首进程。这样可以避免进程被控制终端和控制进程干扰。 -
通过调用
setsid()
,子进程会成为新的会话领导进程,脱离控制终端,彻底实现后台运行。
-
-
改变工作目录:
if(!cwd.empty()){chdir(cwd.c_str()); }
-
守护进程应该将当前工作目录更改为根目录(
/
),以避免阻止文件系统的卸载。执行此操作可以确保守护进程不会锁定文件系统。 -
使用
chdir("/")
进行目录切换。
-
-
重定向文件描述符:
-
守护进程应该关闭标准输入(
stdin
)、标准输出(stdout
)和标准错误输出(stderr
)文件描述符。这是因为守护进程没有终端,通常不会与用户进行交互。 -
可以使用
close()
关闭文件描述符fclose(stdin); fclose(stdout); fclose(stderr);
-
或者将其重定向到
/dev/null
。freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr);
-
-
创建子进程:
- 使用
fork()
创建第二个子进程,确保守护进程不会偶然地重新获得控制终端。第一步的fork()
创建的子进程还可能会被某些父进程或终端所关联,所以第二次fork()
是确保完全与控制终端断开联系的措施。
- 使用
-
清理环境:
-
守护进程通常不需要与用户的会话状态或终端会话有关的环境变量。可以在守护进程化时清除这些变量,例如使用
unsetenv()
删除环境变量。 -
通过调用
umask(0)
来设置文件创建权限掩码,这样守护进程创建的文件权限将不会受到父进程设置的限制。
-
deamon()
:函数通常位于unistd.h
头文件中,使用它可以轻松地将一个进程转化为守护进程。
int daemon(int nochdir, int noclose)
// 将当前进程转化为守护进程 daemon(0, 0)
nochdir
:如果为 0,守护进程会将工作目录更改为根目录(/
),以避免占用系统的任何文件系统。常见的做法是设置为 1 来保持当前工作目录不变,防止守护进程锁定特定目录。
noclose
:如果为 0,守护进程会关闭标准输入、输出和错误输出,并将它们重定向到/dev/null
,通常会避免任何与终端的交互。设置为 1 会让守护进程保持标准文件描述符不关闭。返回值
- 成功时,
daemon()
返回 0。- 失败时,返回 -1,并设置
errno
以指示错误原因。
相关文章:
Linux - 网络套接字
一、网络编程 1)地址结构 1. IP地址结构 struct in_addr:是用于表示 IPv4 地址 的结构体,定义在头文件 <netinet/in.h> 中。它的主要作用是存储一个 32 位的 IPv4 地址,通常与 struct sockaddr_in 一起使用。 struct in_a…...
Oracle数据库监听学习
官方文档: Net Services Administrators Guide Net Services Reference 一、动态注册 1.实例启动后,LREG 进程每分钟自动将服务名(service_name)注册到监听器中 也可以通过 alter system register 命令实现立刻注册。&#x…...
利率债、信用债、可转债区别与优势
利率债、信用债、城投债和可转债是债券市场的主要品种,它们在发行主体、风险收益特征和投资优势上各有不同。以下是它们的区别和优势: 1. 利率债 定义:利率债是由政府或政府支持的机构发行的债券,主要包括国债、政策性金融债&…...
C语言番外篇(4)------------------>VS环境下源码的隐藏
假设你是一个优秀的程序员,开发了一款功能十分强大的计算器。现在有一家做计算器的公司看上了你的功能,想通过每一年给你几万块钱使用这个功能。那我们是只提供一个头文件和静态库给他们使用这个功能就行呢?还是连同源代码一起给这家公司呢&a…...
Java集合
写在前面 本人在学习JUC过程中学习到集合和并发时有许多稀碎知识点 需要总结梳理思路与知识点 本文内容会涉及到ArrayList,HashMap以及扩容机制,ConcurrentHashMap,Synchronized,Volatile,ReentrantLock,…...
el-input 设置类型为number时,输入中文后光标会上移,并且会出现上下箭头
光标上移 设置 el-input 的 typenumber后,只能输入数字,输入中文后会自动清空,但是会出现一个问题:【光标会上移,如下图】 解决方法:修改样式 注意:需要使用样式穿透 :deep( ) /*解决el-in…...
迷你世界脚本自定义UI接口:Customui
自定义UI接口:Customui 彼得兔 更新时间: 2024-11-07 15:12:42 具体函数名及描述如下:(除前两个,其余的目前只能在UI编辑器内部的脚本使用) 序号 函数名 函数描述 1 openUIView(...) 打开一个UI界面(注意…...
解决windows npm无法下载electron包的问题
1.将nsis.zip解压到C:\Users\XXX\AppData\Local\electron-builder\Cache 2.将winCodeSign.zip解压到C:\Users\XXX\AppData\Local\electron-builder\Cache 3.将electron-v20.3.8-win32-ia32.zip复制到C:\Users\XXX\AppData\Local\electron\Cache 4.将electron-v20.3.8-win32-…...
Notepad++ 8.6.7 安装与配置全攻略(Windows平台)
一、软件定位与核心优势 Notepad 是开源免费的代码/文本编辑器,支持超过80种编程语言的高亮显示,相比系统自带记事本具有以下优势: 轻量高效:启动速度比同类软件快30%插件扩展:支持NppExec、JSON Viewer等200插件跨文…...
Unity InputField + ScrollRect实现微信聊天输入框功能
1、实现动态高度尺寸的的InputField 通过这两个部件就可以实现inputField的动态改变尺寸。 将inputField放入到scrollview当中作为子类 将scrollview 链接到UIChatInputField脚本中。 2、实现UIChatInputField //聊天输入框(类似wechat) [RequireComp…...
Java-servlet(三)Java-servlet-Web环境搭建(下)详细讲解利用maven和tomcat搭建Java-servlet环境
Java-servlet(三)Java-servlet-Web环境搭建(下)利用maven和tomcat搭建Java-servlet环境 前言一、配置maven阿里镜像二、利用IDEA创建maven文件创建maven文件删除src文件创建新的src模版删除example以及org文件 三、在第二个xml文件…...
在 CLion 中使用 Boost.Test 进行 C++ 单元测试
1. 安装 Boost.Test Boost.Test 是 Boost C 库的一部分,因此需要安装完整的 Boost 库。 方法 1:使用包管理器安装(推荐) Windows(vcpkg) 直接使用 CLion 集成的 vcpkg安装 boost-test: 也可…...
极狐GitLab 17.9 正式发布,40+ DevSecOps 重点功能解读【二】
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...
文本处理Bert面试内容整理-BERT的预训练任务是什么?
BERT的预训练任务主要有两个,分别是 Masked Language Model (MLM) 和 Next Sentence Prediction (NSP)。这两个任务帮助BERT学习从大规模未标注文本中提取深层次的语义和上下文信息。 1. Masked Language Model (MLM)(掩码语言模型)...
【蓝桥杯】每天一题,理解逻辑(3/90)【Leetcode 快乐数】
闲话系列:每日一题,秃头有我,Hello!!!!!,我是IF‘Maxue,欢迎大佬们来参观我写的蓝桥杯系列,我好久没有更新博客了,因为up猪我寒假用自己的劳动换了…...
“深入浅出”系列之Linux篇:(10)基于C++实现分布式网络通信RPC框架
分布式网络通信rpc框架 项目是分布式网络通信rpc框架, 文中提到单机服务器的缺点: 硬件资源的限制影响并发:受限于硬件资源,聊天服务器承受的用户的并发有限 模块的编译部署难:任何模块小的修改,都导致整…...
Python的那些事第四十一篇:简化数据库交互的利器Django ORM
Django ORM:简化数据库交互的利器 摘要 随着互联网技术的飞速发展,Web开发越来越受到重视。Django作为一款流行的Python Web框架,以其高效、安全、可扩展等特点受到了广大开发者的喜爱。其中,Django ORM(对象关系映射)是Django框架的核心组件之一,它为开发者提供了一种…...
[自动驾驶-传感器融合] 多激光雷达的外参标定
文章目录 引言外参标定原理ICP匹配示例参考文献 引言 多激光雷达系统通常用于自动驾驶或机器人,每个雷达的位置和姿态不同,需要将它们的数据统一到同一个坐标系下。多激光雷达外参标定的核心目标是通过计算不同雷达坐标系之间的刚性变换关系(…...
初学STM32之简单认识IO口配置(学习笔记)
在使用51单片机的时候基本上不需要额外的配置IO,不过在使用特定的IO的时候需要额外的设计外围电路,比如PO口它是没有内置上拉电阻的。因此若想P0输出高电平,它就需要外接上拉电平。(当然这不是说它输入不需要上拉电阻,…...
【长安大学】苹果手机/平板自动连接认证CHD-WIFI脚本(快捷指令)
背景: 已经用这个脚本的记得设置Wifi时候,关闭“自动登录” 前几天实在忍受不了CHD-WIFI动不动就断开,一天要重新连接,点登陆好几次。试了下在网上搜有没有CHD-WIFI的自动连接WIFI自动认证脚本,那样我就可以解放双手&…...
powermock,mock使用笔记
介于日本的形式主义junit4单体测试,特记笔记,以下纯用手机打出来,因为电脑禁止复制粘贴。 pom文件 powermock-module-junit1.7.4 powermock-api-mokcito 1.7.4 spring-test 8 1,测试类头部打注解 RunWith(PowerMockRunner.class…...
大模型微调实战指南
1. 引言 在人工智能领域,大模型(如GPT、BERT、DeepSeek等)已经展现出了强大的通用能力。然而,要让这些模型在特定任务或领域中发挥最佳性能,微调(Fine-tuning)是必不可少的一步。本文将带你从零…...
计算机毕业设计Python+Django+Vue3微博数据舆情分析平台 微博用户画像系统 微博舆情可视化(源码+ 文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
HTML第四节
一.复合选择器 1.后代选择器 注:1.后代选择器会选中后代所有的要选择的标签 2.儿子选择器 3.并集选择器 注:1.注意换行,同时选中多种标签 4.交集选择器 注:1.标签选择器放在最前面,例如放在类选择器的前面 2.两个选择…...
Kubernetes 的正式安装
1.基础的网络结构说明 软件路由器 ikuai 当然同一个仅主机模式 相当于在 同一个我们所谓的广播域内 所以相当于它们的几张网卡 是被连接起来的 为了防止出现问题 我们可以把第二块网卡临时关闭一下 2.准备路由器 ikuai 爱快 iKuai-商业场景网络解决方案提供商 (ikuai8.com)…...
VS2022C#windows窗体应用程序调用DeepSeek API
目录 一、创建DeepSeek API Key 二、创建窗体应用程序 三、设计窗体 1、控件拖放布局 2、主窗体【Form1】设计 3、多行文本框【tbContent】 4、提交按钮【btnSubmit】 5、单行文字框 四、撰写程序 五、完整代码 六、运行效果 七、其它 一、创建DeepSeek API Ke…...
7. 机器人记录数据集(具身智能机器人套件)
1. 树莓派启动机器人 conda activate lerobotpython lerobot/scripts/control_robot.py \--robot.typelekiwi \--control.typeremote_robot2. huggingface平台配置 huggingface官网 注册登录申请token(要有写权限)安装客户端 # 安装 pip install -U …...
阿里云操作系统控制台——ECS操作与性能优化
引言:在数字化时代,云服务器作为强大的计算资源承载平台,为企业和开发者提供了灵活且高效的服务。本文将详细介绍如何一步步操作云服务器 ECS,从开通到组件安装,再到内存全景诊断,帮助快速上手,…...
在飞腾E2000Q开发板上,基于RT-Thread操作系统,实现DeepSeek语音交互
目录 一 ,简介 二 ,流程与结果分享 1. Phytium E2000q demo开发板连接 2. RT-Thread Kconfig 配置选择 (1)驱动 (2)软件包 3. 主要代码 (1)录音功能,将录音结果保存…...
navicat导出postgresql的数据库结构、字段名、备注等等
1、执行sql语句 SELECT A.attnum AS "序号",C.relname AS "表名",CAST ( obj_description ( relfilenode, pg_class ) AS VARCHAR ) AS "表名描述",A.attname AS "字段名称",A.attnotnull as "是否不为null",(case when A…...
K8s 1.27.1 实战系列(三)安装网络插件
Kubernetes 的网络插件常见的有 Flannel 和 Calico ,这是两种主流的 CNI(容器网络接口)解决方案,它们在设计理念、实现方式、性能特征及适用场景上有显著差异。以下是两者的综合对比分析: 一、Flannel 和 Calico 1. 技术基础与网络实现 Flannel 核心机制:基于 Overlay …...
Python实现鼠标点击获取窗口进程信息
最近遇到挺无解的一个问题:电脑上莫名其妙出现一个白色小方块,点击没有反应,关也关不掉,想知道它和哪个软件有关还是显卡出了问题,也找不到思路,就想着要不获取一下它的进程号看看。 于是写了一个Python脚本…...
文件解析:doc、docx、pdf
1.doc解析 ubuntu/debian系统应先安装工具 apt-get install python-dev libxml2-dev libxslt1-dev antiword unrtf poppler-utils pstotext tesseract-ocr \ flac ffmpeg lame libmad0 libsox-fmt-mp3 sox libjpeg-dev swig pip install textract解析: import te…...
JDBC 完全指南:掌握 Java 数据库交互的核心技术
JDBC 完全指南:掌握 Java 数据库交互的核心技术 一、JDBC 是什么?为什么它如此重要? JDBC(Java Database Connectivity)是 Java 语言中用于连接和操作关系型数据库的标准 API。它允许开发者通过统一的接口访问不同的数…...
【STM32】STM32系列产品以及新手入门的STM32F103
📢 STM32F103xC/D/E 系列是一款高性能、低功耗的 32 位 MCU,适用于工业、汽车、消费电子等领域;基于 ARM Cortex-M3,主频最高 72MHz,支持 512KB Flash、64KB SRAM,适合复杂嵌入式应用,提供丰富的…...
esp32驱动带字库芯片TFT屏幕
前言 学习esp32单片机开发,前段时间在网上买了一块2.0寸TFT屏幕。 长这个样子,这个屏幕带汉字字库的硬件模块。我仔细看了一下这个字库模块上面写的字是25Q32FVSIG 1336 文档 卖家也发来了开发文档,是个doc文档,张这个样子。 开…...
[Python入门学习记录(小甲鱼)]第5章 列表 元组 字符串
第5章 列表 元组 字符串 5.1 列表 一个类似数组的东西 5.1.1 创建列表 一个中括号[ ] 把数据包起来就是创建了 number [1,2,3,4,5] print(type(number)) #返回 list 类型 for each in number:print(each) #输出 1 2 3 4 5#列表里不要求都是一个数据类型 mix [213,"…...
网络安全等级保护2.0 vs GDPR vs NIST 2.0:全方位对比解析
在网络安全日益重要的今天,各国纷纷出台相关政策法规,以加强信息安全保护。本文将对比我国网络安全等级保护2.0、欧盟的GDPR以及美国的NIST 2.0,分析它们各自的特点及差异。 网络安全等级保护2.0 网络安全等级保护2.0是我国信息安全领域的一…...
由麻省理工学院计算机科学与人工智能实验室等机构创建低成本、高效率的物理驱动数据生成框架,助力接触丰富的机器人操作任务
2025-02-28,由麻省理工学院计算机科学与人工智能实验室(CSAIL)和机器人与人工智能研究所的研究团队创建了一种低成本的数据生成框架,通过结合物理模拟、人类演示和基于模型的规划,高效生成大规模、高质量的接触丰富型机…...
leetcode15 三数之和
1.哈希法 为了避免重复 class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {set<vector<int>> temple;//使用 set 来存储符合条件的三元组,避免重复vector<vector<int>> out;//存放最终输…...
5c/c++内存管理
1. C/C内存分布 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const char* pChar3 "abcd";int* ptr1 (int*)malloc(sizeof(int) * 4);i…...
蓝桥备赛(11)- 数据结构、算法与STL
一、数据结构 1.1 什么是数据结构? 在计算机科学中,数据结构是一种 数据组织、管理和存储的格式。它是相互之间存在一种 或多种特定关系的数据元素的集合。 ---> 通俗点,数据结构就是数据的组织形式 , 研究数据是用什么方…...
C++ 二叉搜索树代码
C 二叉搜索树代码 #include <iostream> using namespace std;template<typename T> struct TreeNode{T val;TreeNode *left;TreeNode *right;TreeNode():val(0), left(NULL), right(NULL){}TreeNode(T x):val(x), left(NULL), right(NULL){} };template<typena…...
Flask 打包为exe 文件
进入虚拟环境 激活虚拟环境 .venv\Scripts\activatepython build.py 完成标识图片 已经完成打包了,完成,下边是我自己记录的 这时候,我自己数据库文件夹下是没有sql 脚本的,要自己拷贝下这个路径下的文件 E:\开源文件\python-wi…...
JavaWeb-idea配置smart tomcat
一,安装smart tomcat插件 在插件市场搜索smart tomcat 点击安装,我已经安装成功。 二,web项目配置tomcat 点击这里,选择edit 进来之后,选加号 然后选tomcat 在这里,配置完毕后,点apply&…...
DELETE/ UPDATE/ INSERT 语句会自动加锁
在数据库系统中,DELETE、UPDATE 和 INSERT 语句通常会自动加锁,以确保数据的一致性和并发控制。具体的锁类型和效果取决于数据库的实现(如 MySQL、PostgreSQL 等)以及事务的隔离级别。以下是这些操作通常加锁的行为和效果…...
docker本地部署ollama
启动ollama容器 1.使用该命令启动CPU版运行本地AI模型 docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama 2.此命令用于启动GPU版本运行AI模型 前提是笔记本已配置NVIDIA的GPU驱动,可在shell中输入nvidia-smi查看详细情况…...
Linux线程机制
Linux 操作系统中的线程机制是基于 POSIX 线程(Pthreads) 标准实现的,通常称为 pthread。Linux 内核通过Native POSIX Thread Library提供了对多线程的支持。 1. 线程的基本概念 线程是进程中的一个执行单元,是 CPU 调度的基本单…...
LeetCode热题100JS(44/100)第八天|二叉树的直径|二叉树的层序遍历|将有序数组转换为二叉搜索树|验证二叉树搜索树|二叉搜索树中第K小的元素
543. 二叉树的直径 题目链接:543. 二叉树的直径 难度:简单 刷题状态:1刷 新知识: 解题过程 思考 示例 1: 输入:root [1,2,3,4,5] 输出:3 解释:3 ,取路径 [4,2,1,3] 或…...
Java与数据库
目录 一.本文焦点: 二.数据库常用数据类型 三.对数据库操作 四.对数据库中的表操作 五.条件表达 六. 表查询操作进阶 1.多表连接查询 1)交叉连接查询 2)内连接(取两表交集) 3)外连接 4)…...