APUE 第14章 高级I/O

第14章 高级I/O

非阻塞IO

我们可以发出open,read,write这样的IO操作,并使它们永远不会阻塞,如果无法做到,则立即返回出错。

两种方法获得非阻塞IO:
1. open打开使指定O_NONBLOCK;
2. 对于一个已经打开的描述符,调用fcntl增加上述标志位。

记录锁

记录锁(record locking)的名称是一种误用,因为UNIX系统内核根本没有使用文件记录这种概念。更适合的术语是字节范围锁(byte-range locking),因为它锁定的只是文件的一个区域。

这种功能用于支持对数据库的维护。

int fcntl(int fd, int cmd, struct flock *lock);

struct flock {
    short l_type;/*F_RDLCK, F_WRLCK, or F_UNLCK*/
    off_t l_start;/*相对于l_whence的偏移值,字节为单位*/
    short l_whence;/*从哪里开始:SEEK_SET, SEEK_CUR, or SEEK_END*/
    off_t l_len;/*长度, 字节为单位; 0 意味着缩到文件结尾*/
    pid_t l_pid;/*returned with F_GETLK*/
};
结构体描述:
  • l_type:锁类型: F_RDLCK(读共享锁), F_WRLCK(写互斥锁),和F_UNLCK(对一个区域解锁)
  • l_start:锁开始:相对于l_whence要锁或者解锁的区域开始位置
  • l_whence:锁位置(l_whence)
  • l_len:锁长度: 要锁的长度,字节计数
    -l_pid:锁拥有者:记录锁的拥有进程ID,这个进程可以阻塞当前进程,仅F_GETLK形式返回
对于锁区域要注意的几点:
  1. 锁可以开始或者超过文件当前结束位置,但是不可以开始或者超过文件的开始位置
  2. 如果l_len为0,意味着锁的区域为可以到达的最大文件偏移位置。这个类型,可以让我们锁住一个文件的任意开始位置,结束的区域可以到达任意的文件结尾,并且以append方式追加文件时,也会同样上锁。
  3. 如果要锁住整个文件,设置l_start 和 l_whence为文件的开始位置(l_start为0 l_whence 为 SEEK_SET ),并且l_len为0。
  4. 如果有多个读共享锁(l_type of F_RDLCK),其他的读共享锁可以接受,但是写互斥锁(type ofF_WRLCK)拒绝
  5. 如果有一个写互斥锁(type ofF_WRLCK),其他的读共享锁(l_type of F_RDLCK)拒绝,其他的写互斥锁拒绝。
  6. 如果要取得读锁,这个文件描述符必须被打开可以去读;如果要或者写锁,这个文件的描述符必须可以被打开可以去写。

锁的兼容性规则只对不同进程有效,同一进程对自己已加锁的区间再加锁,则会替换旧锁。

fcntl的cmd参数
  • F_GETLK:判断flockptr描述的锁是否被其他的锁阻塞。
  • F_SETLK :设置flockptr描述的锁,如果兼容性规则阻止,则出错返回。
  • F_SETLKW:对应着F_GETLK的可以阻塞的版本。w意味着wait

系统可以按要求组合或分裂相邻区。

锁的隐含继承和释放
  1. 锁与进程和文件两方面有关:
    a、当一个进程终止时,它所建立的锁全部释放;(即 进程退出,文件锁自动释放)
    b、任何时候关闭一个描述符时,则该进程通过这一”描述符可以引用的文件”上的任何一把锁都释放。(即 关闭文件,文件锁自动释放),该进程设置的该文件相关所有锁都释放,
  2. 由fork产生的子进程不继承父进程所设置的锁。(文件锁不能被继承):
    这意味着,若一个进程得到一把锁,然后调用fork,那么对于父进程获得的锁而言,子进程被视为另外一个进程,对于从父进程处继承过来的任一描述符,子进程需要调用fcntl才能获得它自己的锁。
  3. 在执行exec后,新程序可以继承原执行程序的锁。(EXEC文件锁被继承),执行exec后,其实是用当前进程的进程实体替换原进程的进程实体。
    如果对一个文件描述符设置了执行时关闭,当作为exec的一部分关闭该文件描述符时,将释放相应文件的所有锁。
