Linux内核中PF_KEY协议族的实现(4)

2023-11-18

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

6. 通知回调处理

6.1 初始化
在pf_key的初始化函数中定义了通知回调结构pfkeyv2_mgr:
err = xfrm_register_km(&pfkeyv2_mgr);
该结构定义如下:
static struct xfrm_mgr pfkeyv2_mgr =
{
.id = "pfkeyv2",
.notify = pfkey_send_notify,
.acquire = pfkey_send_acquire,
.compile_policy = pfkey_compile_policy,
.new_mapping = pfkey_send_new_mapping,
.notify_policy = pfkey_send_policy_notify,
};

6.2 登记
/* net/xfrm/xfrm_state.c */
// 就是把xfrm_mgr结构挂接到xfrm_km_list链表
int xfrm_register_km(struct xfrm_mgr *km)
{
write_lock_bh(&xfrm_km_lock);
// 将结构挂接到xfrm_km_list链表
list_add_tail(&km->list, &xfrm_km_list);
write_unlock_bh(&xfrm_km_lock);
return 0;
}
EXPORT_SYMBOL(xfrm_register_km);

6.3 发送通知
在pf_key中调用以下两个函数来调用通知回调函数, 分别针对安全策略操作和SA操作:
// 安全策略通知回调
void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
{
struct xfrm_mgr *km;
read_lock(&xfrm_km_lock);
// 状态的通知回调函数为notify_policy
list_for_each_entry(km, &xfrm_km_list, list)
if (km->notify_policy)
km->notify_policy(xp, dir, c);
read_unlock(&xfrm_km_lock);
}
// SA状态通知回调
void km_state_notify(struct xfrm_state *x, struct km_event *c)
{
struct xfrm_mgr *km;
read_lock(&xfrm_km_lock);
// 状态的通知回调函数为notify
list_for_each_entry(km, &xfrm_km_list, list)
if (km->notify)
km->notify(x, c);
read_unlock(&xfrm_km_lock);
}

调用这些通知函数前要填写事件的相关参数, 如:
static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
unsigned proto;
struct km_event c;
proto = pfkey_satype2proto(hdr->sadb_msg_satype);
if (proto == 0)
return -EINVAL;
xfrm_state_flush(proto);
// 填写事件参数
// 协议
c.data.proto = proto;
// 序列号
c.seq = hdr->sadb_msg_seq;
// sock对方(用户空间进程)的pid
c.pid = hdr->sadb_msg_pid;
// 事件
c.event = XFRM_MSG_FLUSHSA;
km_state_notify(NULL, &c);
return 0;
}

6.4 pf_key通知回调函数
6.4.1 发送SA消息时的通知回调
// 状态通知回调, 在SA操作后调用
static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c)
{
// 根据事件类型来发送相关通知
switch (c->event) {
case XFRM_MSG_EXPIRE:
// SA到期的通知, SA消息类型为SADB_EXPIRE,只是进行了登记的PF_KEY socket可以收到
return key_notify_sa_expire(x, c);
case XFRM_MSG_DELSA:
case XFRM_MSG_NEWSA:
case XFRM_MSG_UPDSA:
// SA的通知, SA消息类型为分别为SADB_DELETE, SADB_ADD和SADB_UPDATE,
// 所有PF_KEY socket都可以收到
return key_notify_sa(x, c);
case XFRM_MSG_FLUSHSA:
// 删除全部SA的通知, SA消息类型为分别为SADB_FLUSH, 所有PF_KEY socket都可以收到
return key_notify_sa_flush(c);
case XFRM_MSG_NEWAE: /* not yet supported */
break;
default:
printk("pfkey: Unknown SA event %d\n", c->event);
break;
}
return 0;
}

6.4.2 发送ACQUIRE

