atomic write pipe

 

阅读 skynet 代码 socket_server 部分,发现对 socket 的写操作流程是这样的:

1. 各个服务(各线程)将数据写到 sendctrl_fd,这是一个 pipe 的 写端

2. ctrl_cmd 函数从 recvctrl_fd 读出数据,然后写到真正的 socketfd

 

在第一步中,多个线程向 sendctrl_fd 写数据,但是代码中并没有给 sendctrl_fd 加锁。

 1 static void
 2 send_request(struct socket_server *ss, struct request_package *request, char type, int len) {
 3     request->header[6] = (uint8_t)type;
 4     request->header[7] = (uint8_t)len;
 5     for (;;) {
 6         int n = write(ss->sendctrl_fd, &request->header[6], len+2);
 7         if (n<0) {
 8             if (errno != EINTR) {
 9                 fprintf(stderr, "socket-server : send ctrl command error %s.\n", strerror(errno));
10             }
11             continue;
12         }
13         assert(n == len+2);
14         return;
15     }
16 }

经过各种搜索得出结论:

写 pipe 的数据大小如果小于等于 PIPE_BUF(4k),那么这个写操作是 atomic 的。如果超过了这个 PIPE_BUF,那就不能保证咯。。

 

http://stackoverflow.com/questions/4624071/pipe-buffer-size-is-4k-or-64k

https://docs.oracle.com/cd/E19683-01/806-6546/pipe6-7/index.html

http://man7.org/linux/man-pages/man7/pipe.7.html

http://stackoverflow.com/questions/9701757/when-to-use-pipes-vs-when-to-use-shared-memory

http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html#pipes

 

转载于:https://www.cnblogs.com/hangj/p/6388722.html

