UNIX环境高级编程——高级I/O

news2024/11/16 23:48:22

14.1 引言

本章讨论高级I/O相关主题,包括:

  • 非阻塞I/O;
  • 记录锁;
  • I/O多路转接(selectpoll);
  • 异步I/O;
  • readvwritev函数;
  • 存储映射I/O(mmap)。

14.2 非阻塞I/O

非阻塞I/O使我们可以发出openreadwrite这样的I/O操作,并使这些操作不会永久阻塞。如果这种操作不能完成,则调用立即出错返回,表示该操作如继续执行将阻塞。

对于一个给定的描述符,有两种为其指定非阻塞I/O的方法:
(1)如果调用open获得描述符,则可指定O_NONBLOCK标志;
(2)对于已经打开的一个描述符,则可调用fcntl,由该函数打开O_NONBLOCK文件状态标志。

14.3 记录锁

记录锁的功能是:当第一个进程正在读或修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一文件区。

1. 历史

2. fcntl记录锁

#include <fcntl.h>

int fcntl(int fd, int cmd, .../* struct flock *flockptr */);
										// 返回值:若成功,依赖于cmd(见下),否则,返回-1
  • cmdF_GETLKF_SETLKF_SETLFW

  • 第三个参数 flockptr 是一个指向flock结构的指针:

    struct flock {
    	short	l_type;		/* F_RDLCK, F_WRLCK, or F_UNLCK */
    	short 	l_whence;	/* SEEK_SET, SEEK_CUR, or SEEK_END */
    	off_t 	l_start;	/* offset in bytes, relative to l_whence */
    	off_t 	l_len;		/* length, in bytes; 0 means lock to EOF */
    	pid_t 	l_pid;		/* returned with F_GETLK */
    };
    
    • 所希望的锁类型:l_type可取 F_RDLCK(共享读锁)、F_WRLCK(独占性写锁)或 F_UNLCK(解锁一个区域);
    • 要加锁或解锁区域的起始字节偏移量:l_startl_whence
    • 区域的字节长度:l_len
    • 进程的ID(l_pid)持有的锁能阻塞当前进程(仅由F_GETLK返回)。

共享读锁(F_RDLCK)和独占性写锁(F_WRLCK)的基本规则:任意多个进程在一个给定的字节上可以有一把共享的读锁,但是在一个给定的字节上只能有一个进程有一把独占写锁;如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁;如果在一个字节上已经有一把独占性写锁,则不能再对它加任何读锁。

  • fcntl函数的3种命令:
    • F_GETLK:判断由flockptr所描述的锁是否会被另外一把锁所排斥(阻塞)。如果存在一把锁,它阻止创建由flockptr所描述的锁,则该现有锁的信息将重写flockptr指向的信息。如果不存在这种情况,则除了将l_type设置为设置为F_UNLCK之外,flockptr所指向结构中的其他信息保持不变;
    • F_SETLK:设置由flockptr所描述的锁;此命令也用来清除由flockptr指定的锁(l_typeF_UNLCK);
    • F_SETLKW:这个命令是 F_SETLK 的阻塞版本(命令名中的 W 表示等待(wait))。如果所请求的读锁或写锁因另一个进程当前已经对所请求区域的某部分进行了加锁而不能被授予,那么调用进程会被置为休眠。如果请求的锁已经可用,或者休眠由信号中断,则该进程被唤醒。

3. 锁的隐含继承和释放

关于记录锁的自动继承和释放有3条规则:

  • 锁与进程和文件两者相关联:
    • 当一个进程终止时,它所建立的锁全部释放;
    • 无论一个描述符何时关闭,该进程通过这一描述符引用的文件上的任何一把锁都会释放(这些锁都是该进程设置的)。
  • fork产生的子进程不继承父进程锁设置的锁;
  • 在执行exec后,新程序可以继承原执行程序的锁。

4. FreeBSD实现

5. 在文件尾端加锁

6. 建议性锁和强制性锁

14.4 I/O多路转接

