WebServer项目(三)->linux网络编程基础知识

news2024/9/27 21:28:13

WebServer项目[三]->linux网络编程基础知识

  • 1. I/O多路复用(I/O多路转接)
  • 2. select
    • 1)select简介
    • 2)select详解
      • select具体怎么用?
      • 那FD_CLR函数是干嘛的?
      • 关于 fd_set,它具体是什么?
  • 3. poll(改进select)
  • 4. epoll
  • 5.epoll的两种工作模式
  • 6.UDP通信实现
  • 7.广播
  • 8.组播(多播)
  • 9.本地套接字
  • 10.阻塞/非阻塞,同步/异步
  • 11.Unix/Linux上的五种IO模型
    • 异步I/O模型与信号驱动I/O模型的区别(了解即可)

1. I/O多路复用(I/O多路转接)

I/O 多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能,
Linux 下实现 I/O 多路复用的系统调用主要有 select、poll 和 epoll。

2. select

思路:

  1. 首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中。

  2. 调用一个系统函数(也就是select),监听该列表中的文件描述符,直到这些描述符中的一个或者多个进行I/O操作时,该函数才返回。
    a.这个函数是阻塞
    b.函数对文件描述符的检测的操作是由内核完成的

  3. 在返回时,它会告诉进程有多少(哪些)描述符要进行I/O操作。
    在这里插入图片描述

1)select简介

:sizeof(fd_set) = 128   128 * 8 = 1024
#include <sys/time.h> 
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
			fd_set *exceptfds, struct timeval *timeout);
			- 参数:
				- nfds : 委托内核检测的最大文件描述符的值 + 1
				- readfds : 要检测的文件描述符的读的集合,委托内核检测哪些文件描述符的读的属性
				    - 一般检测读操作
					- 对应的是对方发送过来的数据,因为读是被动的接收数据,检测的就是读缓冲区
					- 是一个传入传出参数
				- writefds : 要检测的文件描述符的写的集合,委托内核检测哪些文件描述符的写的属性
					- 委托内核检测写缓冲区是不是还可以写数据(不满的就可以写)
				- exceptfds : 检测发生异常的文件描述符的集合
				- timeout : 设置的超时时间
					struct timeval {
					long tv_sec; /* seconds */
					long tv_usec; /* microseconds */
					};
					- NULL : 永久阻塞,直到检测到了文件描述符有变化
					- tv_sec = 0 && tv_usec = 0, 不阻塞
					- tv_sec > 0 && tv_usec > 0, 阻塞对应的时间
				- 返回值 :
					- -1 : 失败
					- >0(n) : 检测的集合中有n个文件描述符发生了变化

// 将参数文件描述符fd对应的标志位设置为0
void FD_CLR(int fd, fd_set *set);
// 判断fd对应的标志位是0还是1, 返回值 : fd对应的标志位的值,0,返回0, 1,返回1
int FD_ISSET(int fd, fd_set *set);
// 将参数文件描述符fd 对应的标志位,设置为1
void FD_SET(int fd, fd_set *set);
// fd_set一共有1024 bit, 全部初始化为0
void FD_ZERO(fd_set *set);

2)select详解

在这里插入图片描述
在这里插入图片描述


select具体怎么用?

elect 函数是 Linux 中常用的 I/O 多路复用函数之一,可以用于同时监视多个文件描述符的读、写和异常事件,当其中任意一个文件描述符发生变化时,select 函数就会返回,并通知应用程序处理相应的事件。

使用 select 函数需要注意以下几个步骤:

  1. 调用 select 函数前,需要创建一个 fd_set 类型的集合,并将需要监视的文件描述符添加到集合中fd_set 是一个结构体类型,用于表示待监视的文件描述符集合
