当前位置: 首页 > news >正文

[源码解析]socket系统调用上

文章目录

    • socket函数API
    • 内核源码
      • sock_create
      • inet_create
      • sock_alloc
      • sock_map_fd
      • 相关数据结构

本文将以socket函数为例,分析它在Linux5.12.10内核中的实现,先观此图,宏观上把握它在内核中的函数调用关系:
socket接口调用

socket函数API

socket 函数原型:

#include <sys/socket.h>int socket(int domain, int type, int protocol)

该函数用于创建一个新的socket。

第一个参数:

domain:协议簇,常用的协议簇有:AF_INET, AF_INET6, AF_LOCAL。这个参数决定了socket的地址类型,这个应该很好理解AF_INET用于ipv4地址,AF_INET6用于ipv6地址,AF_LOCAL用于本地进程间通信。

第二个参数:

type:socket类型有好几种,主要是两种:SOCK_STREAM、SOCK_DGRAM(数据报),通俗说就是字节流socket和数据报socket,当你在创建的使用使用哪一种由第二个参数指定。stream socket基于TCP协议,是一个有序、可靠、全双工的字节流通道。datagram socket基于UDP协议,不需要建立和维持连接,可能会丢失或错乱。

第三个参数:

protocol:指定协议,常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TICP等,分别对应TCP协议,UDP协议,STCP协议,TICP协议。通常这个参数设置为0,表示自适应协议

所以这个函数通常这样用:

int socket_fd = socket(AF_INET, SOCK_STREAM, 0);

在Linux下一个进程默认打开的文件描述符是1024个,也就是说一个进程最多能创建1024个socket,超过就会报Too many open files(这个问题在工作中也会遇到)。通过ulimit命令可以查看到

# ulimit -a
core file size          (blocks, -c) unlimited
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 29414
max locked memory       (kbytes, -l) 16384
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 29414
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

如果你要修改这个上限到2021个:

# ulimit -HSn 2021

内核源码

//~/linux-5.12.10/include/linux/socket.h 头文件
extern int __sys_socket(int family, int type, int protocol);

socket函数调用结束后,用户层看到返回一个整型的句柄,但是内核在内部会创建一系列的socket相关的内核对象(不是只有一个对象)

// ~/linux-5.12.10/net/socket.c line:1481
/* Mask which covers at least up to SOCK_MASK-1.  The* remaining bits are used as flags. */
#define SOCK_TYPE_MASK 0xfint __sys_socket(int family, int type, int protocol)
{int retval;struct socket *sock;int flags;//... 略去参数合法性校验代码retval = sock_create(family, type, protocol, &sock);if (retval < 0)return retval;return sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
}SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{return __sys_socket(family, type, protocol);
}

sock_create

sock_create是创建socket的主要位置,其中sock_create又调用__sock_create