I/O多路转接:先构造一张感兴趣的描述符(不止一个)的列表,然后调用一个函数(pollpselectselect),直到这些描述符中的一个已准备好进行I/O时,该函数才返回。

14.4.1 函数select和pselect

#include <sys/select.h>

int select(int maxfdp1, fd_set *restrict readfds,
		   fd_set *restrict writefds, fd_set *restrict exceptfds,
		   struct timeval *restrict tvptr);
										// 返回值:准备就绪的描述符数目;若超时,返回0;若出错,返回-1
  • tvptr 指定愿意等待的时间长度:

    • tvptr == NULL:永远等待,如果捕捉到一个信号则中断此无限期等待;
    • tvptr->tv_sec == 0 && tvptr->tv_usec == 0:根本不等待,测试所有指定的描述符并立即返回;
    • tvptr->tv_sec != 0 && tvptr->tv_usec != 0:等待指定的秒数和微秒数,当指定的描述符之一已准备好,或当指定的时间值已经超过时立即返回。
  • readfdswritefdsexceptfds是指向描述符集的指针, 这3个描述符集说明了我们关心的可读可写或处于异常条件的描述符集合;每个描述符集存储在一个fd_set数据类型中,对这种类型的常用操作函数为:

    #include <sys/select.h>
    
    int FD_ISSET(int fd, fd_set *fdset);
    										// 返回值:若fd在描述符集中,返回非0值;否则,返回0
    void FD_CLR(int fd, fd_set *fdset);
    void FD_SET(int fd, fd_set *fdset);
    void FD_ZERO(fd_set *fdset);					
    
    • FD_ZERO将一个fd_set变量的所有位设置为0
    • FD_SET开启描述符集中的一个指定位;
    • FD_CLR清除描述符集中的一个指定位;
    • FD_ISSET测试描述符集中一个指定位是否已打开;
    • 在声明了一个描述符集之后,必须用FD_ZERO将这个描述符集置0,然后在其中设置我们关心的各个描述符的位。
  • 第一个参数maxfdp1的意思是“最大文件描述符编号值加1”,考虑所有3个描述符集,在这3个描述符集中找出最大描述符编号值,然后加1,这就是第一个参数值;也可将第一个参数设置为FD_SETSIZE,该常量指定最大描述符数(经常是1024);因为描述符编号从0开始,所以要在最大描述符编号值上加1,该参数实际上是要检查的描述符数(从描述符0开始);

pselect是POSIX.1中定义的select函数变体:

#include <sys/select.h>

int pselect(int maxfdp1, fd_set *restrict readfds,
			fd_set *restrict writefds, fd_set *restrict exceptfds,
			const struct timespec *restrict tsptr,
			const sigset_t *restrict sigmask);
										// 返回值:准备就绪的描述符数目;若超时,返回0;若出错,返回-1

除下列几点外,pselectselect相同:

  • pselect的超时值使用timespec结构指定,以秒和纳秒表示超时值,且被声明成const,这保证了调用pselect不会改变此值;
  • pselect可使用可选信号屏蔽字;若sigmaskNULL,那么在与信号有关的方面,pselect的运行状态和select相同;否则,sigmask指向一信号屏蔽字,在调用pselect时,以原子操作的方式安装该信号屏蔽字;在返回时,恢复以前的信号屏蔽字。

14.4.2 函数poll

#include <poll.h>

int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
										// 返回值:准备就绪的描述符数目;若超时,返回0;若出错,返回-1
  • select不同,poll不是为每个条件构造一个描述符集,而是构造一个pollfd结构的数组,每个数组元素指定一个描述符编号以及我们对该描述符感兴趣的条件:

    struct pollfd {
    	int		fd;			/* file descriptor to check, or < 0 to ignore */
    	short	events;		/* events of interest on fd */
    	short	revents;	/* events that occurred on fd */
    };
    
  • fdarray数组中的元素数由nfds指定;

  • 应将每个数组元素的events成员设置成下图中所示值的一个或几个,通过这些值告诉内核我们关心的是每个描述符的哪些事件;返回时,revents成员由内核设置,用于说明每个描述符发生了哪些事件:
    在这里插入图片描述

    • 当一个描述符被挂断(POLLHUP)后,就不能再写该描述符,但是有可能仍然可以从该描述符读取到数据;
  • timeout参数指定愿意等待多长时间:

    • timeout == -1:永远等待,当所指定的描述符中的一个已准备好,或捕捉到一个信号时返回;
    • timeout == 0:不等待,测试所有描述符并立即返回;
    • timeout > 0:等待timeout毫秒,当指定的描述符之一已准备好,或timeout到期时立即返回。