fd_set read_fds; //声明读集合
FD_ZERO(&read_fds); //清空读集合
FD_SET(sockfd, &read_fds); //将 sockfd 添加到读集合中
  1. 给每个文件描述符设置非阻塞模式,以避免在 select 函数调用期间出现阻塞。这可以通过 fcntl 函数来实现。也可以在 socket 创建时就将其设置为非阻塞模式。
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
  1. 调用 select 函数并等待事件发生。select 函数的第一个参数是文件描述符的最大值加 1(为啥加一,比如最大值是101,那么select的源码就是一个for循环:for(int i = 0 ; i < 101+1 ;i++) => 刚好可以遍历0~101),第二个、第三个和第四个参数分别是待检查的读、写和异常事件集合。第五个参数是超时时间,如果设置为 NULL,则 select 函数将一直阻塞,直到有事件发生。如果设置为 0,则 select 函数将立即返回,如果没有事件发生,则返回 0
select(sockfd + 1, &read_fds, NULL, NULL, NULL);
  1. 检测事件并处理。如果返回值大于 0,说明有事件发生。可以使用 FD_ISSET 宏检查那些文件描述符上的事件是否已经发生,并进行相应的处理。
if (FD_ISSET(sockfd, &read_fds)) {
    // sockfd 上有可读事件发生
    char buf[BUFSIZ];
    int n = read(sockfd, buf, BUFSIZ);
    // 处理读取到的数据
}

需要注意的是,使用 select 函数的程序必须以多进程或者多线程的方式来运行,否则 select 函数会阻塞整个程序。同时,select 函数也存在一些局限性,例如最大支持的文件描述符数量有限等问题。

那FD_CLR函数是干嘛的?

FD_CLR 函数是 Linux 中用于从 fd_set 集合中清除指定文件描述符的宏。该函数可以将一个文件描述符从 fd_set 集合中移除,以便在后续的 select 调用中不再监视该文件描述符

FD_CLR 的调用格式如下:

void FD_CLR(int fd, fd_set *set);

其中,fd 是要从集合中移除的文件描述符,set 是 fd_set 集合。

使用 FD_CLR 函数的步骤如下:

  1. 在设置 fd_set 集合时,可以使用 FD_SET 宏将文件描述符添加到集合中。
  2. 如果需要从集合中移除某个文件描述符,可以使用 FD_CLR 宏进行操作。
fd_set fds;
FD_ZERO(&fds);         // 初始化集合
FD_SET(sockfd, &fds);  // 添加 sockfd 到集合

// 等待 sockfd 上发生事件
select(sockfd + 1, &fds, NULL, NULL, NULL);

// 处理事件,并从集合中移除 sockfd
if (FD_ISSET(sockfd, &fds)) {
    // sockfd 上有可读事件发生
    char buf[BUFSIZ];
    int n = read(sockfd, buf, BUFSIZ);
    // 处理读取到的数据
    FD_CLR(sockfd, &fds); // 将 sockfd 从集合中移除
}

需要注意的是,在使用 FD_CLR 函数时,必须确保 fd_set 集合已经初始化并且包含了要移除的文件描述符。否则可能会导致未知的错误。

如果需要从集合中移除某个文件描述符,可以使用 FD_CLR 宏进行操作。

关于 fd_set,它具体是什么?

fd_set 是一个结构体类型,它在头文件 sys/select.h 中被定义。该结构体用于表示一个待监视的文件描述符集合。

==fd_set 结构体本身并不存储文件描述符,而是以位图的形式存储,每个位表示对应的文件描述符是否在集合中。在 Linux 中,fd_set 的长度默认为 1024 位,最大支持的文件描述符数量也是 1024 个。==如果需要扩展 fd_set 的长度,可以使用 FD_SETSIZE 宏进行定义。

sizeof(fd_set)=128字节=1024

fd_set 包含一个数组,数组的每个元素都是 unsigned long 类型,表示 64 个文件描述符的状态。因此,fd_set 最大支持 1024/64=16 个元素,可以用编码方式表示 0~1023 号文件描述符是否在集合中。