// ~/linux-5.12.10/net/socket.c line:1337
/*
//net_proto_family结构体定义了每一个协议族的新建socket句柄
struct net_proto_family {int		family;int		(*create)(struct net *net, struct socket *sock,int protocol, int kern);struct module	*owner;
};static const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly;
*/
int __sock_create(struct net *net, int family, int type, int protocol,struct socket **res, int kern)
{int err;struct socket *sock;const struct net_proto_family *pf;/**      Check protocol is in range*/if (family < 0 || family >= NPROTO)return -EAFNOSUPPORT;if (type < 0 || type >= SOCK_MAX)return -EINVAL;/* Compatibility.This uglymoron is moved from INET layer to here to avoiddeadlock in module load.*/if (family == PF_INET && type == SOCK_PACKET) {pr_info_once("%s uses obsolete (PF_INET,SOCK_PACKET)\n",current->comm);family = PF_PACKET;}err = security_socket_create(family, type, protocol, kern);if (err)return err;/**	Allocate the socket and allow the family to set things up. if*	the protocol is 0, the family is instructed to select an appropriate*	default.*/// 分配socket对象,如果protocol为0 将会被设置合适的协议sock = sock_alloc();if (!sock) {net_warn_ratelimited("socket: no more sockets\n");return -ENFILE;	/* Not exactly a match, but its theclosest posix thing */}sock->type = type;#ifdef CONFIG_MODULES/* Attempt to load a protocol module if the find failed.** 12/09/1996 Marcin: But! this makes REALLY only sense, if the user* requested real, full-featured networking support upon configuration.* Otherwise module support will break!*/if (rcu_access_pointer(net_families[family]) == NULL)request_module("net-pf-%d", family);
#endif// 获取每个协议族的操作表rcu_read_lock();pf = rcu_dereference(net_families[family]);err = -EAFNOSUPPORT;if (!pf)goto out_release;/** We will call the ->create function, that possibly is in a loadable* module, so we have to bump that loadable module refcnt first.*/if (!try_module_get(pf->owner))goto out_release;/* Now protected by module ref count */rcu_read_unlock();/// 调用指定协议族的创建函数,对于AF_INET对应的是inet_createerr = pf->create(net, sock, protocol, kern);if (err < 0)goto out_module_put;/** Now to bump the refcnt of the [loadable] module that owns this* socket at sock_release time we decrement its refcnt.*/if (!try_module_get(sock->ops->owner))goto out_module_busy;/** Now that we're done with the ->create function, the [loadable]* module can have its refcnt decremented*/module_put(pf->owner);err = security_socket_post_create(sock, family, type, protocol, kern);if (err)goto out_sock_release;*res = sock;return 0;out_module_busy:err = -EAFNOSUPPORT;
out_module_put:sock->ops = NULL;module_put(pf->owner);
out_sock_release:sock_release(sock);return err;out_release:rcu_read_unlock();goto out_sock_release;
}

inet_create

__sock_create 里,首先调用sock_alloc来分配一个struct socket内核对象,接着获取协议族的操作函数表,并调用其create方法。对于AF_INET协议族来说,执行到的是inet_create方法

//~/linux-5.12.10/net/ipv4/af_inet.c
/*
/* This is used to register socket interfaces for IP protocols.  */
struct inet_protosw {struct list_head list;/* These two fields form the lookup key.  */unsigned short	 type;	   /* This is the 2nd argument to socket(2). */unsigned short	 protocol; /* This is the L4 protocol number.  */struct proto	 *prot;const struct proto_ops *ops;unsigned char	 flags;      /* See INET_PROTOSW_* below.  */
};
#define list_for_each_entry_rcu		list_for_each_entry#define list_for_each_entry(pos, head, member)				\for (pos = list_first_entry(head, typeof(*pos), member);	\&pos->member != (head);					\pos = list_next_entry(pos, member))*/
static int inet_create(struct net *net, struct socket *sock, int protocol,int kern)
{struct sock *sk;struct inet_protosw *answer;struct inet_sock *inet;struct proto *answer_prot;unsigned char answer_flags;int try_loading_module = 0;int err;if (protocol < 0 || protocol >= IPPROTO_MAX)return -EINVAL;sock->state = SS_UNCONNECTED;/* Look for the requested type/protocol pair. */
lookup_protocol:err = -ESOCKTNOSUPPORT;rcu_read_lock();list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {err = 0;/* Check the non-wild match. */if (protocol == answer->protocol) {if (protocol != IPPROTO_IP)break;} else {/* Check for the two wild cases. */if (IPPROTO_IP == protocol) {protocol = answer->protocol;break;}if (IPPROTO_IP == answer->protocol)break;}err = -EPROTONOSUPPORT;}//...err = -EPERM;if (sock->type == SOCK_RAW && !kern &&!ns_capable(net->user_ns, CAP_NET_RAW))goto out_rcu_unlock;//将 inet_stream_ops 赋值到sock->opssock->ops = answer->ops;answer_prot = answer->prot;answer_flags = answer->flags;rcu_read_unlock();WARN_ON(!answer_prot->slab);err = -ENOBUFS;// 分配sock对象,并把answer_prot赋值到sock->sk_protsk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);if (!sk)goto out;err = 0;if (INET_PROTOSW_REUSE & answer_flags)sk->sk_reuse = SK_CAN_REUSE;inet = inet_sk(sk);inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;inet->nodefrag = 0;if (SOCK_RAW == sock->type) {inet->inet_num = protocol;if (IPPROTO_RAW == protocol)inet->hdrincl = 1;}if (net->ipv4.sysctl_ip_no_pmtu_disc)inet->pmtudisc = IP_PMTUDISC_DONT;elseinet->pmtudisc = IP_PMTUDISC_WANT;inet->inet_id = 0;// 对sock对象进行初始化sock_init_data(sock, sk);sk->sk_destruct	   = inet_sock_destruct;sk->sk_protocol	   = protocol;sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;inet->uc_ttl	= -1;inet->mc_loop	= 1;inet->mc_ttl	= 1;inet->mc_all	= 1;inet->mc_index	= 0;inet->mc_list	= NULL;inet->rcv_tos	= 0;sk_refcnt_debug_inc(sk);if (inet->inet_num) {/* It assumes that any protocol which allows* the user to assign a number at socket* creation time automatically* shares.*/inet->inet_sport = htons(inet->inet_num);/* Add to protocol hash chains. */err = sk->sk_prot->hash(sk);if (err) {sk_common_release(sk);goto out;}}if (sk->sk_prot->init) {err = sk->sk_prot->init(sk);if (err) {sk_common_release(sk);goto out;}}if (!kern) {err = BPF_CGROUP_RUN_PROG_INET_SOCK(sk);if (err) {sk_common_release(sk);goto out;}}
out:return err;
out_rcu_unlock:rcu_read_unlock();goto out;
}