14.5 异步I/O

14.5.1 System V 异步I/O

System V的异步I/O信号是SIGPOLL

14.5.2 BSD 异步I/O

在BSD派生的系统中,异步I/O是信号SIGIOSIGURG的组合。SIGIO是通用异步I/O信号,SIGURG则只用来通知进程网络连接上的带外数据已经到达。

14.5.3 POSIX 异步I/O

POSIX异步I/O接口使用AIO控制块来描述I/O操作。aiocb结构定义了AIO控制块。该结构至少包括下面这些字段(具体的实现可能还包含有额外的字段):

struct aiocb {
	int					aio_fildes;			/* file descriptor */
	off_t				aio_offset;			/* file offset for I/O */
	volatile	void	*aio_buf;			/* buffer for I/O */
	size_t				aio_nbytes;			/* number of bytes to transfer */
	int					aio_reqprio;		/* priority */
	struct	sigevent	aio_sigevent;		/* signal information */
	int					aio_lio_opcode;		/* operation for list I/O */
};
  • 异步I/O操作必须显式地指定偏移量。如果使用异步I/O接口向一个以追加模式(使用O_APPEND)打开的文件中写入数据,AIO控制块中的aio_offset字段会被系统忽略;

  • aio_sigevent字段控制在I/O事件完成后,如何通知应用程序:

    struct sigevent {
    	int				sigev_notify;					/* notify type */
    	int				sigev_signo;					/* signal number */
    	union sigval	sigev_value;					/* notify argument */
    	void (*sigev_notify_function)(union sigval);	/* notify function */
    	pthread_attr_t	*sigev_notify_attributes;		/* notify attrs */
    };
    
    • sigev_notify字段控制通知的类型,取值可能是以下3个中的一个:
      • SIGEV_NONE:异步I/O请求完成后,不通知进程;
      • SIGEV_SIGNAL:异步I/O请求完成后,产生由sigev_signo字段指定的信号;
      • SIGEV_THREAD:当异步I/O请求完成时,由sigev_notify_function字段指定的函数被调用。sigev_value字段被传入作为它的唯一参数。

在进行异步I/O之前需要先初始化AIO控制块,调用aio_read函数来进行异步读操作,或调用aio_write函数来进行异步写操作:

#include <aio.h>

int aio_read(struct aiocb *aiocb);
int aio_write(struct aiocb *aiocb);
										// 两个函数的返回值:若成功,返回0;若出错,返回-1

要想强制所有等待中的异步操作不等待而写入持久化的存储中,可以设立一个AIO控制块并调用aio_fsync函数:

#include <aio.h>

int aio_fsync(int op, struct aiocb *aiocb);
										// 返回值:若成功,返回0;若出错,返回-1
  • AIO控制块中的aio_fildes字段指定了其异步写操作被同步的文件;
  • 如果op参数设定为O_DSYNC,那么操作执行起来就会像调用了fdatasync一样;
  • 如果op参数设定为O_SYNC,那么操作执行起来就会像调用了fsync一样;

为了获知一个异步读、写或者同步操作的完成状态,需要调用aio_error函数:

#include <aio.h>

int aio_error(const struct aiocb *aiocb);
										// 返回值:(见下)

aio_error返回值为下面4种情况中的一种:

  • 0:异步操作成功完成,需要调用aio_return函数获取操作返回值;
  • -1:对aio_error的调用失败,这种情况下,errno会告诉我们为什么;
  • EINPROGRESS:异步读、写或同步操作仍在等待;
  • 其他情况:其他任何返回值是相关的异步操作失败返回的错误码。