fd_set 主要有以下几个宏定义:(上面已讲

  1. FD_ZERO(fd_set *set):将 fd_set 集合清空,即将所有位都设置为 0。
  2. FD_SET(int fd, fd_set *set):将指定的 fd 文件描述符添加到 fd_set 集合中。
  3. FD_CLR(int fd, fd_set *set):将指定的 fd 文件描述符从 fd_set 集合中移除。
  4. FD_ISSET(int fd, fd_set *set):判断指定的 fd 文件描述符是否在 fd_set 集合中。

下面是一个简单的 fd_set 示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main() {
 // 创建 socket
   int sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if (sockfd == -1) {
       perror("socket");
       exit(EXIT_FAILURE);
   }

   // 连接到服务器
   struct sockaddr_in addr;
   addr.sin_family = AF_INET;
   addr.sin_port = htons(80);
   inet_pton(AF_INET, "www.baidu.com", &addr.sin_addr.s_addr);
   if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
       perror("connect");
       exit(EXIT_FAILURE);
   }

   // 将 sockfd 添加到 fd_set 集合中
   fd_set fds;
   FD_ZERO(&fds);
   FD_SET(sockfd, &fds);

   // 等待 sockfd 上发生事件
   select(sockfd + 1, &fds, NULL, NULL, NULL);

   // 处理事件,并从集合中移除 sockfd
   if (FD_ISSET(sockfd, &fds)) {
       // sockfd 上有可读事件发生
       char buf[BUFSIZ];
       int n = read(sockfd, buf, BUFSIZ);
       // 处理读取到的数据
       FD_CLR(sockfd, &fds); // 将 sockfd 从集合中移除
   }

   // 关闭 socket
   close(sockfd);
   return 0;
}


3. poll(改进select)

#include <poll.h>

struct pollfd {
 int   fd;         /* 委托内核检测的文件描述符 */
 short events;     /* 委托内核检测文件描述符的什么事件 */
 short revents;    /* 文件描述符实际发生的事件 */
};

struct pollfd myfd;
myfd.fd = 5;
myfd.events = POLLIN | POLLOUT;
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
 - 参数:
        - fds : 是一个struct pollfd 结构体数组,这是一个需要检测的文件描述符的集合
        - nfds : 这个是第一个参数数组中最后一个有效元素的下标 + 1
        - timeout : 阻塞时长
            0 : 不阻塞
            -1 : 阻塞,当检测到需要检测的文件描述符有变化,解除阻塞
            >0 : 阻塞的时长
    - 返回值:
         -1 : 失败
         >0(n) : 成功,n表示检测到集合中有n个文件描述符发生变化

4. epoll

在这里插入图片描述

#include <sys/epoll.h>
// 创建一个新的epoll实例。在内核中创建了一个数据,这个数据中有两个比较重要的数据,一个是需要检
//测的文件描述符的信息(红黑树),还有一个是就绪列表,存放检测到数据发送改变的文件描述符信息(双向
//链表)。
int epoll_create(int size);
 - 参数:
        size : 目前没有意义了。随便写一个数,必须大于0
    - 返回值:
        -1 : 失败  +  错误号
        > 0 : 文件描述符,操作epoll实例的

typedef union epoll_data {
 void        *ptr;
 int          fd;
 uint32_t     u32;
 uint64_t     u64;
} epoll_data_t;

struct epoll_event {
 uint32_t     events;      /* Epoll events */
 epoll_data_t data;        /* User data variable */
};

常见的Epoll检测事件:
    - EPOLLIN  [其值为 0x001。EPOLLIN 表示对应的文件描述符可以进行读取操作,
    当该文件描述符上有数据可读时,epoll_wait() 函数将返回并触发 EPOLLIN 事件,
    以表明该文件描述符上已经有数据可读。]
    - EPOLLOUT
    - EPOLLERR

