基于抽象命名空间的本地套接字,bind在内核对应的函数是unix_bind_abstract,会根据用户态传的 struct sockaddr_un的内容插入hash表
如果父进程绑定了基于抽象空间的本地套接字,然后fork子进程,后续父进程退出,父进程退出时不会调用unix_release从hash表删除原来的抽象命名空间。在原来的子进程未退出的情况下,重新绑定抽象命名空间将会失败。
//// 父进程未退出
anlan@anlan:/proc/3439/fd$ netstat -anp | grep 1905
(并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户)
unix 2 [ ACC ] 流 LISTENING 68691 3659/main @./1905_daemon@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//// 父进程退出
anlan@anlan:/proc/3439/fd$ netstat -anp | grep 1905
(并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户)
unix 2 [ ACC ] 流 LISTENING 68691 3669/./child @./1905_daemon@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
查看child进程的文件描述符信息
anlan@anlan:/proc/3669/fd$ ls -l
总用量 0
lr-x------ 1 anlan anlan 64 5月 23 15:51 0 -> /dev/null
lrwx------ 1 anlan anlan 64 5月 23 15:51 1 -> /dev/pts/2
lr-x------ 1 anlan anlan 64 5月 23 15:51 103 -> /usr/share/code/v8_context_snapshot.bin
lrwx------ 1 anlan anlan 64 5月 23 15:51 2 -> /dev/pts/2
lrwx------ 1 anlan anlan 64 5月 23 15:51 3 -> 'socket:[68691]'
l-wx------ 1 anlan anlan 64 5月 23 15:51 37 -> /home/anlan/.config/Code/logs/20250523T151626/ptyhost.log
lrwx------ 1 anlan anlan 64 5月 23 15:51 38 -> /dev/ptmx
lr-x------ 1 anlan anlan 64 5月 23 15:51 39 -> /usr/share/code/resources/app/node_modules.asar
//// 再次启动main进程
anlan@anlan:~/桌面/unix_bind$ ./main
bind: Address already in use
除非kill掉child进程否则main进程绑定socket将不会成功
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#define SOCKET_PATH "./daemon"
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_un server_addr, client_addr;
socklen_t client_len;
char buffer[BUFFER_SIZE];
int bytes_read;
// Remove the socket file if it already exists
// Create the socket
server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// Configure server address
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
snprintf(server_addr.sun_path + 1, sizeof(server_addr.sun_path) - 1, "%s", SOCKET_PATH);
// Bind the socket to the address
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
close(server_fd);
exit(EXIT_FAILURE);
}
// Listen for incoming connections
if (listen(server_fd, 5) == -1) {
perror("listen");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Server is listening on %s...\n", SOCKET_PATH);
system("./child &");
while (1) {
// Accept a client connection
client_len = sizeof(client_addr);
client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd == -1) {
perror("accept");
continue;
}
printf("Client connected\n");
// Handle client communication
while ((bytes_read = read(client_fd, buffer, sizeof(buffer))) > 0) {
printf("Received: %.*s", bytes_read, buffer);
// Echo back to client
if (write(client_fd, buffer, bytes_read) != bytes_read) {
perror("write");
break;
}
}
if (bytes_read == -1) {
perror("read");
}
printf("Client disconnected\n");
close(client_fd);
}
// Clean up (this part won't be reached in this example)
close(server_fd);
return 0;
}
// child.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
while (1)
{
sleep(1);
}
return 0;
}