如果异步操作成功,可以调用aio_return函数来获取异步操作的返回值:

#include <aio.h>

ssize_t aio_return(const struct aiocb *aiocb);
										// 返回值:(见下)

aio_return返回值:

  • aio_return函数本身失败,会返回-1,并设置errno;
  • 其他情况下,它将返回异步操作的结果,即会返回readwrite或者fsync在被成功调用时可能返回的结果。

注意:

  • 直到异步操作完成之前,都需要小心不要调用aio_return函数,操作完成之前的结果是未定义的;
  • 对每个异步操作只调用一次aio_return
  • 一旦调用了aio_return,操作系统就可以释放掉包含了I/O操作返回值的记录。

执行I/O操作时,如果还有其他事务要处理而不想被I/O操作阻塞,就可以使用异步I/O。然而,如果在完成了所有事务时,还有异步操作未完成时,可以调用aio_suspend函数来阻塞进程,直到操作完成:

#include <aio.h>

int aio_suspend(const struct aiocb *const list[], int nent,
				const struct timespec *timeout);
										// 返回值:若成功,返回0;若出错,返回-1
  • list参数是一个指向AIO控制块数组的指针,数组中的空指针会被跳过,其他条目都必须指向已用于初始化异步I/O操作的AIO控制块;
  • nent参数表明了数组中的条目数。

aio_suspend的可能返回值:

  • 如果被一个信号中断,它将返回-1,并将errno设置为EINTR
  • 如果在没有任何I/O操作完成的情况下,阻塞的时间超过了函数中可选的timeout参数所指定的时间限制,那么将返回-1,并将errno设置为EAGAIN(不想设置任何时间限制的话,可以把空指针传给timeout参数);
  • 如果有任何I/O操作完成,将返回0;如果在调用aio_suspend操作时,所有的异步I/O操作都已完成,那么aio_suspend将在不阻塞的情况下直接返回。

当还有不想再完成的等待中的异步I/O操作时,可以尝试使用aio_cancel函数来取消它:

#include <aio.h>

int aio_cancel(int fd, struct aiocb *aiocb);
										// 返回值:(见下)
  • fd参数指定了那个未完成的异步I/O操作的文件描述符;
  • 如果aiocb参数为NULL,系统将会尝试取消所有该文件上未完成的异步I/O操作;其他情况下,系统将尝试取消由AIO控制块描述的单个异步I/O操作;“尝试”的含义是无法保证系统能够取消正在进程中的任何操作。
  • aio_cancel函数的可能返回值:
    • AIO_ALLDONE:所有操作在尝试取消它们之前已经完成;
    • AIO_CANCELED:所有要求的操作已被取消;
    • AIO_NOTCANCELED:至少有一个要求的操作没有被取消;
    • -1:调用失败,错误码被存储在errno中。

lio_listio函数提交一系列由一个AIO控制块列表描述的I/O请求:

#include <aio.h>

int lio_listio(int mode, struct aiocb *restrict const list[restrict],
			   int nent, struct sigevent *restrict sigev);
										// 返回值:若成功,返回0;若出错,返回-1
  • mode参数决定了I/O是否真的是异步的;
    • 如果该参数被设定为LIO_WAITlio_listio函数将在所有由列表指定的I/O操作完成后返回,在这种情况下,sigev参数将被忽略;
    • 如果该参数被设定为LIO_NOWAITlio_listio函数将在I/O请求入队后立即返回;
  • 进程将在所有I/O操作完成后,按照sigev参数指定的,被异步地通知,如果不想被通知,可以把sigev设定为NULL
  • list参数指向AIO控制块列表,该列表指定了要运行的I/O操作,列表中可以包含NULL指针,这些条目将被忽略;
  • nent参数指定了数组中的元素个数。

在每一个AIO控制块中,aio_lio_opcode字段指定该操作的类型:

  • LIO_READ:读操作,按照对应的AIO控制块被传给aio_read函数来处理;
  • LIO_WRITE:写操作,按照对应的AIO控制块被传给aio_write函数来处理;
  • LIO_NOP:空操作。