/************************************************************************************************** \Copyright (c) Novatek Microelectronics Corp., Ltd. All Rights Reserved. \file server.c \brief server of full duplex chat tools \project Training \chip NA \Date 2025.08.15 **************************************************************************************************/ #include "common.h" int g_sockfd = -1; atomic_bool g_bRunning = ATOMIC_VAR_INIT(true); atomic_bool g_bClientShutdown = ATOMIC_VAR_INIT(false); pthread_t g_readThread, g_writeThread; char g_sClientName[MAX_NAME_LEN] = {'\0'}; /*! \fn void cleanup_client() \brief Clean up client resources */ void cleanup_client() { TEST_PRINT(EN_TRACE, "in"); TEST_PRINT(EN_MSG, "Cleaning up client resources"); atomic_store_explicit(&g_bRunning, false, memory_order_release); atomic_store_explicit(&g_bClientShutdown, true, memory_order_release); shutdown(g_sockfd, SHUT_RDWR); pthread_cancel(g_readThread); pthread_cancel(g_writeThread); if (0 <= g_sockfd) { close(g_sockfd); g_sockfd = -1; } TEST_PRINT(EN_TRACE, "out"); return; } /*! \fn void handle_signal(int sig) \brief signal processing function \param sig: Signal value */ void handle_signal(int sig) { TEST_PRINT(EN_TRACE, "in"); TEST_PRINT(EN_MSG, "Received signal %d, exiting...", sig); cleanup_client(); TEST_PRINT(EN_TRACE, "out"); } /*! \fn void* read_thread(void* arg) \brief Receive server messages \param arg: unused \return NULL */ void* read_thread(void* arg) { TEST_PRINT(EN_TRACE, "in"); ChatMessage chatMsg = {0}; int recvLen = 0; (void)arg; while (atomic_load_explicit(&g_bRunning, memory_order_acquire) && !atomic_load_explicit(&g_bClientShutdown, memory_order_acquire)) { pthread_testcancel(); recvLen = recv(g_sockfd, &chatMsg, sizeof(ChatMessage), 0); if (0 < recvLen) { TEST_PRINT(EN_MSG, "\n[%s]:\n%s", chatMsg.sSender,chatMsg.sContent); fflush(stdout); } else if (0 == recvLen) { TEST_PRINT(EN_MSG, "Server disconnected"); atomic_store_explicit(&g_bRunning, false, memory_order_release); } else { if (errno != EAGAIN && errno != EWOULDBLOCK) { TEST_PRINT(EN_ERROR, "Recv error: %s", strerror(errno)); atomic_store_explicit(&g_bRunning, false, memory_order_release); } } } TEST_PRINT(EN_TRACE, "out"); return NULL; } /*! \fn void* write_thread(void* arg) \brief Read user input \param arg: unused \return NULL */ void* write_thread(void* arg) { TEST_PRINT(EN_TRACE, "in"); int iChoice = 0; ChatMessage chatMsg = {0}; (void)arg; while (atomic_load_explicit(&g_bRunning, memory_order_acquire) && !atomic_load_explicit(&g_bClientShutdown, memory_order_acquire)) { pthread_testcancel(); TEST_PRINT(EN_MSG, "\n==== Chat menu ====\n"); TEST_PRINT(EN_MSG, "\n1. Private chat\n2. Group chat\n3. Online users\n4. Exit"); fflush(stdout); scanf("%d", &iChoice); getchar(); switch (iChoice) { case 1: chatMsg.type = MSG_TEXT; strncpy(chatMsg.sSender, g_sClientName, MAX_NAME_LEN); chatMsg.sSender[MAX_NAME_LEN - 1] = '\0'; TEST_PRINT(EN_MSG, "Please enter the recipient's username: "); fgets(chatMsg.sReceiver, MAX_NAME_LEN, stdin); chatMsg.sReceiver[strcspn(chatMsg.sReceiver, "\n")] = '\0'; TEST_PRINT(EN_MSG, "Input message: "); fgets(chatMsg.sContent, MAX_MSG_LEN, stdin); chatMsg.sContent[strcspn(chatMsg.sContent, "\n")] = '\0'; chatMsg.type = MSG_TEXT; break; case 2: chatMsg.type = MSG_GROUP; strncpy(chatMsg.sSender, g_sClientName, MAX_NAME_LEN); chatMsg.sSender[MAX_NAME_LEN - 1] = '\0'; TEST_PRINT(EN_MSG, "Enter group messaging: "); fgets(chatMsg.sContent, MAX_MSG_LEN, stdin); chatMsg.sContent[strcspn(chatMsg.sContent, "\n")] = '\0'; strncpy(chatMsg.sReceiver, "ALL", MAX_NAME_LEN); chatMsg.sReceiver[MAX_NAME_LEN - 1] = '\0'; break; case 3: chatMsg.type = MSG_LIST; break; case 4: atomic_store_explicit(&g_bRunning, false, memory_order_release); goto end; default: TEST_PRINT(EN_ERROR, "invalid Choice"); } TEST_PRINT(EN_DEBUG, "send msg"); if (0 > send(g_sockfd, &chatMsg, sizeof(ChatMessage), 0)) { TEST_PRINT(EN_ERROR, "Send failed: %s", strerror(errno)); goto end; } } end: TEST_PRINT(EN_TRACE, "out"); return NULL; } /*! \fn bool client_login() \brief client login function \return login success : true; failed : false */ bool client_login() { int iChoice = 0; ChatMessage chatMsg = {0}; bool bRes = false; while (!atomic_load_explicit(&g_bClientShutdown, memory_order_acquire)) { start: TEST_PRINT(EN_MSG, "\n1. Login in\n2. Register\n3. Exit"); fflush(stdout); scanf("%d", &iChoice); getchar(); switch (iChoice) { case 1: chatMsg.type = MSG_LOGIN; TEST_PRINT(EN_MSG, "Enter user name"); fflush(stdout); fgets(chatMsg.sSender, MAX_NAME_LEN, stdin); chatMsg.sSender[strcspn(chatMsg.sSender, "\n")] = '\0'; TEST_PRINT(EN_DEBUG, "User name %s", chatMsg.sSender); TEST_PRINT(EN_MSG, "Enter user password"); fflush(stdout); fgets(chatMsg.sContent, MAX_PASSWORD_LEN, stdin); send(g_sockfd, &chatMsg, sizeof(ChatMessage), 0); break; case 2: chatMsg.type = MSG_REGIST; TEST_PRINT(EN_MSG, "Enter user name"); fflush(stdout); fgets(chatMsg.sSender, MAX_NAME_LEN, stdin); chatMsg.sSender[strcspn(chatMsg.sSender, "\n")] = '\0'; TEST_PRINT(EN_MSG, "Enter user password"); fflush(stdout); fgets(chatMsg.sContent, MAX_PASSWORD_LEN, stdin); chatMsg.sSender[strcspn(chatMsg.sContent, "\n")] = '\0'; send(g_sockfd, &chatMsg, sizeof(ChatMessage), 0); break; case 3: bRes = false; goto end; default: TEST_PRINT(EN_ERROR, "invalid Choice"); goto start; } int recvLen = recv(g_sockfd, &chatMsg, sizeof(ChatMessage), 0); if (0 < recvLen) { TEST_PRINT(EN_MSG, "\n[%s]:\n%s", chatMsg.sSender,chatMsg.sContent); fflush(stdout); } else if (0 == recvLen) { TEST_PRINT(EN_MSG, "Server disconnected"); bRes = false; goto end; } else { if (errno != EAGAIN && errno != EWOULDBLOCK) { TEST_PRINT(EN_ERROR, "Recv error: %s", strerror(errno)); bRes = false; goto end; } } if (0 == strncmp(chatMsg.sContent, "Login success", MAX_MSG_LEN)) { strncpy(g_sClientName, chatMsg.sReceiver, MAX_NAME_LEN); g_sClientName[MAX_NAME_LEN - 1] = '\0'; bRes = true; break; } } end: TEST_PRINT(EN_TRACE, "out"); return bRes; } int main(int argc, char* argv[]) { TEST_PRINT(EN_TRACE, "in"); struct timeval tv = {0}; struct sockaddr_in servAddr = {0}; signal(SIGINT, handle_signal); signal(SIGTSTP, handle_signal); if (2 != argc) { TEST_PRINT(EN_ERROR, "Usage: %s <server_ip>\n", argv[0]); return EXIT_FAILURE; } g_sockfd = socket(AF_INET, SOCK_STREAM, 0); if (0 > g_sockfd) { TEST_PRINT(EN_ERROR, "Socket creation failed"); return EXIT_FAILURE; } tv.tv_sec = TIMEOUT_SEC; tv.tv_usec = TIMEOUT_USEC; setsockopt(g_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); setsockopt(g_sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); servAddr.sin_family = AF_INET; servAddr.sin_port = htons(SERVER_PORT); if (0 >= inet_pton(AF_INET, argv[1], &servAddr.sin_addr)) { TEST_PRINT(EN_ERROR, "Invalid address"); goto end; } if (0 > connect(g_sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr))) { TEST_PRINT(EN_ERROR, "Connection failed: %s", strerror(errno)); goto end; } TEST_PRINT(EN_MSG, "Connected to %s:%d", argv[1], SERVER_PORT); if (!client_login()) { TEST_PRINT(EN_ERROR, "Client login failed"); goto end; } if (0 != pthread_create(&g_readThread, NULL, read_thread, NULL)) { TEST_PRINT(EN_ERROR, "Failed to create read thread"); goto end; } if (0 != pthread_create(&g_writeThread, NULL, write_thread, NULL)) { TEST_PRINT(EN_ERROR, "Failed to create write thread"); goto end; } if (0 != pthread_join(g_readThread, NULL)) { TEST_PRINT(EN_ERROR, "Failed to join read thread"); goto end; } if (0 != pthread_join(g_writeThread, NULL)) { TEST_PRINT(EN_ERROR, "Failed to join write thread"); goto end; } end: cleanup_client(); TEST_PRINT(EN_TRACE, "out"); return EXIT_SUCCESS; } 在main()函数结束后,会cleanup_client();在cleanup_client();中有线程取消操作,这样有问题吗?
08-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值