-
2021-12-26 11:38:09
接入Internet的主流技术可划分为有线接入和无线接入两种
常见的有线接入技术包括:电话拨号接入、ADSL接入、局域网接入技术Cable Modem和光纤接入等,主要的形式为一点对多点接入,带宽统计复用,以以太网模式进行业务承载。目前电话拨号接入、ASDL接入已基本淘汰。
无线接入技术主要有WiFi、数字微波和卫星通信等,主要以本地多点分配业务、无线室内覆盖、无线宽带大范围接入等方式实现。
有线接入
LAN方式接入
LAN方式接入是利用以太网技术,采用光缆+双绞线的方式对社区进行综合布线。接入方式主要采用以太网技术,以信息化小区的方式为用户服务。在核心节点使用高速路由器,为用户提供FTTX+LAN的宽带接入。基本做到千兆到小区、百兆到居民大楼、十兆到用户。
特点:
-
采用LAN方式接入可以充分利用小区的局域网的资源优势,提供共享宽带;
-
以太网技术成熟、成本低、结构简单、稳定性、可拓充性好;
-
便于网络升级,实现智能化管理,满足不同层次的人们对信息化的需求。
电缆调制解调技术
同轴电缆具有很大的容量,而且抗电子干扰能力强,使用频分多路复用技术可以同时传送上百个电视频道。当和Cable Modem配合使用时,就有了有线电视网高速传送数字信号的技术。
特点:
-
将同轴电缆的整个频带划分为三部分,分别用于数字信号上传、数字信号下传和电视节目(模拟信号)下传;
-
数据下行传输的速率可以达到36Mbps,而上传信道低速调制方式一般为320kbps~10Mbps;
-
在面对比较大的用户基数时,由于频分多路复用方法具有不可拓展性,需要在频分多路复用技术的基础上再采用时分多路复用技术;
-
采用的结构是相对落后的总线型网络结构,即网络用户共同分享有限宽带。
光纤接入技术
光纤接入方式是宽带接入网的发展方向,可以划分为FTTx(Fiber To Thex),x可以是路边(Curb,C)、大楼(Building,B)和家(Home,M)
不同种类的特点:
-
FTTC
-
ONU(有源设备,需提供电源)设置在路边的分线盒处,再ONU网络一侧为光纤,一侧为双绞线;
-
提供2Mbps以下的业务,主要为住宅或小型企业单位服务;
-
适合点到点或者点到多点的树形分支拓扑结构。
-
-
FTTB
-
将ONU放在居民住宅楼或小型企业办公楼内;
-
一点到多点的结构。
-
-
FTTH
-
将ONU移到用户房间内;
-
从本地交换机一直到用户端全部光纤连接,不使用铜缆,也没有有缘设备等中转设备。
-
-
FTTx+LAN
-
以太网布线
-
无线接入
无线接入技术(Radio Interface Technologies,RIT)指通过无限介质将用户终端和网络节点连接起来,实现用户与网络间的信息传递。
GSM接入技术
目前个人移动通信使用最广泛的技术。特点如下:
-
使用窄带TDMA,允许再一个射频(即“蜂窝”)同时进行8组通话;
-
具有较强的保密性和抗干扰性;
-
可以通过WAP(Wireless Application Protocol,无线应用协议)进行上网
CDMA接入技术
CDMA同GSM一样,属于一种比较成熟的无线通信技术,CDMA是利用展频技术,将所要传递的信息加入一个特定信号后,在一个比原来信号还要大的宽带上传输开来。当基地接收到信号后,再将此特定信号删除还原成原来的信号。特点如下:
-
隐秘性和安全性好;
-
每一个频道使用所能提供的全部频谱。
GPRS接入技术
相对原来GSM的拨号方式的电路交换数据传送方式,GPRS是分组交换技术。特点如下:
-
相对于其他的上网方式,用户上网可以减少断网的机会;
-
下载资料和通话是可以同时进行;
-
声音的传送继续使用GSM,数据的传送使用GPRS;
-
传输速度高于GSM,达到56Kbps的传输速度,目前已达到115Kbps
无线局域网技术
无线局域网Wireless LAN,简称WLAN,是计算机网络与无线通信技术相结合的产物。特点如下:
-
不受电缆束缚,可移动,能解决因有线网布线困难等带来的问题;
-
具有组网灵活、扩容方便、与多种网络标准兼容、应用广泛;
-
可满足各类便携机的入网要求;
-
实现计算机局域网远端接入、图文传真、电子邮件等多种功能。
更多相关内容 -
-
你真的搞懂了Python中的四舍五入吗?1.45保留小数点后1位,四舍五入得多少?
2019-11-23 23:00:22先来看看Python内置的四舍五入函数: round(x, n) 该函数返回传入的x数值(浮点型或整型)的四舍五入值; 参数n表示对x进行四舍五入时保留/参考的数位精度; 参数n可以不传入,此时其默认值为0,但若传入的话,则...先来看看Python内置的四舍五入函数:
round(x, n)
- 该函数返回传入的x数值(浮点型或整型)的四舍五入值;
- 参数n表示对x进行四舍五入时保留/参考的数位精度;
- 参数n可以不传入,此时其默认值为0,但若传入的话,则必须为整型;
- 当n为0时,返回的结果为整型,n为负整数或正整数时,返回值的数值类型与传入的x数值的类型一样(整型或者浮点型);
- 若n为正整数,则表示四舍五入时保留/参考的小数点后的位数;
- 若n为负整数,-1表示基于十位四舍五入(舍弃个位)、-2表示基于百位四舍五入(舍弃个位和十位),依次类推;
- round返回的是数值(整型或者浮点型),对于数值,我们关注的是准确性,因为数值是用于算术运算的,比如round(31415.0, 2),得到的结果是“31415.0”,是完全符合要求的,既按约定返回了浮点型(因为传入的参数也是浮点型),同时,也完全满足小数点后2位的数位精度(或者说有效位数),因为对于数值来说,“31415.0”与“31415.00”是完全相等的;
- 但需要注意的是:上述谈到的“数值”的有效位或者精准度,与我们平时提到的“保留多少有效位”,两者的概念是不一样的,或者说至少在编程方面,两者是不同层面的事情,前者关注的是“数值”的精准度,后者用于显示或输出,关注的是对数值的字符串格式化;
- 因此,若数值四舍五入后,用于算术表达式计算,我们则采用round函数对数值进行四舍五入处理,要是仅用于按要求保留有效位显示输出,则直接采用字符串格式化输出即可,例如:f’{31415:.2f}’,输出为“31415.00”。
如下代码所示,各打印了两个数,前者按round调用后返回的真实数值输出,后者按字符串格式化输出。
print('对3.14159265保留小数点后3位、四舍五入的结果:',round(3.14159265, 3), f'{3.14159265:0.3f}') print('对31415.9265仅保留整数部分、四舍五入的结果:',round(31415.9265), f'{31415.9265:.0f}') print('对31415.9265基于百位进行四舍五入的结果:',round(31415.9265, -2), f'{round(31415.9265, -2):.0f}') print('对31415基于百位进行四舍五入的结果:',round(31415, -2), f'{round(31415, -2):.0f}') print('对31415参考小数点后2位、四舍五入的结果:',round(31415, 2), f'{31415:.2f}') print('对31415.0参考小数点后2位、四舍五入的结果:',round(31415.0, 2), f'{31415.0:.2f}')
对3.14159265保留小数点后3位、四舍五入的结果: 3.142 3.142 对31415.9265仅保留整数部分、四舍五入的结果: 31416 31416 对31415.9265基于百位进行四舍五入的结果: 31400.0 31400 对31415基于百位进行四舍五入的结果: 31400 31400 对31415参考小数点后2位、四舍五入的结果: 31415 31415.00 对31415.0参考小数点后2位、四舍五入的结果: 31415.0 31415.00
另外,更重要的,对于Python的这个round内置函数我们在使用时要需要注意以下两点:
- 四舍五入运算必然涉及到进位或者不进位,比如,我们对数值x(如1.45)进行四舍五入运算,假设进位得到A(如1.5,保留小数点后1位)、若不进位得到B(如1.4,保留小数点后1位),有时候会出现一种特别情况,x正好在A与B的中间,即离A和B的距离相同,这种情况下一般是碰到了“5”,此时,究竟取A还是取B呢?Python的处理是取“偶数”(如,1.4的最后一位4为偶数),跟我们传统的“五入”做法是不一样的。如下所示:
print('为了促成偶数,放弃“五入”:',round(0.5),round(-0.5),round(2.5),round(125,-1),round(1.45,1)) print('正好促成偶数,保留“五入”:',round(1.5),round(-1.5),round(1.5),round(135,-1),round(1.35,1))
为了促成偶数,放弃“五入”: 0 0 2 120 1.4 正好促成偶数,保留“五入”: 2 -2 2 140 1.4
- 而且上述规则还存在另外的挑战,当碰到了“5”且又遇上了特别的浮点数时,情况会变得更加复杂,例如,round(6.675, 2)给出的结果是6.67,而不是按上述规则应该得出的6.68,这并不是Python语言的Bug或问题,而是因为:计算机本身对浮点数进行十进制至二进制转换时,存在着一些十进制小数不能以二进制精确地表示出来(相当于除法中除不断的情况),而计算机用来存储数字的二进制位数是有限的,必然导致后面的数位被舍弃,于是,x(比如,当前的6.675)不再是我们十进制中看到的,离A(即6.68)和B(即6.67)的距离完全相等,而是变小了些,离B更近了,于是被round函数选中了6.67。
print('受计算机精度影响的四舍五入:',round(6.675,2),round(1.355,2))
受计算机精度影响的四舍五入: 6.67 1.35
需要说明的是,上述两点特殊情况,对于数值的字符串格式化会产生同样的影响,知道了上述规则和原因后,遇到类似的特殊情况,我们就不会再感到不解或者迷惑了,甚至在特殊的严格场合,我们可能需要进行特殊处理,规避上述情况的发生。如下所示,我们来看看字符串格式化对上述特殊数值的输出情况。
print('旧式格式化操作符:') print('%.0f %.1f %.2f' % (-0.5,1.45,6.675)) print('%.0f %.1f' % (-1.5,1.35)) print('\nstr.format()的格式化:') print('{:.0f} {:.1f} {:.2f}'.format(-0.5,1.45,6.675)) print('{:.0f} {:.1f}'.format(-1.5,1.35)) print('\nf-string格式化:') print(f'{-0.5:.0f} {1.45:.1f} {6.675:.2f}') print(f'{-1.5:.0f} {1.35:.1f}')
旧式格式化操作符: -0 1.4 6.67 -2 1.4 str.format()的格式化: -0 1.4 6.67 -2 1.4 f-string格式化: -0 1.4 6.67 -2 1.4
-
统一数据接入实践分享
2019-10-08 00:15:21 -
函数的可重入和不可重入
2017-04-15 18:26:26https://www.ibm.com/developerworks/cn/linux/l-reent.html这是一篇描述重入函数和不可重入函数的文章。先把他copy过来: ...不过,现在,并发编程已普遍使用,您需要意识到这个缺陷。本文描述了在并行和https://www.ibm.com/developerworks/cn/linux/l-reent.html这是一篇描述重入函数和不可重入函数的文章。先把他copy过来:
在早期的编程中,不可重入性对程序员并不构成威胁;函数不会有并发访问,也没有中断。在很多较老的 C 语言实现中,函数被认为是在单线程进程的环境中运行。
不过,现在,并发编程已普遍使用,您需要意识到这个缺陷。本文描述了在并行和并发程序设计中函数的不可重入性导致的一些潜在问题。信号的生成和处理尤其增加了额外的复杂性。由于信号在本质上是异步的,所以难以找出当信号处理函数 触发某个不可重入函数时导致的 bug。本文:
a. 定义了可重入性,并包含一个可重入函数的 POSIX 清单。
b. 给出了示例,以说明不可重入性所导致的问题。
c. 指出了确保底层函数的可重入性的方法。
d. 讨论了在编译器层次上对可重入性的处理。什么是可重入性?
可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。相反, 不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥 (或者使用信号量,或者在代码的关键部分禁用中断)。可重入函数可以在任意时刻被中断, 稍后再继续运行,不会丢失数据。可重入函数要么使用本地变量,要么在使用全局变量时 保护自己的数据。
可重入函数:
a. 不为连续的调用持有静态数据。
b. 不返回指向静态数据的指针;所有数据都由函数的调用者提供。
c. 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
d. 绝不调用任何不可重入函数。
不要混淆可重入与线程安全。在程序员看来,这是两个独立的概念:函数可以是可重入的,是线程安全的,或者 二者皆是,或者二者皆非。不可重入的函数不能由多个线程使用。另外,或许不可能让某个 不可重入的函数是线程安全的。
IEEE Std 1003.1 列出了 118 个可重入的 UNIX? 函数,在此没有给出副本。参见 参考资料 中指向 unix.org 上此列表的链接。
出于以下任意某个原因,其余函数是不可重入的:
a. 它们调用了 malloc 或 free。
b. 众所周知它们使用了静态数据结构体。
c. 它们是标准 I/O 程序库的一部分。信号和不可重入函数
信号(signal)是软件中断。它使得程序员可以处理异步事件。为了向进程发送一个信号, 内核在进程表条目的信号域中设置一个位,对应于收到的信号的类型。信号函数的 ANSI C 原型是:void (*signal (int sigNum, void (*sigHandler)(int))) (int);
或者,另一种描述形式:
typedef void sigHandler(int); SigHandler *signal(int, sigHandler *);
当进程处理所捕获的信号时,正在执行的正常指令序列就会被信号处理器临时中断。然后进程继续执行, 但现在执行的是信号处理器中的指令。如果信号处理器返回,则进程继续执行信号被捕获时正在执行的 正常的指令序列。
现在,在信号处理器中您并不知道信号被捕获时进程正在执行什么内容。如果当进程正在使用 malloc 在它的堆上分配额外的内存时,您通过信号处理器调用 malloc,那会怎样?或者,调用了正在处理全局数据结构的某个函数,而 在信号处理器中又调用了同一个函数。如果是调用 malloc,则进程会 被严重破坏,因为 malloc 通常会为所有它所分配的区域维持一个链表,而它又 可能正在修改那个链表。
甚至可以在需要多个指令的 C 操作符开始和结束之间发送中断。在程序员看来,指令可能似乎是原子的 (也就是说,不能被分割为更小的操作),但它可能实际上需要不止一个处理器指令才能完成操作。 例如,看这段 C 代码:temp += 1;
在 x86 处理器上,那个语句可能会被编译为:
mov ax,[temp] inc ax mov [temp],ax
这显然不是一个原子操作。
这个例子展示了在修改某个变量的过程中运行信号处理器可能会发生什么事情:清单 1. 在修改某个变量的同时运行信号处理器 #include <signal.h> #include <stdio.h> struct two_int { int a, b; } data; void signal_handler(int signum) { printf ("%d, %d\n", data.a, data.b); alarm (1); } int main (void) { static struct two_int zeros = { 0, 0 }, ones = { 1, 1 }; signal (SIGALRM, signal_handler); data = zeros; alarm (1); while (1) { data = zeros; data = ones; } }
这个程序向 data 填充 0,1,0,1,一直交替进行。同时,alarm 信号 处理器每一秒打印一次当前内容(在处理器中调用 printf 是安全的,当信号发生时 它确实没有在处理器外部被调用)。您预期这个程序会有怎样的输出?它应该打印 0,0 或者 1,1。但是实际的输出 如下所示:
0, 0 1, 1 (Skipping some output...) 0, 1 1, 1 1, 0 1, 0 ...
在大部分机器上,在 data 中存储一个新值都需要若干个指令,每次存储一个字。 如果在这些指令期间发出信号,则处理器可能发现 data.a 为 0 而 data.b 为 1,或者反之。另一方面,如果我们运行代码的机器能够在一个 不可中断的指令中存储一个对象的值,那么处理器将永远打印 0,0 或 1,1。
使用信号的另一个新增的困难是,只通过运行测试用例不能够确保代码没有信号 bug。这一困难的原因在于 信号生成本质上异步的。不可重入函数和静态变量
假定信号处理器使用了不可重入的 gethostbyname。这个函数 将它的值返回到一个静态对象中:static struct hostent host; /* result stored here*/
它每次都重新使用同一个对象。在下面的例子中,如果信号刚好是在 main 中调用 gethostbyname 期间到达,或者甚至在调用之后到达,而程序仍然在使用那个值,则 它将破坏程序请求的值。
清单 2. gethostbyname 的危险用法 main() { struct hostent *hostPtr; ... signal(SIGALRM, sig_handler); ... hostPtr = gethostbyname(hostNameOne); ... } void sig_handler() { struct hostent *hostPtr; ... /* call to gethostbyname may clobber the value stored during the call inside the main() */ hostPtr = gethostbyname(hostNameTwo); ... }
不过,如果程序不使用 gethostbyname 或者任何其他在同一对象中返回信息 的函数,或者如果它每次使用时都会阻塞信号,那么就是安全的。
很多库函数在固定的对象中返回值,总是使用同一对象,它们全都会导致相同的问题。如果某个函数使用并修改了 您提供的某个对象,那它可能就是不可重入的;如果两个调用使用同一对象,那么它们会相互干扰。
当使用流(stream)进行 I/O 时会出现类似的情况。假定信号处理器使用 fprintf 打印一条消息,而当信号发出时程序正在使用同一个流进行 fprintf 调用。 信号处理器的消息和程序的数据都会被破坏,因为两个调用操作了同一数据结构:流本身。
如果使用第三方程序库,事情会变得更为复杂,因为您永远不知道哪部分程序库是可重入的,哪部分是不可重入的。 对标准程序库而言,有很多程序库函数在固定的对象中返回值,总是重复使用同一对象,这就使得那些函数 不可重入。
近来很多提供商已经开始提供标准 C 程序库的可重入版本,这是一个好消息。对于任何给定程序库,您都应该通读它所提供 的文档,以了解其原型和标准库函数的用法是否有所变化。确保可重入性的经验
理解这五条最好的经验将帮助您保持程序的可重入性。
经验 1
返回指向静态数据的指针可能会导致函数不可重入。例如,将字符串转换为大写的 strToUpper 函数可能被实现如下:清单 3. strToUpper 的不可重入版本 char *strToUpper(char *str) { /*Returning pointer to static data makes it non-reentrant */ static char buffer[STRING_SIZE_LIMIT]; int index; for (index = 0; str[index]; index++) buffer[index] = toupper(str[index]); buffer[index] = '\0'; return buffer; }
通过修改函数的原型,您可以实现这个函数的可重入版本。下面的清单为输出准备了存储空间:
清单 4. strToUpper 的可重入版本 char *strToUpper_r(char *in_str, char *out_str) { int index; for (index = 0; in_str[index] != '\0'; index++) out_str[index] = toupper(in_str[index]); out_str[index] = '\0'; return out_str; }
由进行调用的函数准备输出存储空间确保了函数的可重入性。注意,这里遵循了标准惯例,通过向函数名添加“_r”后缀来 命名可重入函数。
经验 2
记忆数据的状态会使函数不可重入。不同的线程可能会先后调用那个函数,并且修改那些数据时不会通知其他 正在使用此数据的线程。如果函数需要在一系列调用期间维持某些数据的状态,比如工作缓存或指针,那么 调用者应该提供此数据。
在下面的例子中,函数返回某个字符串的连续小写字母。字符串只是在第一次调用时给出,如 strtok 子例程。当搜索到字符串末尾时,函数返回 \0。函数可能如下实现:清单 5. getLowercaseChar 的不可重入版本 char getLowercaseChar(char *str) { static char *buffer; static int index; char c = '\0'; /* stores the working string on first call only */ if (string != NULL) { buffer = str; index = 0; } /* searches a lowercase character */ while(c=buff[index]){ if(islower(c)) { index++; break; } index++; } return c; }
这个函数是不可重入的,因为它存储变量的状态。为了让它可重入,静态数据,即 index, 需要由调用者来维护。此函数的可重入版本可能类似如下实现:
清单 6. getLowercaseChar 的可重入版本 char getLowercaseChar_r(char *str, int *pIndex) { char c = '\0'; /* no initialization - the caller should have done it */ /* searches a lowercase character */ while(c=buff[*pIndex]){ if(islower(c)) { (*pIndex)++; break; } (*pIndex)++; } return c; }
经验 3
在大部分系统中,malloc 和 free 都不是可重入的, 因为它们使用静态数据结构来记录哪些内存块是空闲的。实际上,任何分配或释放内存的库函数都是不可重入的。这也包括分配空间存储结果的函数。
避免在处理器分配内存的最好方法是,为信号处理器预先分配要使用的内存。避免在处理器中释放内存的最好方法是, 标记或记录将要释放的对象,让程序不间断地检查是否有等待被释放的内存。不过这必须要小心进行,因为将一个对象 添加到一个链并不是原子操作,如果它被另一个做同样动作的信号处理器打断,那么就会“丢失”一个对象。不过, 如果您知道当信号可能到达时,程序不可能使用处理器那个时刻所使用的流,那么就是安全的。如果程序使用的是某些其他流,那么也不会有任何问题。
经验 4
为了编写没有 bug 的代码,要特别小心处理进程范围内的全局变量,如 errno 和 h_errno。 考虑下面的代码:清单 7. errno 的危险用法 if (close(fd) < 0) { fprintf(stderr, "Error in close, errno: %d", errno); exit(1); }
假定信号在 close 系统调用设置 errno 变量 到其返回之前这一极小的时间片段内生成。这个生成的信号可能会改变 errno 的值,程序的行为会无法预计。
如下,在信号处理器内保存和恢复 errno 的值,可以解决这一问题:清单 8. 保存和恢复 errno 的值 void signalHandler(int signo) { int errno_saved; /* Save the error no. */ errno_saved = errno; /* Let the signal handler complete its job */ ... ... /* Restore the errno*/ errno = errno_saved; }
经验 5
如果底层的函数处于关键部分,并且生成并处理信号,那么这可能会导致函数不可重入。通过使用信号设置和 信号掩码,代码的关键区域可以被保护起来不受一组特定信号的影响,如下:
保存当前信号设置。
用不必要的信号屏蔽信号设置。
使代码的关键部分完成其工作。
最后,重置信号设置。
下面是此方法的概述:清单 9. 使用信号设置和信号掩码 sigset_t newmask, oldmask, zeromask; ... /* Register the signal handler */ signal(SIGALRM, sig_handler); /* Initialize the signal sets */ sigemtyset(&newmask); sigemtyset(&zeromask); /* Add the signal to the set */ sigaddset(&newmask, SIGALRM); /* Block SIGALRM and save current signal mask in set variable 'oldmask' */ sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* The protected code goes here ... ... */ /* Now allow all signals and pause */ sigsuspend(&zeromask); /* Resume to the original signal mask */ sigprocmask(SIG_SETMASK, &oldmask, NULL); /* Continue with other parts of the code */
忽略 sigsuspend(&zeromask); 可能会引发问题。从消除信号阻塞到进程执行下一个 指令之间,必然会有时钟周期间隙,任何在此时间窗口发生的信号都会丢掉。函数调用 sigsuspend 通过重置信号掩码并使进程休眠一个单一的原子操作来解决这一问题。如果您能确保在此时间窗口中生成的信号不会有任何 负面影响,那么您可以忽略 sigsuspend 并直接重新设置信号。
在编译器层次处理可重用性
我将提出一个在编译器层次处理可重入函数的模型。可以为高级语言引入一个新的关键字: reentrant,函数可以被指定一个 reentrant 标识符,以此确保函数可重入,比如:reentrant int foo();
此指示符告知编译器要专门处理那个特殊的函数。编译器可以将这个指示符存储在它的符号表中,并在中间代码生成阶段 使用这个指示符。为达到此目的,编译器的前端设计需要有一些改变。此可重入指示符遵循这些准则:
不为连续的调用持有静态数据。
通过制作全局数据的本地拷贝来保护全局数据。
绝对不调用不可重入的函数。
不返回对静态数据的引用,所有数据都由函数的调用者提供。
准则 1 可以通过类型检查得到保证,如果在函数中有任何静态存储声明,则抛出错误消息。这可以在编译的语法分析 阶段完成。
准则 2,全局数据的保护可以通过两种方式得到保证。基本的方法是,如果函数修改全局数据,则抛出一个错误 消息。一种更为复杂的技术是以全局数据不被破坏的方式生成中间代码。可以在编译器层实现类似于前面经验 4 的方法。 在进入函数时,编译器可以使用编译器生成的临时名称存储将要被操作的全局数据,然后在退出函数时恢复那些数据。 使用编译器生成的临时名称存储数据对编译器来说是常用的方法。
确保准则 3 得到满足,要求编译器预先知道所有可重入函数,包括应用程序所使用的程序库。这些关于函数的 附加信息可以存储在符号表中。
最后,准则 4 已经得到了准则 2 的保证。如果函数没有静态数据,那么也就不存在返回静态数据的引用的问题。
提出的这个模型将简化程序员遵循可重入函数准则的工作,而且使用此模型可以预防代码出现无意的可重入性 bug。
下面就我理解,简单描述可重入和不可重入的函数。
在多线程环境中,可重入函数与不可重入函数对线程安全至关重要。一个可重入的函数简单来说就是可以被中断的函数,也就是说可以在该函数的任意时刻中断它,通过系统调度去执行另一段代码或者本函数片段被重新执行,返回时执行原先的函数的数据不会被破坏或者改变。不可重入函数则相反,一旦被中断执行,返回执行时候数据可能会被破坏或者改变。
在Linux API(库函数 / 系统调用)中,重入与不可重入的函数随处可见,如:
时间类编程: char *asctime(const struct tm *tm); char *asctime_r(const struct tm *tm, char *buf); char *ctime(const time_t *timep); char *ctime_r(const time_t *timep, char *buf); struct tm *gmtime(const time_t *timep); struct tm *gmtime_r(const time_t *timep, struct tm *result); struct tm *localtime(const time_t *timep); struct tm *localtime_r(const time_t *timep, struct tm *result); 目录类编程: struct dirent *readdir(DIR *dirp); int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); 字符串类编程: char *strtok(char *str, const char *delim); char *strtok_r(char *str, const char *delim, char **saveptr);
只要函数名含”_r”,那么它就是可重入函数,与之对应的没有”_r”则是不可重入函数。
下面以一个简单的例子,表现重入与不可重入的函数关系,及其使用。代码功能: 可重入遍历数组 / 不可重入遍历数组。
#include <stdio.h> #include <stdlib.h> #define MAX 6 int i; //数组的索引值 //可重入,它并不依赖全局索引值,而是操作用aip调用者分配来的空间,确保函数是可以重入的 int traverse_arr_r(int *arr, int **save) { if (**save + 1 < MAX && **save >= 0) return arr[(**save)++]; return -1; } //不可重入,依赖全局索引值 int traverse_arr(int *arr) { if (i + 1 < MAX) return arr[i++]; return -1; } int main(void) { int a[5] = {1, 2, 3, 4, 5}; int num; //int i = 0; int *i = NULL; i = (int* )malloc(sizeof(int)); //调用不可重入的函数,还需要注意遍历完一次后要让全局索引归零 //while ((num = traverse_arr(a)) != -1) //{ // printf("num = %d\n", num); //} //调用可重入的函数 while ((num = traverse_arr_r(a, &i)) != -1) { printf("num = %d\n", num); } free(i); return 0; }
-
Excel如何设置四舍五入 四舍五入的设置方法
2021-08-01 03:17:20我们在用excel办公软件制作报表的时候,可能需要对表格中的数据按照指定的要求进行四舍五入,保留指定的小数位数。excel四舍五入怎么设置呢?当然,如果需要处理的数据不多,你可以人工进行四舍五入计算,如果需要... -
float浮点数会自动四舍五入吗 大部分人都想错了
2021-05-19 18:19:23前几天,有个小伙伴在做实验过程中,发现了一个奇怪的现象,这个现象就是…他在用printf输出浮点数的时候,想把数据保留到小数点后的两位,他是这么写的…float c=1.155;printf(“%.2f”,c);他的书写是对的,没有... -
[4G&5G专题-41]:物理层-物理随机接入信道PRACH与随机接入过程
2021-02-17 21:28:37在任何情况下,如终端需要同网络建立通信,都需通过RACH(随机接入信道)向网络发送一个报文来向系统申请一条信令信道,网络将根据信道请求需要来决定所分配的信道类型。 这个在RACH 上发送的报文被称做“信... -
设备接入服务,看完这篇给你整的明明白白
2022-02-08 10:08:16摘要:设备接入服务能够帮助物联网行业用户快速完成设备联网及行业应用集成。 -
NR随机接入(一)
2020-08-03 14:04:49UE完成下行同步后(SSB),还要完成随机接入,才可以和网络进一步交互。 为什么要随机接入? ①请求上行ul grant ②上行同步 简单的说,是为了请求接入资源。随机接入是UE向网络发的第一条消息。 NR中“称为RACH” ... -
高等数学 函数极限求法(一) 代入法
2018-01-29 01:24:08高数 函数极限求法(一) 代入法 极限是什么? 极限就像你坐着宇宙飞船去探索宇宙的边界一样,无限接近 却 没法到达! 大学高数中极限部分可以简单分为两部分:函数极限 和 数列极限 ; 本篇... -
5G中非正交多址接入技术(NOMA)是什么?
2020-09-09 11:15:355G中非正交多址接入技术(NOMA)是什么?NOMA的简要介绍?NOMA的原理?NOMA的分类?NOMA中的关键技术?参考?二级目录三级目录 NOMA的简要介绍? 在正交多址技术中,只能为一个用户分配单一的无线资源,例如按频率... -
揭秘华为数据湖:3大特点、6个标准、入湖流程
2020-12-21 09:26:18数据入湖要遵从6项入湖标准,基于6项标准保证入湖的质量,同时面向不同的消费场景提供两种入湖方式,满足数据消费的要求。 ▲图5-2 数据湖总体视图 经过近两年的数据湖建设,目前已经完成1.2万个逻辑数据实体... -
matlab将数值代入函数
2021-05-06 02:57:49Matlab输入输出格式及多项式函数 Matlab输入及输出格式在运算式中常需要做数据的输入及输出,采用 的方式可以是交谈式的或是指定格式。 ?输入及输出 ? 交谈式的输入 ? 输出格式 交谈式的输入 我们来看一个例子,计算... -
ORAN专题系列-1:什么是开放无线接入网O-RAN
2020-06-10 07:13:50这篇文章将回答如下几个问题:什么是无线接入网RAN? 什么是开放无线接入网ORAN? ORAN与5G的关系? ORAN提出的动机?ORAN的参与方?以及ORAN的技术目标?ORAN标准的组织架构? -
【jQuery】如何用jQuery实现鼠标移入后改变背景颜色
2020-08-06 09:37:36实现效果如图: jQuery代码实现: $(function () { var oldColor = ""; $(".mouse-in-out").mouseover(function () { originalColor = $(this).css("background-... 注:mouse-in-out 为需要更换背景颜色的区域的class -
5G NR随机接入过程
2019-05-19 11:26:331.5G NR 随机接入过程的目的 1.获得上行同步 2.获得UL grant ,申请上行资源 2.5G NR随机接入过程的种类 ...于是UE需要发送一条与自己相关的独特的消息3给基站,基站此时就能分清哪个UE发送的。 非竞争的随... -
网络接入与身份认证简介
2019-03-03 10:07:13EAP过程由认证方发起,而认证协议一般都是由客户端发起,为了在特定的认证协议上支持EAP过程,认证协议需要增加一个或多个的附加报文来满足条件。如以太网上的认证通常情况下是以客户端发送EAPOL报文开始的。 EAP... -
C语言之可重入函数 和不可重入函数
2017-09-06 21:51:32可重入函数 在 实时系统的设计中,...那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会 出错。 不可重入函数在实时系统设计中被视为不安全函数。 满足... -
Internet的接入与IP地址 概述
2020-03-20 15:33:23计算机接入Internet1.公用交换电话网(PSTN)接入2.有线电视(CATV)接入3.局域网接入4.无线接入二.IP地址1.IP地址的格式2.IP地址分类3.子网掩码4.域名系统三.Windows网络查看与配置1.win10查看所有的网络连接状况2.... -
修改apn加快电信4g网速(电信最佳apn接入点)
2021-08-05 07:07:38您好 电信4G网络的带宽可以达到150M,一般使用电信4G网络时接入点可以选择ctlet,详情请登陆电信网上营业厅查看。您好!您先进入手机的接入点设置--新建apn接入点--名称乱填,apn:cmtds--保存 保存之后,选择自己... -
Python四舍五入与保留小数位数(精确舍入与保留)
2020-03-22 16:53:28本文深入讲解Python3中 四舍五入、... 四舍五入(精确)1.1 四舍五入并保留x位小数——用decimal模块中的Decimal方法(精度高)1.2 四舍五入后取整2. 非精确的舍入2.1 print("%.xf" % a)形式2.2 format()形式2.3 round... -
阿里云接入备案
2018-09-30 09:48:45以下全部内容来源于:阿里云官网,复制下来只是为了方便自己查看; 主体和域名均已通过其他服务商成功备案。 接入备案分为以下两类: ... 接入成功后,不会影响您上家服务商处的备案数据,您可同时使用两家服务... -
校园网络构建核心层,汇聚层,接入层,各层级交换机一般使用什么型号
2021-02-05 07:23:27楼道交换机,也就是你说的接入层。这一层次的交换机你只需要考虑端口密度,其他都不重要。一台48口不够,那就2台、3台。当然经费到位的话还可以做主备冗余,或者机框式交换机,直接插3块48口板就ok了。汇聚层则只... -
可能是史上最详细攻略的广州人才引进入户
2020-06-03 16:26:24广州人才引进入户(以职称入户为例) 申报条件 满足以下条件之一: (一)经我市认定或审核确认的高层次、高技能人才; (二)具有博士研究生学历,或具有博士学位,或具有高级职称的人员,年龄需在50周岁以下; (三... -
摊牌了我后悔入行了,浅谈为何不该入行嵌入式
2022-05-22 14:31:32摊牌了我后悔入行了,浅谈为何不该入行嵌入式\\\插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的(禾厶-亻言-手戈)。最近比较闲,带做毕设,带学生参加省级以上... -
5G/NR 随机接入过程学习总结
2019-05-21 23:14:42对于随机接入过程,NR与LTE之间有相同点,也有不同点,其最大的区别在于触发场景已经Msg1的处理,详情见... 2) RRC连接重建:以便UE在无线链路失败后重新建立无线连接(期间重建小区可能是UE无线链路失败的小区,也... -
线程安全与可重入函数的区别与联系
2018-05-20 11:56:44线程安全 线程安全 是多个线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,...(4)线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作互不影响结果是相同的。 -
华为内部资料流出!揭秘华为数据湖:3大特点、6个标准、入湖流程
2020-12-03 21:15:00▼表5-4 非结构化数据的基本特征类属性 2)文件解析内容入湖 对数据源的文件内容进行文本解析、拆分后入湖。入湖的过程中,原始文件仍存储在源系统,数据湖中仅存储解析后的内容增强元数据。内容解析入湖需同时满足... -
5G/NR 随机接入过程之事件触发场景
2019-05-07 23:01:06RRC连接重建:以便UE在无线链路失败后重新建立无线连接(期间重建小区可能是UE无线链路失败的小区,也可能不是); 切换:UE处于RRC_CONNETED态,此时UE需要新的小区建立上行同步; RRC_CONNETTED态下,上行或下行... -
微信硬件平台的基础接入和硬件云标准接入分析
2016-05-27 12:29:21微信硬件平台的基础接入和硬件云标准接入分析