postgresql socket读数据返回-1
如下所示:
{ n = secure_raw_read(port, ptr, len); // pg的socket读是非阻塞读,所以返回-1不影响,后面等到socket可读之后继续读。见下文socket返回值解释 waitfor = WL_SOCKET_READABLE; } /* In blocking mode, wait until the socket is ready */ if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN)) // 非阻塞读,且errono==EAGAIN,所以就是每次处理完一个SQL语句后,会继续读下一个SQL,发现读不到,进到这里,通过latch设置等待客户端可读事件 { WaitEvent event; Assert(waitfor); ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL); WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1, WAIT_EVENT_CLIENT_READ); /* * If the postmaster has died, it's not safe to continue running, * because it is the postmaster's job to kill us if some other backend * exits uncleanly. Moreover, we won't run very well in this state; * helper processes like walwriter and the bgwriter will exit, so * performance may be poor. Finally, if we don't exit, pg_ctl will be * unable to restart the postmaster without manual intervention, so no * new connections can be accepted. Exiting clears the deck for a * postmaster restart. * * (Note that we only make this check when we would otherwise sleep on * our latch. We might still continue running for a while if the * postmaster is killed in mid-query, or even through multiple queries * if we never have to wait for read. We don't want to burn too many * cycles checking for this very rare condition, and this should cause * us to exit quickly in most cases.) */ if (event.events & WL_POSTMASTER_DEATH) ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating connection due to unexpected postmaster exit"))); /* Handle interrupt. */ if (event.events & WL_LATCH_SET) { ResetLatch(MyLatch); ProcessClientReadInterrupt(true); /* * We'll retry the read. Most likely it will return immediately * because there's still no data available, and we'll wait for the * socket to become ready again. */ } goto retry; }
在底层,linux内核 4.x平台中,等待socket可读是通过epoll_wait实现的。
#0 0x00007fa94fd880bb in epoll_wait () from /lib64/libc.so.6 #1 0x00000000007916de in WaitEventSetWaitBlock (nevents=1, occurred_events=0x7fffb042fd40, cur_timeout=-1, set=0x2bf7e58) at latch.c:1295 #2 WaitEventSetWait (set=0x2bf7e58, timeout=timeout@entry=-1, occurred_events=occurred_events@entry=0x7fffb042fd40, nevents=nevents@entry=1, wait_event_info=wait_event_info@entry=100663296) at latch.c:1247 #3 0x0000000000688233 in secure_read (port=0x2c5b970, ptr=0xdc7500 <PqRecvBuffer>, len=8192) at be-secure.c:184 #4 0x000000000068eb7b in pq_recvbuf () at pqcomm.c:947 #5 pq_recvbuf () at pqcomm.c:923 #6 0x000000000068f985 in pq_getbyte () at pqcomm.c:990 #7 0x00000000007b531e in SocketBackend (inBuf=0x7fffb042ff40) at postgres.c:357 #8 ReadCommand (inBuf=0x7fffb042ff40) at postgres.c:530 #9 PostgresMain (argc=<optimized out>, argv=argv@entry=0x2c64490, dbname=<optimized out>, username=<optimized out>) at postgres.c:4598 #10 0x000000000073549d in BackendRun (port=0x2c5b970, port=0x2c5b970) at postmaster.c:5063
3.x内核版本中我记得是poll而非poll。
socket读写返回不同值的总结
在调用socket读写函数read(),write()时,都会有返回值。如果没有正确处理返回值,就可能引入一些问题
1当read()或者write()函数返回值大于0时,表示实际从缓冲区读取或者写入的字节数目
2当read()函数返回值为0时,表示对端已经关闭了 socket,这时候也要关闭这个socket,否则会导致socket泄露。netstat命令查看下,如果有closewait状态的socket,就是socket泄露了
当write()函数返回0时,表示当前写缓冲区已满,是正常情况,下次再来写就行了。
3当read()或者write()返回-1时,一般要判断errno
如果errno == EINTR,表示系统当前中断了,直接忽略
如果errno == EAGAIN或者EWOULDBLOCK,非阻塞socket直接忽略;如果是阻塞的socket,一般是读写操作超时了,还未返回。这个超时是指socket的SO_RCVTIMEO与SO_SNDTIMEO两个属性。所以在使用阻塞socket时,不要将超时时间设置的过小。不然返回了-1,你也不知道是socket连接是真的断开了,还是正常的网络抖动。一般情况下,阻塞的socket返回了-1,都需要关闭重新连接。
4.另外,对于非阻塞的connect,可能返回-1.这时需要判断errno,如果 errno == EINPROGRESS,表示正在处理中,否则表示连接出错了,需要关闭重连。之后使用select,检测到该socket的可写事件时,要判断getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &errlen),看socket是否出错了。如果err值为0,则表示connect成功;否则也应该关闭重连
5 在使用epoll时,有ET与LT两种模式。ET模式下,socket需要read或者write到返回-1为止。对于非阻塞的socket没有问题,但是如果是阻塞的socket,正如第三条中所说的,只有超时才会返回。所以在ET模式下千万不要使用阻塞的socket。那么LT模式为什么没问题呢?一般情况下,使用LT模式,我们只要调用一次read或者write函数,如果没有读完或者没有写完,下次再来就是了。由于已经返回了可读或者可写事件,所以可以保证调用一次read或者write会正常返回。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)