关于ACQUIRE的作用, 摘一段UNP vol.1, r3中的话:
chapter19.5:
"When the kernel needs to communicate with a peer and policy says that an SA is required but one is not available, the kernel sends an SADB_ACQUIRE message to key management sockets that have registered the SA type required, containing a proposal extension describing the kernel's proposed algorithms and key lengths. The proposal may be a combination of what is supported by the system and preconfigured policy that limits what is permitted for this communication. The proposal is a list of algorithms, key lengths, and lifetimes, in order of preference. When a key management daemon receives an SADB_ACQUIRE message, it performs the acts required to choose a key that fits one of the kernel's proposed combinations, and installs this key in the kernel. "

static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp, int dir)
{
struct sk_buff *skb;
struct sadb_msg *hdr;
struct sadb_address *addr;
struct sadb_x_policy *pol;
struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
int sockaddr_size;
int size;
struct sadb_x_sec_ctx *sec_ctx;
struct xfrm_sec_ctx *xfrm_ctx;
int ctx_size = 0;

sockaddr_size = pfkey_sockaddr_size(x->props.family);
if (!sockaddr_size)
return -EINVAL;
// 消息长度包括SA基本头, 两个SA地址, 两个网络地址和策略
size = sizeof(struct sadb_msg) +
(sizeof(struct sadb_address) * 2) +
(sockaddr_size * 2) +
sizeof(struct sadb_x_policy);
// 还添加AH或ESP中所有算法描述的长度
if (x->id.proto == IPPROTO_AH)
size += count_ah_combs(t);
else if (x->id.proto == IPPROTO_ESP)
size += count_esp_combs(t);
if ((xfrm_ctx = x->security)) {
ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len);
size += sizeof(struct sadb_x_sec_ctx) + ctx_size;
}
// 分配skb
skb = alloc_skb(size + 16, GFP_ATOMIC);
if (skb == NULL)
return -ENOMEM;
// 先填基本SA消息头信息
hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
hdr->sadb_msg_version = PF_KEY_V2;
hdr->sadb_msg_type = SADB_ACQUIRE;
hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
hdr->sadb_msg_len = size / sizeof(uint64_t);
hdr->sadb_msg_errno = 0;
hdr->sadb_msg_reserved = 0;
hdr->sadb_msg_seq = x->km.seq = get_acqseq();
hdr->sadb_msg_pid = 0;
/* src address */
// 填充SA源地址信息
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
addr->sadb_address_proto = 0;
addr->sadb_address_reserved = 0;
if (x->props.family == AF_INET) {
// IPV4地址
addr->sadb_address_prefixlen = 32;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->props.saddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
// IPV6地址
addr->sadb_address_prefixlen = 128;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr,
x->props.saddr.a6, sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();

/* dst address */
// 填充SA目的地址信息
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = 0;
addr->sadb_address_reserved = 0;
if (x->props.family == AF_INET) {
addr->sadb_address_prefixlen = 32;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->id.daddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
addr->sadb_address_prefixlen = 128;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr,
x->id.daddr.a6, sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();
// 填充策略信息
pol = (struct sadb_x_policy *) skb_put(skb, sizeof(struct sadb_x_policy));
pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
pol->sadb_x_policy_dir = dir+1;
pol->sadb_x_policy_id = xp->index;
/* Set sadb_comb's. */
// 填充AH或ESP的可用算法信息
if (x->id.proto == IPPROTO_AH)
dump_ah_combs(skb, t);
else if (x->id.proto == IPPROTO_ESP)
dump_esp_combs(skb, t);
/* security context */
if (xfrm_ctx) {
// 填充安全上下文信息
sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb,
sizeof(struct sadb_x_sec_ctx) + ctx_size);
sec_ctx->sadb_x_sec_len =
(sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t);
sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi;
sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg;
sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len;
memcpy(sec_ctx + 1, xfrm_ctx->ctx_str,
xfrm_ctx->ctx_len);
}
// 广播到进行了登记的PF_KEY套接口
return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
}

