《后台开发核心技术与应用实践》(四)
2017-04-24 22:28
597 查看
多线程
1 多线程
2多线程的创建与结束
3 线程的属性
4 多线程的同步
5 多线程的重入
进程
1程序与进程
2进程的创建与结束
3僵尸进程
4守护进程
进程间通信
1管道
2消息队列
3共享内存
4信号量
5ipcs命令
(2)同一个进程内部的多个线程共享该进程的所有资源;通过线程可以支持同一个应用程序内部的并发,免去了进程频繁切换的开销;并发任务间通信也更简单。
(3)多线程在的进程在内存中有多个栈,每个栈对应一个线程,多个栈之间以一定的空白区域隔开,以备栈的增长,任何一个空白区域被填满都会导致栈溢出。
(2)线程退出:执行完成后隐式退出;由线程本身显示调用pthread_exit 函数退出;被其他线程用pthread_cancel函数终止
(3)pthread_join用于等待一个线程的结束,也就是主线程中要是加了这段代码,就会在加代码的位置卡主,直到这个线程执行完毕才往下走。一般都是pthread_exit在线程内退出,然后返回一个值。这个时候就跳到主线程的pthread_join了(因为一直在等你结束),这个返回值会直接送到pthread_join,实现了主与分线程的通信。
(4)向线程传递参数
(5)获得线程id
(2)属性对象主要包括:作用域(scope)、栈尺寸(stack size)、栈地址(stack address)、优先级(priority)、分离的状态(detached state)、调度策略和参数(scheduling policy and parameters)。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
(2)条件变量。条件变量本身不是锁!但它也可以造成阻塞。通常与互斥锁配合使用,给多线程提供一个会合的场所。
(3)读写锁。与互斥量类似,但读写锁允许更高的并行性。其特性为:写独占,读共享。
(4)信号量。进化版的互斥锁(1–>N)。信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
1.不在函数内部使用静态或者全局数据
2.不返回静态或者全局数据,所有的数据都由函数调用者提供
3.使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
4. 如果必须访问全局数据,使用互斥锁来保护
5.不调用不可重入函数
(2)不可重入函数特点:
1.函数中使用了静态变量,无论是全局静态变量还是局部静态变量
2.函数返回静态变量
3.函数中调用了不可重入函数
4.函数中使用了静态的数据结构
5.调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
6.调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构。
(3)_REENTRANT宏
在一个多线程程序里,默认情况下,只有一个errno变量供所有的线程共享。在一个线程准备获取刚才的错误代码时,该变量很容易被另一个线程中的函数调用所改变。类似的问题还存在于fputs之类的函数中,这些函数通常用一个单独的全局性区域来缓存输出数据。
为解决这个问题,需要使用可重入的例程。可重入代码可以被多次调用而仍然工作正常。编写的多线程程序,通过定义宏_REENTRANT来告诉编译器我们需要可重入功能,这个宏的定义必须出现于程序中的任何#include语句之前。
_REENTRANT为我们做三件事情,并且做的非常优雅:
1.它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。
2.stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。
3.在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值。
(2)程序转换成进程步骤:
1.内核将程序读入内存,为程序分配内存空间
2.内核为该进程分配进程标识符(PID)和其他所需资源
3.内核为进程保存PID及相应的状态信息,把进程放到运行队列中等待执行。程序转化为进程后就可以被操作系统的调度程序调度执行了。
1.在父进程中,fork返回新创建子进程的进程ID;
2.在子进程中,fork返回0;
3.如果创建出错,fork返回-1
(2)exit()函数结束进程。
1.exit是函数,带参数,执行完后把控制权交给系统;return是函数执行完后的返回,执行完后把控制权交给调用函数
2.exit是正常终止进程;abort是异常终止
3.exit()会将缓冲区的数据写完后再退出;_exit()函数直接退出
(2)僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。
(2)创建守护进程步骤:
1.创建子进程,父进程退出(使子进程成为孤儿进程)
2.在子进程中创建新的会话(脱离控制终端)
3.改变当前目录为根目录
4.重设文件权限掩码
5.关闭文件描述符
6.守护进程的退出
(2)无名管道和有名管道(FIFO)。前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。
(2)得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存,在Linux系统下,这只有通过限制Linux系统存取的内存才可以做到,这当然不太实际。常用的方式是通过shmXXX函数族来实现利用共享内存进行存储的。
(3)使用共享内存优点:方便,函数接口简单,数据的共享使得进程间的数据不用传送,而是直接访问,也加快了效率。不像无名管道一样要求亲缘关系。
(4)使用共享内存缺点:没有提供同步机制,使得在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。
(2)ipcs -q列出进程中的消息队列
(3)ipcs -s列出所有信号量
(4)ipcs -m列出所有共享内存信息
(5)ipcs -l列出系统的限额
(6)ipcs -t列出最后的访问时间
(7)ipcs -u列出当前的使用情况
《后台开发核心技术与应用实践》(一)
《后台开发核心技术与应用实践》(二)
《后台开发核心技术与应用实践》(三)
《后台开发核心技术与应用实践》(四)
1 多线程
2多线程的创建与结束
3 线程的属性
4 多线程的同步
5 多线程的重入
进程
1程序与进程
2进程的创建与结束
3僵尸进程
4守护进程
进程间通信
1管道
2消息队列
3共享内存
4信号量
5ipcs命令
9. 多线程
9.1. 多线程
(1)多进程频繁上下文切换引起的额外开销可能会严重影响系统性能;进程间通信要求复杂的系统级实现(2)同一个进程内部的多个线程共享该进程的所有资源;通过线程可以支持同一个应用程序内部的并发,免去了进程频繁切换的开销;并发任务间通信也更简单。
(3)多线程在的进程在内存中有多个栈,每个栈对应一个线程,多个栈之间以一定的空白区域隔开,以备栈的增长,任何一个空白区域被填满都会导致栈溢出。
9.2.多线程的创建与结束
(1)线程创建:pthread_create函数(2)线程退出:执行完成后隐式退出;由线程本身显示调用pthread_exit 函数退出;被其他线程用pthread_cancel函数终止
(3)pthread_join用于等待一个线程的结束,也就是主线程中要是加了这段代码,就会在加代码的位置卡主,直到这个线程执行完毕才往下走。一般都是pthread_exit在线程内退出,然后返回一个值。这个时候就跳到主线程的pthread_join了(因为一直在等你结束),这个返回值会直接送到pthread_join,实现了主与分线程的通信。
(4)向线程传递参数
(5)获得线程id
9.3. 线程的属性
(1)属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用,之后必须用pthread_attr_destroy函数来释放资源。(2)属性对象主要包括:作用域(scope)、栈尺寸(stack size)、栈地址(stack address)、优先级(priority)、分离的状态(detached state)、调度策略和参数(scheduling policy and parameters)。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
9.4. 多线程的同步
(1)互斥锁。通过加锁将原先分离的多个指令构成不可分割的一个原子操作(2)条件变量。条件变量本身不是锁!但它也可以造成阻塞。通常与互斥锁配合使用,给多线程提供一个会合的场所。
(3)读写锁。与互斥量类似,但读写锁允许更高的并行性。其特性为:写独占,读共享。
(4)信号量。进化版的互斥锁(1–>N)。信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
9.5. 多线程的重入
(1)可重入函数特点:1.不在函数内部使用静态或者全局数据
2.不返回静态或者全局数据,所有的数据都由函数调用者提供
3.使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
4. 如果必须访问全局数据,使用互斥锁来保护
5.不调用不可重入函数
(2)不可重入函数特点:
1.函数中使用了静态变量,无论是全局静态变量还是局部静态变量
2.函数返回静态变量
3.函数中调用了不可重入函数
4.函数中使用了静态的数据结构
5.调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
6.调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构。
(3)_REENTRANT宏
在一个多线程程序里,默认情况下,只有一个errno变量供所有的线程共享。在一个线程准备获取刚才的错误代码时,该变量很容易被另一个线程中的函数调用所改变。类似的问题还存在于fputs之类的函数中,这些函数通常用一个单独的全局性区域来缓存输出数据。
为解决这个问题,需要使用可重入的例程。可重入代码可以被多次调用而仍然工作正常。编写的多线程程序,通过定义宏_REENTRANT来告诉编译器我们需要可重入功能,这个宏的定义必须出现于程序中的任何#include语句之前。
_REENTRANT为我们做三件事情,并且做的非常优雅:
1.它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。
2.stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。
3.在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值。
10.进程
10.1.程序与进程
(1)进程结构:代码段、数据段、堆栈段。堆栈段包括进程控制块PCB(2)程序转换成进程步骤:
1.内核将程序读入内存,为程序分配内存空间
2.内核为该进程分配进程标识符(PID)和其他所需资源
3.内核为进程保存PID及相应的状态信息,把进程放到运行队列中等待执行。程序转化为进程后就可以被操作系统的调度程序调度执行了。
10.2.进程的创建与结束
(1)fork()函数创建进程。仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:1.在父进程中,fork返回新创建子进程的进程ID;
2.在子进程中,fork返回0;
3.如果创建出错,fork返回-1
(2)exit()函数结束进程。
1.exit是函数,带参数,执行完后把控制权交给系统;return是函数执行完后的返回,执行完后把控制权交给调用函数
2.exit是正常终止进程;abort是异常终止
3.exit()会将缓冲区的数据写完后再退出;_exit()函数直接退出
10.3.僵尸进程
(1)孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。(2)僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。
10.4.守护进程
(1)linux或UNIX在系统引导时会开启很多服务,这些服务就叫作守护进程。守护进程是脱离终端并且在后台运行的进程(2)创建守护进程步骤:
1.创建子进程,父进程退出(使子进程成为孤儿进程)
2.在子进程中创建新的会话(脱离控制终端)
3.改变当前目录为根目录
4.重设文件权限掩码
5.关闭文件描述符
6.守护进程的退出
11.进程间通信
11.1.管道
(1)管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程或兄弟进程。(2)无名管道和有名管道(FIFO)。前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。
11.2.消息队列
消息队列用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中用来保存消息的队列,它在系统内核中是以消息链表的形式出现。消息链表中节点的结构用msg声明。11.3.共享内存
(1)共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。(2)得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存,在Linux系统下,这只有通过限制Linux系统存取的内存才可以做到,这当然不太实际。常用的方式是通过shmXXX函数族来实现利用共享内存进行存储的。
(3)使用共享内存优点:方便,函数接口简单,数据的共享使得进程间的数据不用传送,而是直接访问,也加快了效率。不像无名管道一样要求亲缘关系。
(4)使用共享内存缺点:没有提供同步机制,使得在使用共享内存进行进程间通信时,往往要借助其他的手段来进行进程间的同步工作。
11.4.信号量
信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。11.5.ipcs命令
(1)ipcs -a列出本用户所有相关的ipcs参数(2)ipcs -q列出进程中的消息队列
(3)ipcs -s列出所有信号量
(4)ipcs -m列出所有共享内存信息
(5)ipcs -l列出系统的限额
(6)ipcs -t列出最后的访问时间
(7)ipcs -u列出当前的使用情况
《后台开发核心技术与应用实践》(一)
《后台开发核心技术与应用实践》(二)
《后台开发核心技术与应用实践》(三)
《后台开发核心技术与应用实践》(四)
相关文章推荐
- C++小结(二)(《后台开发核心技术与应用实践》第二章笔记)
- 《后台开发核心技术与应用实践》(二)
- 《后台开发核心技术与应用实践》(一)
- 后台开发:核心技术与应用实践(边写代码边读书才是最好的学习方式)
- 做后台开发用到的技能都在这儿——《后台开发:核心技术与应用实践》
- 《后台开发核心技术与应用实践》(三)
- 地址已经被使用——Address already in use(纠正《后台开发:核心技术于应用实践》书中的错误)
- 做后台开发用到的技能都在这儿——《后台开发:核心技术与应用实践》
- 推荐一本今年八月份的新书《后台开发:核心技术与应用实践》,作者腾讯资深后台开发工程师徐晓鑫
- 项目实践精解:C#核心技术应用开发
- 状态栏框架-- 深入Android应用开发:核心技术解析与最佳实践
- 项目实践精解:C#核心技术应用开发
- 极简的C#很优雅——《C#实战开发:核心技术应用与实践》
- 企业级JavaScript应用技术核心及HTML 5开发实践
- C++后台开发核心技术实践学习日志2017/5/11
- App后台开发运维和架构实践学习总结(1)——App后台核心技术之用户验证方案
- 项目实践精解:C#核心技术应用开发
- App后台开发运维和架构实践学习总结(1)——App后台核心技术之用户验证方案
- APP后台开发运维与架构实践 3 : App后台核心技术