建议性锁和强制性锁
  • 建议锁又称协同锁。对于这种类型的锁,内核只是提供加减锁以及检测是否加锁的操作,但是不提供锁的控制与协调工作。也就是说,如果应用程序对某个文件进行操作时,没有检测是否加锁或者无视加锁而直接向文件写入数据,内核是不会加以阻拦控制的。因此,建议锁,不能阻止进程对文件的操作,而只能依赖于大家自觉的去检测是否加锁然后约束自己的行为;多数 Unix 和类 Unix 操 作系统使用建议型锁,有些也使用强制型锁或兼而有之。
  • 强制锁,是内核的文件锁。每个对文件操作时,例如执行open、read、write等操作时,OS内部检测该文件是否被加了强制锁,如果加锁导致这些文件操作失败。也就是内核强制应用程序来遵守游戏规则;微软的操作系统往往使用的是强制型锁。
int select( int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);
/*
maxfdp1:是一个整数值,是指集合中所有文件描述符的范围,即3个集合中所有文件描述符的最大值加1,不能错!
readfds:(可选)指针,指向一组等待可读性检查的描述符。
writefds:(可选)指针,指向一组等待可写性检查的描述符。
exceptfds:(可选)指针,指向一组等待错误检查的描述符。
timeout:select()最多等待时间,设为NULL则阻塞,设为0则不阻塞,设为正数则等待指定时间。
*/
readfds, writefds, exceptfds可以实现为位图。利用一组函数来设置、清除、测试指定fd。 返回时,-1表示出错,一般是因为接收到某信号;0表示没有描述符准备好,此时3个集合都置0;一个正数表示准备好的3个集合的数量之和,此时3个集合将准备好的位置1。注意,如果同一描述符出现在多个集合中,则累加计数。

一个描述符碰到文件尾端,它仍是可读的。
一个描述符是否阻塞与select是否阻塞没有关系

pselect变体的区别,使用timespec结构可以实现更精准的超时时间(它以秒和纳秒表示,而timeval以秒和微秒表示);它的超时时间是const,所以不能改变时间值;它还可以使用信号屏蔽字。 poll提供的功能与select类似:
int poll(struct pollfd fd[], nfds_t nfds, int timeout);
    参数:
  1. 第一个参数:一个结构数组,struct pollfd结构如下:
struct pollfd{
  int fd;              //文件描述符
 short events;    //请求的事件
 short revents;   //返回的事件
};

events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件,poll用已经发生的事件填充revents。poll函数通过在revents中设置标志肌肤POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。如果fd小于0, 则events字段被忽略,而revents被置为0.标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。
2. 第二个参数nfds:要监视的描述符的数目。
3. 最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果 它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。

然而,select和poll都不是线程安全的,显而易见。

传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是“活跃”的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对“活跃”的socket进行操作—这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有“活跃”的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个“伪”AIO,因为这时候推动力在os内核。

用readv,writev进行散布读和聚集写。

管道、FIFO以及某些设备(特别是终端和网络)有以下两种性质:
1. 一次read操作返回的数据少于要求的数据
2. 一次write操作的返回值少于指定输出的值
利用readn, writen将按需地多次调用read,write直至完成。

存储映射I/O是一种基于内存区域的高级I/O操作,它将磁盘文件与进程地址空间中的一个内存区域相映射。当从这段内存中读数据时,就相当于读磁盘文件中的数据,将数据写入这段内存时,则相当于将数据直接写入磁盘文件。这样就可以在不使用基本I/O操作函数read和write的情况下执行I/O操作。

实现存储映射I/O的核心操作是通过mmap系统调用将一个给定的磁盘文件映射到一个存储区域中

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

prot用来指定对映射区域的保护要求,但是它的保护范围不能超过文件open时指定的打开权限。比如以只读(PROT_READ)方式打开一个文件,那么以读写(PROT_READ|PROT_WRITE)方式保护内存区域是不合法的。flags用来指定内存区域的多种属性,两个典型的取值是MAP_SHARED和MAP_PRIVATE。MAP_SHARED标志指定了进程对内存区域的修改会影响到映射文件,且多个共享该映射区的进程都可以看见。而当对flags指定MAP_PRIVATE时,进程会为该映射内存区域创建一个私有副本,对该内存区的所有操作都是在这个副本上进行的,此时对内存区域的修改并不会影响到映射文件。

off的值和addr的值通常被要求是系统虚拟存储页长度的倍数,将两者都指定为0可以省去这一麻烦而交由系统处理。

mprotect()用于更改一个现有映射的权限,msync()用于将页冲洗到被映射的文件中,可以选择同步或异步方式,munmap()用于解除一个映射,但它并不能影响被映射的对象。

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值