6.4.3 编译策略
// 将sadb_x_policy(标准接口格式)编译为xfrm_policy(内核具体实现格式)
static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
u8 *data, int len, int *dir)
{
struct xfrm_policy *xp;
struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
struct sadb_x_sec_ctx *sec_ctx;
// 选项opt必须是IP_IPSEC_POLICY, 否则出错
switch (sk->sk_family) {
case AF_INET:
if (opt != IP_IPSEC_POLICY) {
*dir = -EOPNOTSUPP;
return NULL;
}
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
if (opt != IPV6_IPSEC_POLICY) {
*dir = -EOPNOTSUPP;
return NULL;
}
break;
#endif
default:
*dir = -EINVAL;
return NULL;
}
*dir = -EINVAL;
if (len < sizeof(struct sadb_x_policy) ||
pol->sadb_x_policy_len*8 > len ||
pol->sadb_x_policy_type > IPSEC_POLICY_BYPASS ||
(!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
return NULL;
// 分配策略空间
xp = xfrm_policy_alloc(GFP_ATOMIC);
if (xp == NULL) {
*dir = -ENOBUFS;
return NULL;
}
// 填写策略动作
xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ?
XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
// 填写策略有效期参数
xp->lft.soft_byte_limit = XFRM_INF;
xp->lft.hard_byte_limit = XFRM_INF;
xp->lft.soft_packet_limit = XFRM_INF;
xp->lft.hard_packet_limit = XFRM_INF;
xp->family = sk->sk_family;
xp->xfrm_nr = 0;
// 解析ipsec请求
if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
(*dir = parse_ipsecrequests(xp, pol)) < 0)
goto out;
/* security context too */
if (len >= (pol->sadb_x_policy_len*8 +
sizeof(struct sadb_x_sec_ctx))) {
// 转换安全上下文
char *p = (char *)pol;
struct xfrm_user_sec_ctx *uctx;
p += pol->sadb_x_policy_len*8;
sec_ctx = (struct sadb_x_sec_ctx *)p;
if (len < pol->sadb_x_policy_len*8 +
sec_ctx->sadb_x_sec_len) {
*dir = -EINVAL;
goto out;
}
if ((*dir = verify_sec_ctx_len(p)))
goto out;
uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx);
*dir = security_xfrm_policy_alloc(xp, uctx);
kfree(uctx);
if (*dir)
goto out;
}
*dir = pol->sadb_x_policy_dir-1;
return xp;
out:
security_xfrm_policy_free(xp);
kfree(xp);
return NULL;
}
6.4.4 NAT穿越映射
// 发送新映射(NAT穿越)SA消息, 包含SA头, SA, 转换前地址,端口, 转换后的地址端口
static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport)
{
struct sk_buff *skb;
struct sadb_msg *hdr;
struct sadb_sa *sa;
struct sadb_address *addr;
struct sadb_x_nat_t_port *n_port;
struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
int sockaddr_size;
int size;
__u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0);
struct xfrm_encap_tmpl *natt = NULL;
// 协议的地址长度
sockaddr_size = pfkey_sockaddr_size(x->props.family);
if (!sockaddr_size)
return -EINVAL;
if (!satype)
return -EINVAL;
if (!x->encap)
return -EINVAL;
// NAT转换结构
natt = x->encap;
/* Build an SADB_X_NAT_T_NEW_MAPPING message:
*
* HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) |
* ADDRESS_DST (new addr) | NAT_T_DPORT (new port)
*/
// 消息总长: SA消息头+SA+两个SA地址+两个协议地址+2个NAT端口
size = sizeof(struct sadb_msg) +
sizeof(struct sadb_sa) +
(sizeof(struct sadb_address) * 2) +
(sockaddr_size * 2) +
(sizeof(struct sadb_x_nat_t_port) * 2);
// 分配skb
skb = alloc_skb(size + 16, GFP_ATOMIC);
if (skb == NULL)
return -ENOMEM;
// 填写SA头
hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
hdr->sadb_msg_version = PF_KEY_V2;
hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING;
hdr->sadb_msg_satype = satype;
hdr->sadb_msg_len = size / sizeof(uint64_t);
hdr->sadb_msg_errno = 0;
hdr->sadb_msg_reserved = 0;
hdr->sadb_msg_seq = x->km.seq = get_acqseq();
hdr->sadb_msg_pid = 0;
/* SA */
// 填写SA结构
sa = (struct sadb_sa *) skb_put(skb, sizeof(struct sadb_sa));
sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t);
sa->sadb_sa_exttype = SADB_EXT_SA;
sa->sadb_sa_spi = x->id.spi;
sa->sadb_sa_replay = 0;
sa->sadb_sa_state = 0;
sa->sadb_sa_auth = 0;
sa->sadb_sa_encrypt = 0;
sa->sadb_sa_flags = 0;
/* ADDRESS_SRC (old addr) */
// 转换前SA地址参数
addr = (struct sadb_address*)
skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
addr->sadb_address_proto = 0;
addr->sadb_address_reserved = 0;
if (x->props.family == AF_INET) {
// 填写转换前IPV4协议地址具体参数
addr->sadb_address_prefixlen = 32;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->props.saddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
// 填写转换前IPV6协议地址具体参数
addr->sadb_address_prefixlen = 128;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr,
x->props.saddr.a6, sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();
/* NAT_T_SPORT (old port) */
// 填写转换前端口参数
n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
n_port->sadb_x_nat_t_port_port = natt->encap_sport;
n_port->sadb_x_nat_t_port_reserved = 0;
/* ADDRESS_DST (new addr) */
// 填写转换后地址属性参数
addr = (struct sadb_address*)
skb_put(skb, sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = 0;
addr->sadb_address_reserved = 0;
if (x->props.family == AF_INET) {
// 填写转换后IPV4协议地址具体参数
addr->sadb_address_prefixlen = 32;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ipaddr->a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
// 填写转换后IPV6协议地址具体参数
addr->sadb_address_prefixlen = 128;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, &ipaddr->a6, sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();
/* NAT_T_DPORT (new port) */
// 填写转换前端口参数
n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port));
n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t);
n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
n_port->sadb_x_nat_t_port_port = sport;
n_port->sadb_x_nat_t_port_reserved = 0;
// 发送给进行登记了的PF_KEY套接口
return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
}