14.6 函数readv和writev

readvwritev函数用于在一次函数调用中读、写多个非连续缓冲区,也称这两个函数为散布读(scatter read)和聚集写(gather write):

#include <sys/uio.h>

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
										// 两个函数的返回值:已读或已写的字节数;若出错,返回-1
  • 这两个函数的第二个参数是指向iovec结构数组的一个指针:

    struct iovec {
    	void 	*iov_base;	/* starting address of buffer */
    	ssize_t	iov_len;	/* size of buffer */
    };
    
  • iov数组中的元素数由iovcnt指定,其最大值受限于IOV_MAX,下图显式了这两个函数的参数和iovec结构之间的关系:
    在这里插入图片描述

  • writev函数从缓冲区中聚集输出数据的顺序是:iov[0]、iov[1]直至iov[iovcnt-1];writev返回输出的字节总数,通常应等于所有缓冲区长度之和;

  • readv函数将读入的数据按iov[0]、iov[1]直至iov[iovcnt-1]的顺序散布到缓冲区中;readv返回读到的总字节数,如果遇到文件尾端,已无数据可读,则返回0。

14.7 函数readn和writen

14.8 存储映射I/O

存储映射I/O(memory-mapped I/O)能将一个磁盘文件映射到存储空间中的一个缓冲区上,于是,当从缓冲区中取数据时,就相当于读文件中的相应字节,将数据存入缓冲区时,相应字节就自动写入文件。这样,就可以在不使用readwrite的情况下执行I/O。

mmap函数告诉内核将一个给定的文件映射到一个存储区域中:

#include <sys/mman.h>

void *mmap(void *addr, size_t len, int prot, int flag, int fd, off_t off);
										// 返回值:若成功,返回映射区的起始地址;若出错,返回 MAP_FAILED
  • addr参数用于指定映射存储区的起始地址,通常将其设置为0,表示由系统选择该映射区的起始地址;
  • fd参数是指定要被映射文件的描述符;
  • len参数是映射的字节数;
  • off是要映射字节在文件中的起始偏移量;
  • prot参数指定了映射存储区的保护要求,如下图:
    在这里插入图片描述
  • flag参数影响映射存储区的多种属性:
    • MAP_FIXED:返回值必须等于addr,不鼓励使用该标志;
    • MAP_SHARED:这一标志描述了本进程对映射区所进行的存储操作配置。此标志指定存储操作修改映射文件,也就是,存储操作相当于对该文件的write
    • MAP_PRIVATE:此标志说明对映射区的存储操作导致创建该映射文件的一个私有副本。所有后来对该映射区的引用都是引用该副本。
      在这里插入图片描述

调用mprotect可以更改一个现有映射的权限:

#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);
										// 返回值:若成功,返回0;若出错,返回-1
  • prot的合法值与mmap中的prot参数一样;
  • 地址参数addr的值必须是系统页长的整数倍。

如果共享映射中的页已修改,可以调用msync将该页冲洗到被映射的文件中:

#include <sys/mman.h>

int msync(void *addr, size_t len, int flags);
										// 返回值:若成功,返回0;若出错,返回-1
  • 如果映射是私有的,那么不修改被映射的文件;
  • addr地址必须与页边界对齐;
  • flags参数指定对如何冲洗存储区有某种程度的控制,一定要指定MS_ASYNCMS_SYNC中的一个:
    • MS_ASYNC:简单调试要写的页;
    • MS_SYNC:在返回之前等待写操作完成;
    • MS_INVALIDATE:可选,通知操作系统丢弃那些与底层存储器没有同步的页。

当进程终止时,会自动解除存储映射区的映射,或者直接调用munmap函数也可以解除映射区:

#include <sys/mman.h>

int munmap(void *addr, size_t len);
										// 返回值:若成功,返回0;若出错,返回-1
  • 关闭映射存储区时使用的文件描述符并不解除映射区;
  • munmap并不影响被映射的对象,调用munmap并不会使映射区的内容写到磁盘文件上。