// 对epoll实例进行管理:添加文件描述符信息,删除信息,修改信息=>放到内核的红黑树上
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
 - 参数:
       - epfd : epoll实例对应的文件描述符
       - op : 要进行什么操作
			    它有三个宏定义作为参数:
                EPOLL_CTL_ADD: 添加   它的值为1
				EPOLL_CTL_MOD: 修改   它的值为3
				EPOLL_CTL_DEL: 删除   它的值为2
       - fd : 要检测的文件描述符
       - event : 检测文件描述符什么事情

// 检测函数                
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
 - 参数:
        - epfd : epoll实例对应的文件描述符(作用是对epoll实例进行操作)
        - events : 传出参数,保存了发送了变化的文件描述符的信息
        - maxevents : 第二个参数结构体数组的大小
        - timeout : 阻塞时间
            - 0 : 不阻塞
            - -1 : 阻塞,直到检测到fd数据发生变化,解除阻塞
            - > 0 : 阻塞的时长(毫秒)  
    - 返回值:
         - 成功,返回发送变化的文件描述符的个数 > 0
         - 失败 -1

5.epoll的两种工作模式

Epoll 的工作模式:

  • LT 模式 (水平触发)
    假设委托内核检测读事件 -> 检测fd的读缓冲区
    读缓冲区有数据 - > epoll检测到了会给用户通知
    a.用户不读数据,数据一直在缓冲区,epoll 会一直通知
    b.用户只读了一部分数据,epoll会通知
    c.缓冲区的数据读完了,不通知

  • ET 模式(边沿触发)
    假设委托内核检测读事件 -> 检测fd的读缓冲区
    读缓冲区有数据 - > epoll检测到了会给用户通知
    a.用户不读数据,数据一致在缓冲区中,epoll下次检测的时候就不通知了
    b.用户只读了一部分数据,epoll不通知
    c.缓冲区的数据读完了,不通知

6.UDP通信实现

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
	- 参数:
		- sockfd : 通信的fd
		- buf : 要发送的数据
		- len : 发送数据的长度
		- flags : 0
		- dest_addr : 通信的另外一端的地址信息
		- addrlen : 地址的内存大小
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
	- 参数:
		- sockfd : 通信的fd
		- buf : 接收数据的数组
		- len : 数组的大小
		- - flags : 0
		- src_addr : 用来保存另外一端的地址信息,不需要可以指定为NULL
		- addrlen : 地址的内存大小

7.广播

向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1。
a.只能在局域网中使用。
b.客户端需要绑定服务器广播使用的端口,才可以接收到广播消息。
在这里插入图片描述

8.组播(多播)

单播地址标识单个 IP 接口,广播地址标识某个子网的所有 IP 接口,多播地址标识一组 IP 接口。
单播和广播是寻址方案的两个极端(要么单个要么全部),多播则意在两者之间提供一种折中方案。多播数据报只应该由对它感兴趣的接口接收,也就是说由运行相应多播会话应用系统的主机上的接口接收。另外,广播一般局限于局域网内使用,而多播则既可以用于局域网,也可以跨广域网使用。
a.组播既可以用于局域网,也可以用于广域网
b.客户端需要加入多播组,才能接收到多播的数据

在这里插入图片描述

9.本地套接字

本地套接字的作用:本地的进程间通信
有关系的进程间的通信
没有关系的进程间的通信
本地套接字实现流程和网络套接字类似,一般呢采用TCP的通信流程。

10.阻塞/非阻塞,同步/异步

阻塞和非阻塞是指程序在执行调用时的状态。如果一个程序在执行某个调用时,如果该调用需要等待某些结果才能继续执行,那么这个调用就是阻塞的。相反,如果该调用不需要等待结果就能继续执行,那么这个调用就是非阻塞的。
同步和异步是指程序在调用完成后的状态。如果一个程序在调用完成后需要等待结果才能继续执行,那么这个调用就是同步的。相反,如果该调用不需要等待结果就能继续执行,那么这个调用就是异步的。
举个例子,如果你在使用浏览器访问一个网站,如果该网站的服务器需要一段时间才能响应你的请求,那么你的浏览器就会被阻塞,直到服务器响应完成。这就是一个阻塞的同步调用。相反,如果你使用 Ajax 技术,浏览器会发起一个异步调用,不会被阻塞,而是可以继续执行其他任务,当服务器响应完成后,浏览器会执行回调函数来处理服务器返回的数据。这就是一个非阻塞的异步调用。
总的来说,阻塞和非阻塞是针对程序在执行调用时的状态,而同步和异步是针对程序在调用完成后的状态。在编写程序时,需要根据实际情况选择合适的调用方式。
在这里插入图片描述