6.4.5 发送策略的通知

// 策略通知回调, 在安全策略操作后调用
static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
{
if (xp && xp->type != XFRM_POLICY_TYPE_MAIN)
return 0;
switch (c->event) {
case XFRM_MSG_POLEXPIRE:
// 策略到期通知, 空函数
return key_notify_policy_expire(xp, c);
case XFRM_MSG_DELPOLICY:
case XFRM_MSG_NEWPOLICY:
case XFRM_MSG_UPDPOLICY:
// 策略的通知, SA消息类型为分别为SADB_X_SPDDELETE, SADB_X_SPDADD, SADB_X_SPDUPDATE等,
// 所有PF_KEY socket都可以收到
return key_notify_policy(xp, dir, c);
case XFRM_MSG_FLUSHPOLICY:
if (c->data.type != XFRM_POLICY_TYPE_MAIN)
break;
// 策略的通知, SA消息类型为分别为SADB_X_FLUSH, 所有PF_KEY socket都可以收到
return key_notify_policy_flush(c);
default:
printk("pfkey: Unknown policy event %d\n", c->event);
break;
}
return 0;
}

在pf_key.c中只用到了6.4.1和6.4.5的这两个函数.

...... 待续 ......
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux内核中PF_KEY协议族的实现(4) 的相关文章

  • 如何在 Python 中将列表变量传递给 subprocess.call 命令

    我有一个清单 apps apps append wq35a5huqlja45jsyukrpmwuiayovrmh apps append q7mimvgduueernwvw4y22t5huemykntw apps append pmudbp
  • 无法在 Linux 中阻止从命名管道 (FIFO) 读取

    很奇怪 我似乎无法完成这项工作 这是我的架构 我有一个命名管道 它将在永远运行 root读取器进程和多个应用程序编写器进程 读者进程必须是blocking当作家们在nonblocking 因此 这就是我在阅读器进程中所做的 该进程将运行ro
  • 使用 sed 和 pstree 显示正在使用的终端类型

    我一直在尝试仅显示用作名称的终端类型 例如 如果我使用 konsole 它会显示 konsole 到目前为止我一直在使用这个命令 pstree A s 输出这个 systemd konsole bash pstree 我有以下内容可以从该行
  • 什么是 Linux 上易于使用的 C++ 分析器? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我需要分析一些在 Linux 上运行 C 的代码 你们能推荐一些分析器吗 使用 gprof 只需编译 pg标志 我认为 但不确定 你必须关
  • Python:文本覆盖在所有窗口顶部,包括 Linux 中的全屏

    我正在尝试用 python 编写一个简单的脚本 在所有窗口和全屏应用程序之上输出文本 该脚本的目的是以类似于 Steam FPS 计数器工作方式的方式输出平均负载和可用内存 以及其他有用的统计数据 到目前为止 我尝试了 pygame 但据我
  • 编辑 CMakeLists.txt 以使用 -fPIC 进行编译

    我正在尝试为名为 libnifalcon 的 NOVINT Falcon 安装驱动程序 我使用 cmake 创建 make 文件 但是当我运行 make 时出现错误 Linking CXX shared library lib libnif
  • Python 套接字库认为套接字未打开时已打开

    我正在使用一些Python 如下所示 HOST 127 0 0 1 PORT 43434 single socket socket socket AF INET socket SOCK STREAM try single bind HOST
  • 减少内存分配GCC命令

    今天 在我的计算机科学课上 我被告知我可以调整程序在编译期间可以分配的内存量 使用 GCC Linux 该数量默认设置为最佳模式 这意味着尽可能多 在调试应用程序期间 我可以从这个编译器功能中受益匪浅 因为我需要正确处理分配错误 这在我的
  • 如何在 ssh 命令中使用长输入参数正确转义 qsub 命令?

    我有一个复杂的 qsub 命令可以远程运行 PROJECT NAME TEXT TEST PROJECT PACK ORGANIZATION source organization MY ORGANIZATION CONTACT NAME
  • Linux 上的 头文件在哪里?为什么我找不到 ? [复制]

    这个问题在这里已经有答案了 可能的重复 如何在Linux中实现C的getch 函数 https stackoverflow com questions 3276546 how to implement getch function of c
  • 使用端口 80 (Ubuntu / Linode) 运行 Node.js 的最佳实践 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我正在设置我的第一个Node js服务器上的cloud Linux node我对以下细节还很陌生Linux admin 顺便说一句 我并没有尝试
  • 哪个信号被传递到信号处理程序中死锁的进程

    我有一个来自调用信号处理程序后死锁的进程的核心转储 如何确定传送了哪个信号以及是谁发送的 GDB 为接收信号的线程生成的回溯如下 信号处理程序在第 15 帧中被调用 gdb bt 0 0x00007fa9c204654b in sys fu
  • 如何在没有 root 访问权限的情况下在 Ubuntu 上安装 Google Test?

    我正在尝试根据以下方式安装 Google Test这个答案 https stackoverflow com a 21314020 6560773在没有 root 访问权限的 Ubuntu 上 因为我需要在工作中学习和使用它 设法在我自己的用
  • 获取后台进程的退出代码

    我有一个从我的主 bourne shell 脚本中调用的命令 CMD 该命令需要很长时间 我想修改脚本如下 作为后台进程并行运行命令 CMD CMD 在主脚本中 有一个循环每隔几秒监视生成的命令 该循环还向标准输出回显一些消息 指示脚本的进
  • 产生并处理软件中断

    有人可以告诉我如何在Linux下生成软件中断然后用request irq处理它吗 或者也许这是不可能的 您可以使用软中断来代替 您可以通过编辑 include linux interrupt h 来定义您的 sofirq 然后使用函数 ra
  • x86-64 上这个语句有什么问题?

    该函数的目的是获取堆栈的起始地址 unsigned long find start void asm movq rsp eax 当我编译它时 出现错误 Error suffix or operands invalid for movq mo
  • 由于参数中有空格,Bash 脚本因未知选项而失败

    我正在尝试运行 aws create lambda 函数 事情的经过如下 eval aws lambda create function function name FUNCTION NAME runtime RUNTIME role RO
  • Bash 变量:区分大小写?

    Bash shell 脚本区分大小写吗 是可变的date与DATE 是的 它区分大小写 就像 UNIX 的其余部分一样 date and DATE是两个不同的变量 makefile and Makefile是两个不同的文件 h and H是
  • 使用Linux虚拟鼠标驱动

    我正在尝试实施一个虚拟鼠标驱动程序根据基本 Linux 设备驱动程序书 有一个用户空间应用程序 它生成坐标以及内核模块 See 虚拟鼠标驱动程序和用户空间应用程序代码 http www embeddedlinux org cn Essent
  • Kubernetes Pod 已终止 - 退出代码 137

    我需要一些关于 k8s 1 14 和在其上运行 gitlab 管道所面临的问题的建议 许多作业都会抛出退出代码 137 错误 我发现这意味着容器突然终止 集群信息 库伯内特版本 1 14 使用的云 AWS EKS 节点 C5 4xLarge