实例代码

chapter14

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/605066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hesai激光雷达使用PTP时间同步

PTP是一个网络同步时钟的协议&#xff0c;全称是Precise Time Protocol&#xff0c;也称为IEEE 1588&#xff0c;其有IEEE1588v1和IEEE1588v2两个版本&#xff08;其中v1版本是为工业自动化测量和控制系统开发的&#xff0c;适用于工业局域网应用。v2版本是在v1版本的基础上&am…

Win11电脑突然没有声音了怎么办?

Win11电脑突然没有声音了怎么办&#xff1f;有用户电脑连接了音响之后&#xff0c;无论自己怎么调整都没有声音&#xff0c;那么遇到这个问题之后&#xff0c;要怎么去进行修复呢&#xff1f;如果你也遇到了没有电脑没有声音的情况&#xff0c;可以通过以下的方法来进行解决。 …

Vue (7)

文章目录 1. 单文件组件1.1 创建 .vue 文件1.2 vue 脚手架1.2.1 说明1.2.2 创建 vue 脚手架1.2.3 了解 vue_test 中的文件 1.2.4 render 函数 1.3 修改默认配置1.4 ref 属性1.5 props 配置项1.6 mixin 混入1.7 插件1.8 Scoped 样式 2. 组件化编码流程2.1 TodoList 案例总结 前言…

Pycharm设置Python每个文件开头自定义模板(带上声明字符编码、作者名、时间等)

Pycharm设置地址&#xff1a; 在File---settings---Editor---File and Code Templates---Python script 脚本里添加: 模板声明设置参考&#xff1a; # ---encoding:utf-8--- # Time : ${DATE} ${HOUR}:${MINUTE} # Author : 作者名 # Email &#xff1a;你的邮箱 # Sit…

Nginx启动,重启以及基本命令

1.启动nginx 进入nginx安装目录执行 ./sbin/nginx -c ./conf/nginx.conf 2.验证nginx配置文件是否正确 方法1. 进入nginx安装目录sbin下&#xff0c;输入命令 ./nginx -t 回显test is successful说明配置正确 方法2. 在启动nginx命令 -c 前加 -t ./sbin/nginx -t -c ./c…

一次诡异405 METHOD_NOT_ALLOWED “Request method ‘POST‘ not supported“问题排查记录

概述 任何稍微只要有一点经验的开发者都知道HTTP 405&#xff0c;表示方法不支持。如&#xff0c;本来是定义为POST接口&#xff0c;前端使用GET请求&#xff0c;就会报错。 但是我还真遇上一次405 METHOD_NOT_ALLOWED "Request method POST not supported"问题&am…

ReadDataByIdentifier(0x22)服务

ReadDataByIdentifier&#xff08;0x22&#xff09;服务 ReadDataByIdentifier服务允许客户端从一个或多个dataIdentifiers标识的服务器请求数据记录值。 客户端请求消息包含一个或多个两字节的dataIdentifier值&#xff0c;用于标识服务器维护的数据记录 允许的dataIdentifie…

【Azure】微软 Azure 基础解析(八)Azure 存储服务:探索Blob存储、队列存储、文件存储的特性与适用场景

本系列博文还在更新中&#xff0c;收录在专栏&#xff1a;「Azure探秘&#xff1a;构建云计算世界」 专栏中。 本系列文章列表如下&#xff1a; 【Azure】微软 Azure 基础解析&#xff08;三&#xff09;描述云计算运营中的 CapEx 与 OpEx&#xff0c;如何区分 CapEx 与 OpEx…

代码随想录第50天

1.买卖股票的最佳时机III&#xff1a; 动态规划五部曲详细分析一下&#xff1a; 确定dp数组以及下标的含义 一天一共就有五个状态&#xff0c; 没有操作 &#xff08;其实我们也可以不设置这个状态&#xff09;第一次持有股票第一次不持有股票第二次持有股票第二次不持有股…

2021年全国A级景区矢量分布数据(11969条)