11.Unix/Linux上的五种IO模型

  1. 阻塞式I/O模型(Blocking I/O Model):在阻塞式I/O模型中,当应用程序调用I/O操作时,程序会一直阻塞(即等待)直到I/O操作完成。这意味着应用程序将无法做其他事情,直到I/O操作完成。(容易理解)

  2. 非阻塞式I/O模型(Non-Blocking I/O Model):在非阻塞式I/O模型中,当应用程序调用I/O操作时,程序会立即返回,而不是一直等待I/O操作完成。如果I/O操作还没有完成,应用程序可以进行其他操作,或者再次尝试I/O操作。(容易理解)

  3. I/O复用模型(I/O Multiplexing Model):在I/O复用模型中,应用程序可以同时监视多个I/O操作,等待其中的任何一个I/O操作完成。这样,应用程序可以同时处理多个I/O操作,而不必阻塞或非阻塞地等待每个I/O操作完成。

  4. 信号驱动I/O模型(Signal-Driven I/O Model):在信号驱动I/O模型中,应用程序发起一个I/O操作后,程序可以继续执行其他任务,而不是一直等待I/O操作完成。当I/O操作完成时,操作系统会发送一个信号给应用程序,应用程序可以捕获该信号并处理I/O操作的结果。

  5. 异步I/O模型(Asynchronous I/O Model):在异步I/O模型中,应用程序发起一个I/O操作后,程序可以继续执行其他任务,而不必等待I/O操作完成。当I/O操作完成后,操作系统会通知应用程序,并将I/O操作的结果传递给应用程序。

异步I/O模型与信号驱动I/O模型的区别(了解即可)

异步I/O模型和信号驱动I/O模型都是在I/O操作完成后通知应用程序的模型,它们的区别主要在于通知的方式和处理方式。

异步I/O模型是一种完全异步的模型。在异步I/O模型中,应用程序发起一个I/O操作后,程序可以继续执行其他任务,而不必等待I/O操作完成。当I/O操作完成后,操作系统会通知应用程序,并将I/O操作的结果传递给应用程序。应用程序需要在回调函数中处理I/O操作的结果。

信号驱动I/O模型是一种半同步、半异步的模型。在信号驱动I/O模型中,应用程序发起一个I/O操作后,程序可以继续执行其他任务,而不是一直等待I/O操作完成。当I/O操作完成时,操作系统会发送一个信号给应用程序,应用程序可以捕获该信号并处理I/O操作的结果。

因此,异步I/O模型相对于信号驱动I/O模型具有更高的效率和更好的可扩展性,因为它不需要等待I/O操作的完成,并且不需要使用信号来通知应用程序。但是,异步I/O模型的实现比较复杂,需要使用回调函数来处理I/O操作的结果。信号驱动I/O模型的实现相对简单,但是由于使用了信号来通知应用程序,可能会存在一些信号处理的问题。因此,在选择I/O模型时,需要根据具体的需求和实际情况来选择。

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

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

相关文章

qemu-img resize gpt分区 parted修复分区信息 虚拟机 lvm 扩容根分区

扩容qcow2虚拟盘 关闭虚拟机 virsh destroy redflag1 qemu-img resize从20G扩容至40G qemu-img resize redflag.qcow2 40G 启动 virsh start redflag1 查看状态&#xff0c;当前无任何变化 fdisk 查看vda&#xff0c;已经变大 查看lvm信息 xfs_info 扩容虚拟机根分区 修…

