您的位置:首页 > 运维架构 > Linux

Linux programming

2016-01-25 15:23 363 查看

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: