在做onvif开发的时候,因为侦听端口可能会被修改,因此accept之前通过select侦听,
select设置超时时间,所以即使没有客户端连接,超时后也可以修改侦听端口。
但是测试时发现select设置了超时时间后cpu占用率很高,达到80%以上。
经过排查和查资料发现是select超时时间使用的问题。最初设置超时时间后,进入循环,有客户端连接则处理。
但实际上select设置超时时间时,time_out参数传入的是指针,每次select都是使用time_out剩余的时间。
多次调用后time_out的值就被减为0 了,这时相当于全速运行,因此cpu高
正确做法是:
1、类似FD_SET,每次调用select都重置time_out,即每次循环都要重新给time_out赋值。
2、使用pselect替换select,pselect中传入的time_out参数是值传递,不会改变time_out的值,即每次超时时间一样;
在man中也有说明:
DESCRIPTION
select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of
I/O operation (e.g., input possible). A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without
blocking.
The operation of select() and pselect() is identical, with three differences:
(i) select() uses a timeout that is a struct timeval (with seconds and microseconds), while pselect() uses a struct timespec (with seconds and nanoseconds).
(ii) select() may update the timeout argument to indicate how much time was left. pselect() does not change this argument.
select会更新timeout参数的值,每次调用都是用上次剩余的时间,多次调用或一次超时返回后timeout就一直为0,频繁调用导致CPU高。pselect则不会改变这个参数。
(iii) select() has no sigmask argument, and behaves as pselect() called with NULL sigmask.
正确应该每次都重新赋值(或采用pselect)
struct timeval time_out;
time_out.tv_sec = 2;
time_out.tv_usec = 0;
ret = select(listen_fd + 1, &read_fd, NULL, NULL, &time_out);
这点要特别注意,很容易忽视。