KD2684S电机匝间耐电压测试仪

一、产品简介 试验仪适用于电机、变压器、电器线圈等这些由漆包线绕制的产品。因漆包线的绝缘涂敷层本身存在着质量问题&#xff0c;以及在绕线、嵌线、刮线、接头端部整形、绝缘浸漆、装配等工序工艺中不慎而引起绝缘层的损伤等&#xff0c;都会造成线圈层间或匝间绝缘层的绝缘…

BGP的路径属性及选路规则

路径属性 路径属性对于BGP而言&#xff0c;BGP路径属性描述了该条路由的各项特征&#xff0c;同时&#xff0c;路由携带的路径属性也在某些场景下影响BGP路由优选的决策。 公认属性-----所有的BGP路由器均可以识别的属性 强制属性-----指当BGP路由器使用update报文通报路由更新…

机器学习实战:Python基于DT决策树模型进行分类预测(六)

文章目录 1 前言1.1 决策树的介绍1.2 决策树的应用 2 Scikit-learn数据集演示2.1 导入函数2.2 导入数据2.3 建模2.4 评估模型2.5 可视化决策树2.6 优化模型2.7 可视化优化模型 3 讨论 1 前言 1.1 决策树的介绍 决策树&#xff08;Decision Tree&#xff0c;DT&#xff09;是一…

R语言的Meta分析【全流程、不确定性分析】方法与Meta机器学习技术应用

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

【springboot】缓存之@Cacheable、@CachePut、@CacheEvict的用法

目录 一、注解参数说明1.1 属性说明1.1.1 value/cacheNames 属性1.1.2 key属性1.1.3 keyGenerator属性1.1.4 cacheManager属性1.1.5 cacheResolver属性1.1.6 condition属性1.1.7 unless 属性1.1.8 sync 属性 1.2 Cacheable注解1.3 CachePut注解1.4 CacheEvict注解1.4.1 allEntr…

低代码产品如何分类,大部分人都没有搞清楚

最近许多技术峰会都出现了低代码这个名词&#xff0c;可以说&#xff0c;低代码是中台之后&#xff0c;又一个热门话题和名词了。 一、什么是低代码平台&#xff1f; 低代码平台是无需编码或通过少量代码就可以快速生成应用程序的开发平台。也是一款图形化、拖拉拽方式快速实…

hadoop伪分布式安装

文章目录 1. 将安装包hadoop-3.1.3.tar.gz上次至linux中2. 进行解压操作3. 修改目录名称4. 配置环境变量5. 修改自定义配置文件5.1 hadoop-env.sh5.2 core-site.xml5.3 hdfs-site.xml5.4 workers 6. 格式化集群7. 免密登录8. 启动hdfs9. 关闭hdfs 1. 将安装包hadoop-3.1.3.tar.…

群晖NAS与阿里云盘同步的方法

同步方法&#xff1a;通过在 docker 中安装 aliyundrive-webdav 实现与阿里云盘同步。 下载和安装 aliyundrive-webdav 在 docker 的注册表中搜素 aliyun&#xff0c;选择点赞比较多的 messense/aliyundrive-webdav&#xff1a; 下载后安装。建议在配置和启动之前&#xff…

Docker 的安装和镜像容器的基本操作

文章目录 一、Docker 概述1、Docker的概念2、容器的优点3、容器与虚拟机的区别4、容器在内核中支持2种重要技术5、Docker核心概念 二、Docker的安装1、docker的安装步骤2、实例操作&#xff1a;安装docker 三、Docker 镜像操作1、搜索镜像2、获取镜像3、镜像加速下载4、查看镜像…

基础工业工程(易树平、郭伏)——第三草 工作研究

