MISC
Linux的图形编程环境库
GTK: GNOME环境基础
Qt:KDE环境的基础
window编译Unix代码的方法
1.修改编译器,让window下的编译器把诸如fork的调用翻译成等价的形式--这就是mingw的做法.
2.修改库,让window提供一个类似unix提供的库,他们对程序的接口如同unix一样,而这些库,当然是由win32的API实现的--这就是cygwin的做法.
Process Layout
Linux的内存模型,一般为:
地址 | 作用 | 说明 |
>=0xc000 0000 | 内核虚拟存储器 | 用户代码不可见区域 |
<0xc000 0000 | Stack(用户栈) | ESP指向栈顶 |
| ↓
↑ |
空闲内存 |
>=0x4000 0000 | 文件映射区 |
|
<0x4000 0000 |
↑ |
空闲内存
|
| Heap(运行时堆) | 通过brk/sbrk系统调用扩大堆,向上增长。 |
| .data、.bss(读写段) | 从可执行文件中加载 |
>=0x0804 8000 | .init、.text、.rodata(只读段) | 从可执行文件中加载 |
<0x0804 8000 | 保留区域 |
|
很多书上都有类似的描述,本图取自于《深入理解计算机系统》p603
每个进程的所占内存信息在/proc/XX/maps中记录。包括堆,栈,代码段。。。
当动态分配的大内存时,不是在heap区,而是在文件映射区
Network
Socket通信的步骤
Server端:
1) 调用socket来创建server_socket,
2) 调用bind来命名server_socket,
3) 调用listen来创建队列用来存放来自client的连接
4) 调用accept来接受client的连接,并获得client_socket
5) 当与client建立连接后,可以对client_socket进行write和read
6) 调用close来关闭server_socket和client_socket
Client端:
1) 调用socket来创建client_socket
2) 调用connect使client_socket与server的server_socket建立连接
3) 当与server建立连接后,可以对client_socket进行write和read
1) Server socket是命名套接字,用于建立连接
2) Client socket是未命名套接字,用于双向读写数据,即发送和接收数据
阻塞/非阻塞发送接收
关于socket属性和send/recv的MSG_WAITALL参数设置的实验结果:
前提:getsockopt(sockfd,SOL_SOCKET, SO_SNDBUF, &val, &len) -> val is 50700
(1) Sock fd默认 + send (MSG_WAITALL)202800(50700 * 4) -> 第一次send返回值为202800
(2) Sock fd默认 + send (MSG_DONTWAIT)202800(50700 * 4) –> 第一次send返回值为65536,对方没有取出接收缓冲区中的数据时,第二次send返回值为147456
(3) Sock fd设置|=O_NONBLOCK + send(不论设置阻塞还是非阻塞)或是用write,现象同(2)
结论:
1. 如果fd没有设置为非阻塞,send的wait参数决定是否阻塞发送;否则一律非阻塞发送
2. Block send,不论是否用select,send返回值总是要发送的字节数
3. NonBlock send,不论是否用select, send返回值可能不等于要发送的字节数;但是发送字节数小于65536时,如果用select判断可读时写入sock,返回值总是要发送的字节数目。
4. block read在可读时就返回,返回值不一定等于参数设置的字节数
Socket的断开处理
正常断开从来都应该是客户端主动提出的,服务端响应客户端退出请求,close发送FIN到客户端,客户端recv返回0,于是close完成整个断开.
非正常断开只能做心跳,服务端select接受所有客户端的UDP心跳包并立即返回心跳响应包并给相应客户端心跳计数清0,对于不可读的套接字,累加心跳计数+1,在计数超过K次时,则断开并关闭该客户的TCP套接字和UDP.
客户端则是给服务器发送一个心跳包,然后select该 UDP心跳套接字N秒,如超时,则心跳计数+1,如果连续超时K次,则断开并关闭该TCP套接字和UDP套接字.
如果对方调用close正常断开(在没有自定退出包的情况下)
recv会收到 0,这时就可以判断对方断开
如果再次recv产生一个-1错误
IO的处理方式——阻塞和非阻塞,Select,poll,epoll
非阻塞模式,如果暂时没有数据,返回的值也会是<=0的,
阻塞模式,返回<=0的值是可以认为socket已经无效了。
当使用 select()函数测试一个socket是否可读时,如果select()函数返回值为1,
且使用recv()函数读取的数据长度为0时,就说明该socket已经断开。
在linux新的内核中,有了一种替换它的机制,就是epoll。
相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。
Linux线程
线程标准
Native POSIX Thread Library (NPTL)标准,大部分线程库都是基于此标准而实现的。
Linux的线程实现
Linux线程是通过进程来实现。Linux kernel为进程创建提供一个clone()系统调用,clone的参数包括如CLONE_VM, CLONE_FILES, CLONE_SIGHAND 等。通过clone()的参数,新创建的进程,也称为LWP(Lightweight process)与父进程共享内存空间,文件句柄,信号处理等,从而达到创建线程相同的目的。
Linux 2.6的线程库叫NPTL(NativePOSIX Thread Library)。POSIX thread(pthread)是一个编程规范,通过此规范开发的多线程程序具有良好的跨平台特性。尽管是基于进程的实现,但新版的NPTL创建线程的效率非常高。
NPTL的实现是在kernel增加了futex(fastuserspace mutex)支持用于处理线程之间的sleep与wake。futex是一种高效的对共享资源互斥访问的算法。kernel在里面起仲裁作用,但通常都由进程自行完成。
NPTL是一个1×1的线程模型,即一个线程对于一个操作系统的调度进程,优点是非常简单。而其他一些操作系统比如Solaris则是MxN的,M对应创建的线程数,N对应操作系统可以运行的实体。(N<M),优点是线程切换快,但实现稍复杂。
在技术实现上,NPTL仍然采用1:1的线程模型,并配合glibc和最新的Linux Kernel2.5.x开发版在信号处理、线程同步、存储管理等多方面进行了优化。和LinuxThreads不同,NPTL没有使用管理线程,核心线程的管理直接放在核内进行,这也带了性能的优化。
线程实现
在核外实现的线程又可以分为"一对一"、"多对一"两种模型,前者用一个核心进程(也许是轻量进程)对应一个线程,将线程调度等同于进程调度,交给核心完成,而后者则完全在核外实现多线程,调度也在用户态完成。后者就是前面提到的单纯的用户级线程模型的实现方式
Linux内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但Linux着重优化了进程的调度开销,一定程度上也弥补了这一缺陷。目前最流行的线程机制LinuxThreads所采用的就是线程-进程"一对一"模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。
POSIC线程相比进程的优势:
代价小,可以使两件事请或者更多事情以一种非常紧密的方式同时发生
因为当执行fork调用时,创建进程副本。新的进程将拥有自己的变量和PID,时间调度也是独立的,完全与父进程无关; 但是进程中创建一个新线程时,新的执行线程将拥有自己的栈(即局部变量),但是与它的创建者共享全局变量,文件描述符,信号处理函数和当前目录状态
installpthread manpage
man -k pthread OR apropos pthread
sudo apt-get install manpages-posixmanpages-posix-dev
createdetach pthread
非分离线程:父线程要用join函数等待子线程结束来回收子线程的资源
NOTE:线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源
分离线程:子线程退出后资源自动释放掉
线程退出
1.执行完自动退出
2.线程本身调用pthread_exit (void * retval);退出
外部调用pthread_cancel (pthread_t thread);终止一个线程
Dynamic library loading
Debugging
errno
进程中的errno变量是被所有线程共享的,所以会覆盖掉上一次的数值
man 3 errno to view description of error no
打印errno的方法:
1. #include <string.h>
char * strerror(int errnum);
2. #include <stdio.h>
void perror(const char *msg); //print to console
查看程序堆栈大小ulimit -a