文件描述符的资源很重要 几乎所有的系统调用都是和文件描述符打交道 ,但是文件描述符的数量是有限的 我们必须关闭不使用的文件描述符 以释放其资源。
比如作为守护进程运行的服务器程序 就应该总是关闭标准输入 输出 错误 这三个文件描述符
liunx对应用程序能打开的最大文件描述符数量受两个层次的限制 :用户限制和系统限制
ulimit -n 来查看用户级文件描述符数量
ulimit -SHn max-file-number 将文件描述符设置为max-file-number
这种修改是临时的 只在当前的session中有效 永久修改可以在/etc/security/limits.conf文件中加入下面两项
hard nofile max-file-number
soft nofile max-file-number
第一行表示系统硬限制 第二行表示软限制
要修改系统级的文件描述符的数量限制 可以使用下面命令
sysctl -w fs.file-max = max-file-number
这个命令也是临时修改 要想永远生效 则需要在/etc/sysctl.conf文件中添加下面一项:
fs.file-max = max-file-number
然后通过sysctl -p命令来使得更改生效
几乎所有的内核模块都在/proc/sys/文件系统下面提供某个配置文件供用户修改 通常一个配置文件对应着一个内核参数 文件名就是参数的名字 文件的内容就是参数的值 可以通过sysctl -a来查看这些参数名称
此目录下面的参数都和文件系统相关 最重要的是下面两个参数
此模块下记录了网络模块相关的参数 其中TCP/IP协议相关的参数主要位于三个子目录中:core ipv4 ipv6
下面将gdb如何调试多进程和多线程
后台程序不可避免而比较困难的部分
父进程用fork创建子进程后 默认gdb继续调试父进程 那么如何切换到子进程
子进程本质也是进程 可以先运行服务器的程序 找到子进程的pid 然后将它附加attach到gdb调试器上面
(待尝试后补充截图)
使用调试器选择follow-fork-mode
gdb调试器的选项follow-fork-mode允许在程序执行的时候 是选择 调试父进程还是子进程
mode 可选parent 和child
下面列举常用的gdb命令用于多线程程序调试
用I/O复用方式 多线程,多进程并发编程方式。
单纯的I/O服用方式施压是最高的 现成和进程的调度本身也是要占用CPU的时间
可以使用epoll来实现一个通用的服务器压力测试程序。
#include #include #include #include #include #include #include #include #include #include #include static const char* request = "GET http://localhost/index.html HTTP/1.1\r\nConnection: keep-alive\r\n\r\nxxxxxxxxxxxx"; int setnonblocking( int fd ) { int old_option = fcntl( fd, F_GETFL ); int new_option = old_option | O_NONBLOCK; fcntl( fd, F_SETFL, new_option ); return old_option; } void addfd( int epoll_fd, int fd ) { epoll_event event; event.data.fd = fd; event.events = EPOLLOUT | EPOLLET | EPOLLERR; epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event ); setnonblocking( fd ); } bool write_nbytes( int sockfd, const char* buffer, int len ) { int bytes_write = 0; printf( "write out %d bytes to socket %d\n", len, sockfd ); while( 1 ) { bytes_write = send( sockfd, buffer, len, 0 ); if ( bytes_write == -1 ) { return false; } else if ( bytes_write == 0 ) { return false; } len -= bytes_write; buffer = buffer + bytes_write; if ( len <= 0 ) { return true; } } } bool read_once( int sockfd, char* buffer, int len ) { int bytes_read = 0; memset( buffer, '\0', len ); bytes_read = recv( sockfd, buffer, len, 0 ); if ( bytes_read == -1 ) { return false; } else if ( bytes_read == 0 ) { return false; } printf( "read in %d bytes from socket %d with content: %s\n", bytes_read, sockfd, buffer ); return true; } void start_conn( int epoll_fd, int num, const char* ip, int port ) { int ret = 0; struct sockaddr_in address; bzero( &address, sizeof( address ) ); address.sin_family = AF_INET; inet_pton( AF_INET, ip, &address.sin_addr ); address.sin_port = htons( port ); for ( int i = 0; i < num; ++i ) { sleep( 1 ); int sockfd = socket( PF_INET, SOCK_STREAM, 0 ); printf( "create 1 sock\n" ); if( sockfd < 0 ) { continue; } if ( connect( sockfd, ( struct sockaddr* )&address, sizeof( address ) ) == 0 ) { printf( "build connection %d\n", i ); addfd( epoll_fd, sockfd ); } } } void close_conn( int epoll_fd, int sockfd ) { epoll_ctl( epoll_fd, EPOLL_CTL_DEL, sockfd, 0 ); close( sockfd ); } int main( int argc, char* argv[] ) { assert( argc == 4 ); int epoll_fd = epoll_create( 100 ); start_conn( epoll_fd, atoi( argv[ 3 ] ), argv[1], atoi( argv[2] ) ); epoll_event events[ 10000 ]; char buffer[ 2048 ]; while ( 1 ) { int fds = epoll_wait( epoll_fd, events, 10000, 2000 ); for ( int i = 0; i < fds; i++ ) { int sockfd = events[i].data.fd; if ( events[i].events & EPOLLIN ) { if ( ! read_once( sockfd, buffer, 2048 ) ) { close_conn( epoll_fd, sockfd ); } struct epoll_event event; event.events = EPOLLOUT | EPOLLET | EPOLLERR; event.data.fd = sockfd; epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event ); } else if( events[i].events & EPOLLOUT ) { if ( ! write_nbytes( sockfd, request, strlen( request ) ) ) { close_conn( epoll_fd, sockfd ); } struct epoll_event event; event.events = EPOLLIN | EPOLLET | EPOLLERR; event.data.fd = sockfd; epoll_ctl( epoll_fd, EPOLL_CTL_MOD, sockfd, &event ); } else if( events[i].events & EPOLLERR ) { close_conn( epoll_fd, sockfd ); } } } }
如果websrv服务器程序足够稳定 那服务器程序就不会崩溃 一直运行下去。