当流程走到inet_create函数的时候根据type去inetsw数组中找到对应类型套接字的inet_protosw结构体,我们前面提到协议栈中已经定义了PF_INET协议族支持的inet_protosw结构体,总共有4个。

找到inet_protosw结构体以后还需要进一步判断protocol和inet_protosw中定义的protocol是否是一致的。内核中定义支持的protocol有一个特殊的值IPPROTO_IP(IPPROTO_IP为0),可以理解为一个通配符也可以理解为一个默认值,就是说我不指定protocol,由内核自己决定使用哪一个protocol。

那么内核根据什么来选择protocol呢?就是根据内核定义的全局inetsw中对应类型的inet_protosw中的protocol。

/* Upon startup we insert all the elements in inetsw_array[] into* the linked list inetsw.*/
// static struct list_head inetsw[SOCK_MAX];
// inetsw_array挂在链表上
static struct inet_protosw inetsw_array[] =
{{.type =       SOCK_STREAM,.protocol =   IPPROTO_TCP,.prot =       &tcp_prot,.ops =        &inet_stream_ops,.flags =      INET_PROTOSW_PERMANENT |INET_PROTOSW_ICSK,},{.type =       SOCK_DGRAM,.protocol =   IPPROTO_UDP,.prot =       &udp_prot,.ops =        &inet_dgram_ops,.flags =      INET_PROTOSW_PERMANENT,},{.type =       SOCK_DGRAM,.protocol =   IPPROTO_ICMP,.prot =       &ping_prot,.ops =        &inet_sockraw_ops,.flags =      INET_PROTOSW_REUSE,},{.type =       SOCK_RAW,.protocol =   IPPROTO_IP,	/* wild card */  //0.prot =       &raw_prot,.ops =        &inet_sockraw_ops,.flags =      INET_PROTOSW_REUSE,}
};// ~/linux-5.12.10/net/ipv4/af_inet.c  inet_create函数
// int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
// 初始化protocol为0, type为SOCK_STREAM
// 经过list_for_each_entry_rcu遍历,protocol修正为IPPROTO_TCP
// protocol = answer->protocol --> protocol = IPPROTO_TCP
// 如果type为SOCK_DGRAM, 则protocol被修正为IPPROTO_UDP
list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {err = 0;/* Check the non-wild match. */if (protocol == answer->protocol) {if (protocol != IPPROTO_IP)break;} else {/* Check for the two wild cases. */if (IPPROTO_IP == protocol) {protocol = answer->protocol;break;}if (IPPROTO_IP == answer->protocol)break;}err = -EPROTONOSUPPORT;}

继续看sock_init_data实现

// ~/linux-5.12.10/net/core/sock.c
void sock_init_data(struct socket *sock, struct sock *sk)
{sk_init_common(sk);sk->sk_send_head	=	NULL;timer_setup(&sk->sk_timer, NULL, 0);sk->sk_allocation	=	GFP_KERNEL;sk->sk_rcvbuf		=	sysctl_rmem_default;sk->sk_sndbuf		=	sysctl_wmem_default;sk->sk_state		=	TCP_CLOSE;sk_set_socket(sk, sock);sock_set_flag(sk, SOCK_ZAPPED);if (sock) {sk->sk_type	=	sock->type;RCU_INIT_POINTER(sk->sk_wq, &sock->wq);sock->sk	=	sk;sk->sk_uid	=	SOCK_INODE(sock)->i_uid;} else {RCU_INIT_POINTER(sk->sk_wq, NULL);sk->sk_uid	=	make_kuid(sock_net(sk)->user_ns, 0);}rwlock_init(&sk->sk_callback_lock);if (sk->sk_kern_sock)lockdep_set_class_and_name(&sk->sk_callback_lock,af_kern_callback_keys + sk->sk_family,af_family_kern_clock_key_strings[sk->sk_family]);elselockdep_set_class_and_name(&sk->sk_callback_lock,af_callback_keys + sk->sk_family,af_family_clock_key_strings[sk->sk_family]);sk->sk_state_change	=	sock_def_wakeup;sk->sk_data_ready	=	sock_def_readable;sk->sk_write_space	=	sock_def_write_space;sk->sk_error_report	=	sock_def_error_report;sk->sk_destruct		=	sock_def_destruct;sk->sk_frag.page	=	NULL;sk->sk_frag.offset	=	0;sk->sk_peek_off		=	-1;sk->sk_peer_pid 	=	NULL;sk->sk_peer_cred	=	NULL;sk->sk_write_pending	=	0;sk->sk_rcvlowat		=	1;sk->sk_rcvtimeo		=	MAX_SCHEDULE_TIMEOUT;sk->sk_sndtimeo		=	MAX_SCHEDULE_TIMEOUT;sk->sk_stamp = SK_DEFAULT_STAMP;
#if BITS_PER_LONG==32seqlock_init(&sk->sk_stamp_seq);
#endifatomic_set(&sk->sk_zckey, 0);#ifdef CONFIG_NET_RX_BUSY_POLLsk->sk_napi_id		=	0;sk->sk_ll_usec		=	sysctl_net_busy_read;
#endifsk->sk_max_pacing_rate = ~0UL;sk->sk_pacing_rate = ~0UL;WRITE_ONCE(sk->sk_pacing_shift, 10);sk->sk_incoming_cpu = -1;sk_rx_queue_clear(sk);/** Before updating sk_refcnt, we must commit prior changes to memory* (Documentation/RCU/rculist_nulls.rst for details)*/smp_wmb();refcount_set(&sk->sk_refcnt, 1);atomic_set(&sk->sk_drops, 0);
}

当软中断上收到数据包时会调用sk_data_ready函数指针(实际被设置成了sock_def_readable())来唤醒在sock上等待的进程。

sock_alloc

sock_alloc函数分配一个struct socket结构体,将sockfs相关属性填充在socket_alloc结构体的vfs_inode变量中,以限定后续对这个sock文件允许的操作。sock_alloc()里体现了linux一切皆文件(Everything is a file)理念,即使用文件系统来管理socket,这也是VFS所要达到的效果

struct socket *sock_alloc(void)
{struct inode *inode;struct socket *sock;inode = new_inode_pseudo(sock_mnt->mnt_sb);if (!inode)return NULL;sock = SOCKET_I(inode);inode->i_ino = get_next_ino();inode->i_mode = S_IFSOCK | S_IRWXUGO;inode->i_uid = current_fsuid();inode->i_gid = current_fsgid();inode->i_op = &sockfs_inode_ops;return sock;
}

sock_map_fd

static int sock_map_fd(struct socket *sock, int flags)
{struct file *newfile;int fd = get_unused_fd_flags(flags);if (unlikely(fd < 0)) {sock_release(sock);return fd;}newfile = sock_alloc_file(sock, flags, NULL);if (!IS_ERR(newfile)) {fd_install(fd, newfile);return fd;}put_unused_fd(fd);return PTR_ERR(newfile);
}// linux-5.12.10/fs/file.c
int __get_unused_fd_flags(unsigned flags, unsigned long nofile)
{return alloc_fd(0, nofile, flags);
}int get_unused_fd_flags(unsigned flags)
{return __get_unused_fd_flags(flags, rlimit(RLIMIT_NOFILE));
}

sock_map_fd–>get_unused_fd_flags–>__get_unused_fd_flags–>alloc_fd获取一个可用的fd


/** allocate a file descriptor, mark it busy.*/
static int alloc_fd(unsigned start, unsigned end, unsigned flags)
{struct files_struct *files = current->files;unsigned int fd;int error;struct fdtable *fdt;spin_lock(&files->file_lock);
repeat:fdt = files_fdtable(files);fd = start;if (fd < files->next_fd)fd = files->next_fd;if (fd < fdt->max_fds)fd = find_next_fd(fdt, fd);/** N.B. For clone tasks sharing a files structure, this test* will limit the total number of files that can be opened.*/error = -EMFILE;if (fd >= end)goto out;error = expand_files(files, fd);if (error < 0)goto out;/** If we needed to expand the fs array we* might have blocked - try again.*/if (error)goto repeat;if (start <= files->next_fd)files->next_fd = fd + 1;__set_open_fd(fd, fdt);if (flags & O_CLOEXEC)__set_close_on_exec(fd, fdt);else__clear_close_on_exec(fd, fdt);error = fd;
#if 1/* Sanity check */if (rcu_access_pointer(fdt->fd[fd]) != NULL) {printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);rcu_assign_pointer(fdt->fd[fd], NULL);}
#endifout:spin_unlock(&files->file_lock);return error;
}

sock_map_fd–>get_unused_fd_flags–>__get_unused_fd_flags–>sock_alloc_file分配struct file结构

// net/socket.c
/**	Obtains the first available file descriptor and sets it up for use.**	These functions create file structures and maps them to fd space*	of the current process. On success it returns file descriptor*	and file struct implicitly stored in sock->file.*	Note that another thread may close file descriptor before we return*	from this function. We use the fact that now we do not refer*	to socket after mapping. If one day we will need it, this*	function will increment ref. count on file by 1.**	In any case returned fd MAY BE not valid!*	This race condition is unavoidable*	with shared fd spaces, we cannot solve it inside kernel,*	but we take care of internal coherence yet.*//***	sock_alloc_file - Bind a &socket to a &file*	@sock: socket*	@flags: file status flags*	@dname: protocol name**	Returns the &file bound with @sock, implicitly storing it*	in sock->file. If dname is %NULL, sets to "".*	On failure the return is a ERR pointer (see linux/err.h).*	This function uses GFP_KERNEL internally.*/struct file *sock_alloc_file(struct socket *sock, int flags, const char *dname)
{struct file *file;if (!dname)dname = sock->sk ? sock->sk->sk_prot_creator->name : "";file = alloc_file_pseudo(SOCK_INODE(sock), sock_mnt, dname,O_RDWR | (flags & O_NONBLOCK),&socket_file_ops);if (IS_ERR(file)) {sock_release(sock);return file;}sock->file = file;file->private_data = sock;stream_open(SOCK_INODE(sock), file);return file;
}

相关数据结构

// file: include/linux/net.h
struct socket_wq {/* Note: wait MUST be first field of socket_wq */wait_queue_head_t	wait;struct fasync_struct	*fasync_list;unsigned long		flags; /* %SOCKWQ_ASYNC_NOSPACE, etc */struct rcu_head		rcu;
} ____cacheline_aligned_in_smp;/***  struct socket - general BSD socket*  @state: socket state (%SS_CONNECTED, etc)*  @type: socket type (%SOCK_STREAM, etc)*  @flags: socket flags (%SOCK_NOSPACE, etc)*  @ops: protocol specific socket operations*  @file: File back pointer for gc*  @sk: internal networking protocol agnostic socket representation*  @wq: wait queue for several uses*/
struct socket {socket_state		state;short			type;unsigned long		flags;struct file		*file;struct sock		*sk;const struct proto_ops	*ops;struct socket_wq	wq;
};

至此,一个tcp对象,确切地说是AF_INET协议族下的SOCK_STREAM对象就算创建完成了。这里花费了一个socket系统调用的开销。

ref: https://www.cnblogs.com/liyuanhong/articles/10591069.html

相关文章:

[源码解析]socket系统调用上

文章目录socket函数API内核源码sock_createinet_createsock_allocsock_map_fd相关数据结构本文将以socket函数为例&#xff0c;分析它在Linux5.12.10内核中的实现&#xff0c;先观此图&#xff0c;宏观上把握它在内核中的函数调用关系&#xff1a;socket函数API socket 函数原…...

Jenkins部署与自动化构建

Jenkins笔记 文章目录Jenkins笔记[toc]一、安装Jenkinsdocker 安装 JenkinsJava启动war包直接安装二、配置mavenGit自动构建jar包三、自动化发布到测试服务器运行超时机制数据流重定向编写清理Shell脚本四、构建触发器1. 生成API token2. Jenkins项目配置触发器3. 远程Git仓库配…...

网络编程三要素

网络编程三要素 IP、端口号、协议 三要素分别代表什么 ip&#xff1a;设备在网络中的地址&#xff0c;是唯一的标识 端口号&#xff1a;应用程序在设备中的唯一标识 协议&#xff1a;数据在网络中传输的规则 常见的协议有UDP、TCP、http、https、ftp ip&#xff1a;IPv4和…...

如何编写一个自己的web前端脚手架

脚手架简介 脚手架是创建前端项目的命令行工具&#xff0c;集成了常用的功能和配置&#xff0c;方便我们快速搭建项目&#xff0c;目前网络上也有很多可供选择的脚手架。 一个"简单脚手架"的构成其实非常少&#xff0c;即 代码模板 命令行工具。其中代码模板是脚手…...

计算机网络第1章(概述)

文章目录1.1、计算机网络在信息时代的作用1.2、因特网概述1、网络、互连网&#xff08;互联网&#xff09;和因特网2、因特网发展的三个阶段3、因特网的标准化工作4、因特网的组成1.3 三种交换方式1、电路交换&#xff08;Circuit Switching&#xff09;2、分组交换&#xff08…...

grid布局

一、概述 CSS Grid 布局是 CSS 中最强大的布局系统。与 flexbox 的一维布局系统不同&#xff0c;CSS Grid 布局是一个二维布局系统&#xff0c;也就意味着它可以同时处理列和行。通过将 CSS 规则应用于 父元素 (成为 Grid Container 网格容器)和其 子元素&#xff08;成为 Gri…...

博客平台打造出色的个人资料管理与展示:实用技巧与代码示例

个人资料管理与展示是博客平台的重要功能之一。本文将通过设计思路、技术实现和代码示例&#xff0c;详细讲解如何构建出色的个人资料管理与展示功能。结合CodeInsight平台的实践案例&#xff0c;帮助您深入了解个人资料管理与展示的设计原则和技术实现。 一、设计思路 在设计…...

【genius_platform软件平台开发】第九十三讲:串口通信(485通信)

485通信1. 485通信1.1 termios结构1.2 头文件1.3 函数讲解1.3.1 tcgetattr1.3.2 tcsetattr1.4 示例工程1.5 参考文献1.5.1 stty命令1.5.2 命令格式1.5.2 microcom命令1.5.2.1介绍1.5.2.2指令1.5.3 echo命令1.5.3.1 语法1.5.3.2 选项列表1.5.3.3 使用示例1.5.3.4 e cho > 输出…...

JavaScript动画相关讲解

JavaScript是一种非常流行的脚本语言&#xff0c;广泛应用于Web开发、游戏开发、移动应用开发等领域。在Web开发中&#xff0c;动画效果是非常重要的一部分&#xff0c;可以提高网站的用户体验和吸引力。JavaScript提供了一些基本的动画函数&#xff0c;但是这些函数往往不能满…...

InnoSetup制作安装包(EXE)

功能描述 1.666666.war为项目war包&#xff0c;666666.bat为启动war包脚本&#xff0c;通过InnoSetup将它们打包到安装包666666.exe 2.666666.exe安装包安装过程中将666666.bat注册为自启动服务&#xff0c;安装结束自动执行脚本启动项目666666.war --------------------------…...

CASE WHEN函数语句多条件下使用详解

目录 CASE 的两种格式&#xff1a; 简单CASE函数 和 CASE搜索函数 同时配合 SUM 以及 COUNT 方法的使用 ① SUM函数 ② COUNT函数 CASE WHEN函数语句&#xff0c;实现简单CASE函数和CASE搜索函数两种格式。同时配合 SUM以及COUNT方法的使用 CASE 的两种格式&#xff1a; 简…...

2.31、守护进程(2)

2.31、守护进程&#xff08;2&#xff09;1.守护进程的创建步骤2.什么情况下子进程不会继承父进程的组ID3.哪些操作会导致子进程的组ID发生改变4.kill怎么杀掉守护进程的实现守护进程1.守护进程的创建步骤 执行一个 fork()&#xff0c;之后父进程退出&#xff0c;子进程继续执…...

记录上传文件异常 /tmp/tomcat... (No space left on device)

一&#xff0c;问题描述 用postman调用上传接口&#xff0c;基本每两次调用会有一次报错&#xff0c;如下 {"timestamp": "2023-04-11T03:00:15.4690000","status": 500,"error": "Internal Server Error","exceptio…...

无向连通图中长度为 n 的循环

给定一个无向连通图和一个数字 n,计算图中长度为 n 的循环总数。长度为 n 的圈简单地表示该圈包含 n 个顶点和 n 条边。我们必须计算所有存在的此类循环。 示例: 输入:n = 4 输出:总周期数 = 3 解释 :遵循 3 个独特的循环0 -> 1 -> 2 -> 3 -> 0 0 -> 1 …...

打造出ChatGPT的,是怎样一群人?

震惊世界的ChatGPT&#xff0c;要多少人才能开发出来&#xff1f;几百&#xff0c;还是几千&#xff1f; 答案是&#xff1a;87个人。 老实说&#xff0c;刚看到这个数字真是惊到我了&#xff0c;印象里&#xff0c;之前看媒体报道各大巨头人工智能人才储备时&#xff0c;动辄…...

数据结构——栈与队列相关题目

数据结构——栈与队列相关题目232. 用栈实现队列思路225. 用队列实现栈1.两个队列实现栈2.一个队列实现栈20. 有效的括号思路1047. 删除字符串中的所有相邻重复项思路155. 最小栈150. 逆波兰表达式求值思路239. 滑动窗口最大值单调队列347. 前 K 个高频元素思路232. 用栈实现队…...

Redhat6.7离线安装rabbitmq

一、下载资源文件&#xff08;.rpm文件&#xff09; 链接: https://pan.baidu.com/s/1j2Ze_Jjm0oMrP-r95PPCtA?pwdv3is 提取码: v3is 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 创建rabbit文件夹Mkdir rabbit 三、通过ftp上传文件 四、安装erlang环境 …...

EasyCVR平台基于GB28181协议的语音对讲配置操作教程

EasyCVR基于云边端协同&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;平台可支持海量视频的轻量化接入与汇聚管理&#xff0c;可提供视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、平台级联等功能…...

谷歌发布Self-Debug方法,让大模型学会自己修bug,一次性生成正确代码

文 | 智商掉了一地你有没有想过&#xff0c;让一台计算机诊断和修复自己生成的错误代码&#xff1f;一篇最新的研究论文介绍了一种名为 Self-Debugging 的技术&#xff0c;通过在生成的代码中添加自解释的信息&#xff0c;让计算机像一个可以自己修复代码的程序员一样调试自己的…...

行为型模式-模板方法

行为型模式-模板方法 模板方法(Template Method)解决算法框架问题描述适用环境优点:缺点:违反原则:代码实现模板方法(Template Method) 解决算法框架问题 描述 定义了一个算法的骨架,并将某些步骤延迟到子类中进行实现,从而使得算法的具体实现能够在子类中自由变化…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...