一、景区定义 旅游景区是以旅游及其相关活动为主要功能或主要功能之一的空间或地域。本标准中旅游景区是指具有参观游览、休闲度假、康乐健身等功能&#xff0c;具备相应旅游服务设施并提供相应旅游服务的独立管理区。该管理区应有统一的经营管理机构和明确的地域范围。包括风景…

设计模式-中介者模式

中介者模式 问题背景中介者设计模式基本介绍原理 问题解决-代码示例注意事项和细节 问题背景 一提到中介&#xff0c;我们首先就能想到租房中介&#xff0c;有租房中介在我们需要租房子的时候可以给我们节省很大精力。 那没有中介我们要怎么租房呢&#xff1f; 我们可以想象一…

English Learning - L3 作业打卡 Lesson4 Day25 2023.5.29 周一

English Learning - L3 作业打卡 Lesson4 Day25 2023.5.29 周一 引言&#x1f349;句1: My husband has a good job.成分划分弱读连读爆破语调 &#x1f349;句2: He makes enough money to support our family.成分划分弱读连读语调 &#x1f349;句3: So we say he brings ho…

防火墙之部署服务器NAT

防火墙部署服务器NAT 原理概述&#xff1a; NAT&#xff08;Network Address Translation&#xff09;&#xff0c;是指网络地址转换&#xff0c;1994年提出的。 当在专用网内部的一些主机本来已经分配到了本地IP地址&#xff08;即仅在本专用网内使用的专用地址&#xff09;&a…

离散化以及树状数组

今天我们先来讲一讲什么叫做离散化&#xff08;简单的映射关系&#xff09; 一、离散化 一、概念&#xff1a;就是把一个无限的空间去映射到一个有限的空间中去&#xff08;通俗的可以理解成将数据相应的缩小&#xff09;为了更好的理解&#xff0c;请看下图&#xff1a; 已知…

90后测试员:“入职阿里,这一次,我决定不跳槽了...”

所谓“舒适”生活 记得上一份工作是去年听从了朋友的意见&#xff0c;“你一定要找一份舒适的工作&#xff0c;这样你一天就有好多时间玩&#xff0c;好多时间干自己想干的事情&#xff0c;摸鱼真香&#xff01;” 在这份“教导”下&#xff0c;开始了我的找工作之旅&#xf…

Day972.OAuth 2.0是要通过什么方式解决什么问题? -OAuth 2.0

OAuth 2.0是要通过什么方式&#xff1f;解决什么问题&#xff1f; 使用某个App 的时候&#xff0c;是直接使用了第三方帐号&#xff08;比如微信、微博&#xff09;登录&#xff0c;还是选择了重新注册新用户&#xff1f;如果选择了重新注册用户&#xff0c;那你还得上传头像、…

DPDK imissed、ierrors、rx_nombuf

在采用DPDK进行网络抓包时常常会通过rte_eth_stats_get函数获取当前网卡的丢包状态&#xff0c;首先看一下该函数的声明&#xff1a; // 函数声明(dpdk-stable-19.11.3/lib/librte_ethdev/rte_ethdev.h) int rte_eth_stats_get(uint16_t port_id, struct rte_eth_stats *stats…

高级程序员和新手小白程序员区别你是那个等级看解决bug速度

IT入门深似海 ,程序员行业&#xff0c;我觉得是最难做的。加不完的班&#xff0c;熬不完的夜。 和产品经理&#xff0c;扯不清,理还乱的宿命关系 一直都在 新需求-做项目-解决问题-解决bug-新需求 好像一直都是这么一个循环。&#xff08;哈哈哈&#xff09;我觉得一个好的程序…

【传染病模型】

传染病模型&#xff1a;原理介绍与应用实战 一、概述 在公共卫生研究中&#xff0c;传染病模型是一种关键的理论工具&#xff0c;用于理解和预测传染病的传播方式。 二、传染病模型原理 2.1 SIR模型 SIR模型是描述感染性传播病病人数量变化最简单的模型之一。其中&#xf…