第三草 工作研究 第一节 工作研究概述 一、工作研究的对象 工作研究的对象是作业系统&#xff0c;这是一个由多个相互关联的因素所组成的有机整体&#xff0c;旨在实现预定的功能和目标。作业系统的目标表现为输出一定的“产品”或“服务”&#xff0c;主要由材料、设备、能…

超级简洁、彻底组件化的轻量级Android Kotlin Jetpack MVVM组件化框架

结构 特点&#xff1a; 彻底组件化&#xff0c;且更简洁&#xff0c;Module具有独立的Application、AndroidMinifast、资源文件等&#xff1b;Application和Library的切换更加快捷&#xff1b;超级简洁、且多功能的网络层封装&#xff0c;自带2级缓存&#xff0c;App端内嵌了日…

盛元广通高校实验室安全智能管理平台

实验室安全问题一直以来都是高校管理的重点&#xff0c;依据《高等学校实验室安全规范》相关要求&#xff0c;应教育相关部门以及应急管理部门的相关规定&#xff0c;关于安全工作的系列重要指示和部署&#xff0c;必须按照危险源管控分级管理体系对实验室进行分级分类管理&…

ebay、速卖通、ozon销量下滑怎么办?怎样可以提高转化率

单量不好&#xff0c;就开始焦虑&#xff0c;而真正需要了解为什么会出现销量下滑的原因&#xff0c;从不断更新自己的知识&#xff0c;提高自己的技能&#xff0c;如何提高自己的技能呢&#xff1f; 把所学到的知识学以致用&#xff0c;listing的评分&#xff0c;退货率&…

ESP32学习四-自定义分区表

1、简介 ESP32-WROOM-32集成了4MB SPI FLASH。对应的&#xff0c;也会对这4MB FLAHS进行分区处理。在编译esp32程序时&#xff0c;通过make menuconfig -> Partition Table可以设置三种分区。 工厂程序&#xff08;无OTA分区&#xff09; 工厂程序&#xff08;双OTA分…

自旋锁/读者写者问题

自旋锁 自旋锁的概念和理解 锁在处理需要申请加锁的线程的时候&#xff0c;一般有两种处理方法&#xff1a;一种是挂起等待&#xff0c;另外一种是自旋。自旋即轮询。 挂起等待&#xff1a; 当一个线程成功申请锁&#xff0c;并进入临界区后&#xff0c;其它线程在申请的时候…

【经验分享】硬件工程师需要知道的DFM可制造性设计

最近&#xff0c;有硬件工程师朋友找我讨论DFM&#xff0c;也就是可制造性设计。Design for Manufacturability。 什么是可制造性设计&#xff0c;看一张图很容易明白&#xff1a; 过大的PCB&#xff0c;无法上产线批量生产&#xff0c;极大的PCB面积浪费&#xff0c;自然是失…

Spring Batch 指南

SpringBatch 介绍 目前&#xff0c;Spring Batch是批处理框架界为数不多的优秀框架(Java语言开发)。 Spring Batch 是一个轻量级的、完善的批处理框架,旨在帮助企业建立健壮、高效的批处理应用。 Spring Batch是Spring的一个子项目,使用Java语言并基于Spring框架为基础开发,…

.Net 6.0 部署Linux+Nginx +PM2教程

今天带大家将本地.Net6.0项目部署到Linux系统中,其中有用到Nginx反向代理和PM2进程管理工具,希望本偏文章能对你有所帮助,成为你成功路上的垫脚石! 背景: 在.Net 5.0横空出世之后,.Net已经支持夸平台了,身为一名合格的码农,你敢说你不会用Linux? 哈哈哈开个玩笑,因为工作最近接…

Idea启动运行报错:Error:java: 无效的源发行版: 13

最近在做Springboot项目时&#xff0c;常常出现上述错误&#xff0c;小编也不知道怎么回事&#xff0c;到网上找了这个方面的解决办法&#xff0c;但是却发现根本解决不了&#xff0c;最终通过小编多次尝试&#xff0c;终于发现&#xff0c;为什么会报这个错误。(应该是Java版本…