随机推荐

  • pycharm如何配置Anaconda虚拟环境

    文章目录 一 Anaconda虚拟环境创建 二 pycharm添加虚拟环境 一 Anaconda虚拟环境创建 1 此电脑 右键 属性 高级系统设置 高级 环境变量 系统变量 path 新建 相应路径 Anaconda 相应路径 Anacon
  • 小程序和Vue写法的区别

    小程序和Vue写法的区别主要有以下几点 语法不同 小程序使用的是WXML WXSS和JS 而Vue使用的是HTML CSS和JSX 数据绑定方式不同 小程序使用的是双向数据绑定 而Vue使用的是单向数据流 1 在小程序中需要使用e curr
  • oracle数据库imp/sqlplus命令无效引发的问题

    好久没有使用Oracle数据库 在导入数据库dmp文件时出现imp命令无效 oracle导入dmp文件命令 imp user password ip 端口 server name file 文件路径 dmp full y 如 imp crm
  • HTML 常用快捷键,HTML介绍

    一 1 修改主题 2 3 修改 4 长代码换行 file setting general wrap 对勾选中 5 新建项目 file new project 6 关联浏览器 file setting tool web borther 复制路
  • bigdata_git版本控制系统

    一丶版本控制系统发展 集中式VCS 分布式VCS git 二丶git工作流程图 三丶分支管理 每个项目确立后可以添加多个分支 分支可以更新版本 只要分支没有合并提交 对其他人没有任何影响 这也是跟svn的不同 四丶内部数据存储方式 git统
  • 惠斯通电桥与运算放大器的输入失调电流和输入偏置电流

    在做数字开关气压表项目中 使用的气压传感器的结构是惠斯通电桥 输出差分信号 差分电压与气压大小成线性关系 运放的失调电压对精度影响很大 在这里考虑选择使用低漂移运放 在选择运放时考虑了输入电阻 失调电压的影响 如果运放的输入电阻大小与电桥电
  • 大模型训练避坑指南

    原文 https baijiahao baidu com s id 1760862056681517207 wfr spider for pc 自 2022 年 11 月底 ChatGPT 发布以来 大模型的热度持续发酵 相信高屋建瓴的讨论
  • 辛普森悖论_所谓的辛普森悖论

    辛普森悖论 We all know the Simpsons family from Disneyland but have you heard about the Simpson s Paradox from statistic theo
  • cdn缓存服务器有网站图片,CDN缓存服务器图片存储一致性hash算法的理解

    用hash做缓存 假如有三台服务器 1 2 3 有三万张图片 我想将图片平均缓存到我三台服务器上 一个服务器大概一万张 怎么去实现这个办法呢 可以用hash来取余数进行操作 加入我们是以图片的名字作为key进行hash计算 hash 图片名
  • C - 滑动窗口 /【模板】单调队列

    Description 有一个长为 n 的序列 a 以及一个大小为 k 的窗口 现在这个从左边开始向右滑动 每次滑动一个单位 求出每次滑动后窗口中的最大值和最小值 例如 The array is 1 3 1 3 5 3 6 7 and k
  • .whl is not a supported wheel on this platform的原因及其解决办法

    在PIP安装 whl文件的时候碰到这个错误 具体如下 我的python版本是3 4 4 这个错误的原因如下 可能的原因1 安装的不是对应python版本的库 下载的库名中cp27代表python2 7 其它同理 可能的原因2 下载的是对应版
  • IDEA写SQL语句时不会提示表名、列名的处理方法(实测有效)

    打出表名没有提示 下面进行设置 按照别人的设置没效果 打开设置 还是刚才的路径 要同时设置两个地方才有效
  • 《Netty实战》读书笔记

    第一章 Netty 异步和事件驱动 Netty包含网络编程 多线程处理和并发 NIO NIO 代表非阻塞 I O Non blocking I O Netty 的核心组件 Netty 的主要构件 Channel 回调 Future 事件和
  • SSM商城项目实战总结

    SSM商城项目实战总结 编程思想是指在软件开发过程中 程序员所遵循的一种思维模式或方法论 它是指导程序员如何组织和解决问题的一种思考方式 下面是对常见的编程思想进行的总结 面向对象编程 OOP 面向对象编程是一种将数据和操作数据的方法组合在
  • ADS使用J-LINK调试之配置方法

    ADS使用J LINK调试之配置方法 1 安装好ADS1 2及J LINK驱动文件 2 ADS配置 随便打开项目 进入AXD调试界面 在AXD中 选择 3 进入以下界面 选择 Add 进入J LINK安装目录下 添加 JLinkRDI dl
  • ubuntu终止terminal中下载任务以及继续下载

    ctrl c是终止正在下载的任务 wget c URL是继续刚才终止的那个任务
  • 22 年国内最牛的 Java 面试八股文合集(全彩版),不接受反驳

    很多小伙伴从四月份就开始准备面试了 截止现在已经过去 2 个多月的时间 显然这段时间的准备没有白费 很多小伙伴都报喜 成功拿到了 XX 公司的 Offer 下面我把这段时间给小伙伴本准备的 Java 面试八股文合集 全彩版 拿出来 来帮助没
  • Python 字符串拼接 ‘+=‘ 和 ‘join()‘ 谁的速度更快?

    一 字符串拼接的两种方法 程序当中经常出现需要不断接收新字符串并将这些字符串组成新字符串输出的情况 该方法一般有两种解决方案 创建一个空字符串 test str 将每次新传入的 new str 使用 test str new str 的方式
  • 面试题 02.07. 链表相交

    给你两个单链表的头节点 headA 和 headB 请你找出并返回两个单链表相交的起始节点 如果两个链表没有交点 返回 null 图示两个链表在节点 c1 开始相交 来源 力扣 LeetCode 链接 https leetcode cn p
  • Linux内核中PF_KEY协议族的实现(4)

    本文档的Copyleft归yfydz所有 使用GPL发布 可以自由拷贝 转载 转载时请保持文档的完整性 严禁用于任何商业用途 msn yfydz no1 hotmail com 来源 http yfydz cublog cn 6 通知回调处