netlink介绍

一般来说用户空间和内核空间的通信方式有很多种,而Netlink可以实现双工通信。

Netlink套接字是用以实现用户进程内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。

在Linux 内核中,使用netlink 进行应用与内核通信的应用有很多,如:

  • 路由 daemon(NETLINK_ROUTE)
  • 用户态 socket 协议(NETLINK_USERSOCK)
  • 防火墙(NETLINK_FIREWALL)
  • netfilter 子系统(NETLINK_NETFILTER)
  • 内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)
  • 通用netlink(NETLINK_GENERIC)

Netlink 相对于系统调用ioctl 以及 /proc文件系统而言,具有以下优点:

  • netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,(如 #define NETLINK_MSG_FOR_SCHIPS 20 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换)
  • netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息
  • 使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖
  • netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性
  • 内核可以使用 netlink 首先发起会话

Netlink协议基于BSD socket和AF_NETLINK地址簇,使用32位的端口号寻址,每个Netlink协议通常与一个或一组内核服务/组件相关联,如NETLINK_ROUTE用于获取和设置路由与链路信息、NETLINK_KOBJECT_UEVENT用于内核向用户空间的udev进程发送通知等。

接口

用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 netlink。

数据定义

宏定义

#define NLMSG_ALIGNTO   4U
/* 宏NLMSG_ALIGN(len)用于得到不小于len且字节对齐的最小数值 */
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) /* Netlink 头部长度 */
#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) /* 计算消息数据len的真实消息长度(消息体 + 消息头)*/
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) /* 宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值 */
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) /* 宏NLMSG_DATA(nlh)用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 */
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) /* 宏NLMSG_NEXT(nlh,len)用于得到下一个消息的首地址, 同时len 变为剩余消息的长度 */
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) /* 判断消息是否 >len */
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
(nlh)->nlmsg_len <= (len)) /* NLMSG_PAYLOAD(nlh,len) 用于返回payload的长度*/
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

用户态数据结构

用户态应用使用标准的 socket API有sendto()recvfrom(), sendmsg(), recvmsg()

Netlink通信跟常用UDP Socket通信类似,struct sockaddr_nl是netlink通信地址,跟普通socket struct sockaddr_in类似。

sockaddr_nl
struct sockaddr_nl {
__kernel_sa_family_t nl_family; /* AF_NETLINK (跟AF_INET对应)*/
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID (通信端口号)*/
__u32 nl_groups; /* multicast groups mask */
};
nlmsghdr

struct nlmsghdr是netlink提供的协议头,netlink协议是面向消息的,需要定义自己的协议。自定义协议按照协议头格式填充协议头内容,并定义自己的payload,通常自定义的协议体包含自定义协议头与额外的属性。

/* struct nlmsghd 是netlink消息头*/
struct nlmsghdr {
__u32 nlmsg_len; /* 整个netlink消息的长度,包含消息头。 */
__u16 nlmsg_type; /* Message content */
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process port ID */
};

解析:

  • nlmsg_type:消息状态,内核在include/uapi/linux/netlink.h中定义了以下4种通用的消息类型:
#define NLMSG_NOOP      0x1 /* Nothing. 不执行任何动作,必须将该消息丢弃。 */
#define NLMSG_ERROR 0x2 /* Error 消息发生错误。 */
#define NLMSG_DONE 0x3 /* End of a dump标识分组消息的末尾。 */
#define NLMSG_OVERRUN 0x4 /* Data lost 缓冲区溢出,表示某些消息已经丢失。 */ #define NLMSG_MIN_TYPE 0x10/* < 0x10: reserved control messages */
  • nlmsg_flags:消息标记,表示消息的类型,如下:
/* Flags values */
#define NLM_F_REQUEST 1 /* It is request message. */
#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */
#define NLM_F_ECHO 8 /* Echo this request */
#define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */ /* Modifiers to GET request */
#define NLM_F_ROOT 0x100 /* specify tree root */
#define NLM_F_MATCH 0x200 /* return all matching */
#define NLM_F_ATOMIC 0x400 /* atomic GET */
#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) /* Modifiers to NEW request */
#define NLM_F_REPLACE 0x100 /* Override existing */
#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
#define NLM_F_APPEND 0x800 /* Add to end of list */
  • nlmsg_seq:消息序列号,用以将消息排队有些类似TCP协议中的序号(不完全一样),可选,不强制使用。
  • nlmsg_pid:发送端口的ID号,对于内核来说该值就是0,对于用户进程来说就是其socket所绑定的ID号。
msghdr
struct iovec {                    /* Scatter/gather array items */
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
/* iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度 (即有多少个buff) */
struct msghdr {
void *msg_name; /* optional address */
socklen_t msg_namelen; /* size of address */
struct iovec *msg_iov; /* scatter/gather array */
size_t msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data, see below */
size_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags on received message */
};

内核数据结构

netlink_kernel_cfg
/* optional Netlink kernel configuration parameters */
struct netlink_kernel_cfg {
unsigned int groups;
unsigned int flags;
void (*input)(struct sk_buff *skb); /* input 回调函数 */
struct mutex *cb_mutex;
void (*bind)(int group);
bool (*compare)(struct net *net, struct sock *sk);
};
netlink消息类型
#define NETLINK_ROUTE       0   /* Routing/device hook              */
#define NETLINK_UNUSED 1 /* Unused number */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* auditing */
#define NETLINK_FIB_LOOKUP 10
#define NETLINK_CONNECTOR 11
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
#define NETLINK_RDMA 20
#define NETLINK_CRYPTO 21 /* Crypto layer */
#define NETLINK_SMC 22 /* SMC monitoring * #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG #define MAX_LINKS 32

函数

netlink 内核函数

创建内核socket
static inline struct sock *
netlink_kernel_create(struct net *net,
int unit,
struct netlink_kernel_cfg *cfg) /*
net: 所在的网络命名空间, 一般默认为 init_net 是内核定义的变量;
init_net 来自 net_namespace.c(extern struct net init_net)
貌似是不建议使用init_net的,但对于测试足够了 unit:netlink协议类型 cfg: netlink内核配置参数 */

一般在模块初始化函数中使用:

struct sock *nlsk = NULL; // 全局变量
static void netlink_rcv_msg(struct sk_buff *skb); // 接收消息时的回调函数
#define NETLINK_MSG_FOR_SCHIPS 30 // 指定接收时的回调函数
struct netlink_kernel_cfg cfg = {
.input = netlink_rcv_msg, /* set recv callback */
}; /* create netlink socket */
nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_MSG_FOR_SCHIPS, &cfg);
if(nlsk == NULL)
{
printk("netlink_kernel_create error !\n");
return -1;
}
// ...

最多可以定义32种类型,若 uint > MAX_LINKS 则该函数返回NULL,源代码片段如下:

// kernel/net/netlink/af_netlink.c

__netlink_kernel_create(struct net *net, int unit, struct module *module,
struct netlink_kernel_cfg *cfg)
{
struct socket *sock;
struct sock *sk;
struct netlink_sock *nlk;
struct listeners *listeners = NULL;
struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL;
unsigned int groups; BUG_ON(!nl_table); if (unit < 0 || unit >= MAX_LINKS)
return NULL;
// ...
}
接收消息

作为回调函数被调用的。

static void netlink_rcv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh = NULL;
char *umsg = NULL;
char *kmsg = "hello users!!!"; if(skb->len >= nlmsg_total_size(0))
{
nlh = nlmsg_hdr(skb);
umsg = NLMSG_DATA(nlh);
if(umsg) // 如何处理要根据实际的数据结构来定义
{
printk("kernel recv from user: %s\n", umsg);
}
}
}
单播和多播

单播就是点对点发送的意思,作用相当于用户函数中的sendto

/* 发送单播消息 */
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
/*
ssk: netlink socket
skb: skb buff 指针
portid: 通信的端口号
nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用定时睡眠
*/ /* 发送多播消息 */
extern int netlink_broadcast(struct sock *ssk,
struct sk_buff *skb,
__u32 portid,
__u32 group,
gfp_t allocation);
/*
ssk: 同上(对应netlink_kernel_create 返回值)、
skb: 内核skb buff
portid: 端口id
group: 是所有目标多播组对应掩码的"OR"操作的合值。
allocation: 指定内核内存分配方式,通常GFP_ATOMIC用于中断上下文,而GFP_KERNEL用于其他场合。这个参数的存在是因为该API可能需要分配一个或多个缓冲区来对多播消息进行clone
*/

一般这么用:

int send_usrmsg(char *pbuf, uint16_t len)
{
struct sk_buff *nl_skb;
struct nlmsghdr *nlh; int ret; /* 创建sk_buff 空间 */
nl_skb = nlmsg_new(len, GFP_ATOMIC);
if(!nl_skb)
{
printk("netlink alloc failure\n");
return -1;
} /* 设置netlink消息头部 */
nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_MSG_FOR_SCHIPS, len, 0);
if(nlh == NULL)
{
printk("nlmsg_put failaure \n");
nlmsg_free(nl_skb);
return -1;
} /* 拷贝数据发送 */
memcpy(nlmsg_data(nlh), pbuf, len);
ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT);
// nlmsg_free(nl_skb); 注意,不需要free(内核会做这件事情)
return ret;
}

netlink 用户函数

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
创建用户socket
int socket(int domain, int type, int protocol);

在netlink 中,一般使用以下的方式:

#define NETLINK_MSG_FOR_SCHIPS 30 // 创建自己的消息类型
int skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_MSG_FOR_SCHIPS);
// ...
绑定地址
int bind(int sockfd,
const struct sockaddr *addr,
socklen_t addrlen);

在netlink 中,一般使用以下的方式:

#define USER_PORT 123
struct sockaddr_nl bind_addr;
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.nl_family = AF_NETLINK; //AF_NETLINK
bind_addr.nl_pid = USER_PORT; //端口号(port ID)
bind_addr.nl_groups = 0;
bind(skfd, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
// ...
发送数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen); #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))

在netlink 中,一般使用以下的方式:

struct nlmsghdr *nlh = NULL;
struct sockaddr_nl bind_addr; // 在上面已经初始化过了
struct sockaddr_nl dest_addr;
char *umsg = "hello netlink!!"; memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; // to kernel
dest_addr.nl_groups = 0; nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
memset(nlh, 0, sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
nlh->nlmsg_flags = 0;
nlh->nlmsg_type = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = bind_addr.nl_pid; //self port
memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl));
free(nlh); // 只是为了防止内存泄漏
// ...
接收数据
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);

在netlink 中,一般使用以下的方式:

user_msg_info u_info;
socklen_t len; memset(&u_info, 0, sizeof(u_info));
len = sizeof(struct sockaddr_nl);
ret = recvfrom(skfd, &u_info, sizeof(user_msg_info), 0, (struct sockaddr *)&dest_addr, &len);

总结

使用netlink时需要定义好消息类型,socket的端口id(pid);如果需要使用多播,则还需要指定nl_groups。

实际通信时,需要内核先配置好;应用程序再创建对应的socket。

发送和接收需要使用netlink 的一些宏进行处理。

实例

用户态程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h> /********* NETLINK 传输有关定义*********/
// 消息类型
#define NETLINK_MSG_FOR_SCHIPS 30 // 不超过32
// 端口号
#define USER_PORT 123 #define MSG_LEN 125
#define MAX_PLOAD MSG_LEN typedef struct _user_msg_info
{
struct nlmsghdr hdr;
char msg[MSG_LEN];
} user_msg_info; int main(int argc, char **argv)
{
int skfd;
int ret;
user_msg_info u_info;
socklen_t len;
struct nlmsghdr *nlh = NULL;
struct sockaddr_nl bind_addr;
struct sockaddr_nl dest_addr;
char *umsg = "hello netlink!!"; /* 创建NETLINK socket */
skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_MSG_FOR_SCHIPS);
if(skfd == -1)
{
perror("create socket error");
return -1;
} memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.nl_family = AF_NETLINK; //AF_NETLINK
bind_addr.nl_pid = USER_PORT; //端口号(port ID)
bind_addr.nl_groups = 0;
if(bind(skfd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0)
{
perror("bind() error\n");
close(skfd);
return -1;
} memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; // to kernel
dest_addr.nl_groups = 0; nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
memset(nlh, 0, sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
nlh->nlmsg_flags = 0;
nlh->nlmsg_type = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = bind_addr.nl_pid; //self port memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl));
if(!ret)
{
perror("sendto error");
close(skfd);
exit(-1);
}
printf("send kernel:%s\n", umsg); memset(&u_info, 0, sizeof(u_info));
len = sizeof(struct sockaddr_nl);
ret = recvfrom(skfd, &u_info, sizeof(user_msg_info), 0, (struct sockaddr *)&dest_addr, &len);
if(!ret)
{
perror("recv form kernel error");
close(skfd);
exit(-1);
} printf("from kernel:%s\n", u_info.msg);
close(skfd); free((void *)nlh);
return 0;
}

内核模块程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/netlink.h> // 消息类型
#define NETLINK_MSG_FOR_SCHIPS 30 // 不超过32
// 端口号
#define USER_PORT 123
// 消息长度限制
#define MSG_LEN 125
#define MAX_PLOAD MSG_LEN struct sock *nlsk = NULL;
extern struct net init_net; static void netlink_rcv_msg(struct sk_buff *skb);
int send_usrmsg(char *pbuf, uint16_t len); struct netlink_kernel_cfg cfg = {
.input = netlink_rcv_msg, /* set recv callback */
}; int send_usrmsg(char *pbuf, uint16_t len)
{
struct sk_buff *nl_skb;
struct nlmsghdr *nlh; int ret; /* 创建sk_buff 空间 */
nl_skb = nlmsg_new(len, GFP_ATOMIC);
if(!nl_skb)
{
printk("netlink alloc failure\n");
return -1;
} /* 设置netlink消息头部 */
nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_MSG_FOR_SCHIPS, len, 0);
if(nlh == NULL)
{
printk("nlmsg_put failaure \n");
nlmsg_free(nl_skb);
return -1;
} /* 拷贝数据发送 */
memcpy(nlmsg_data(nlh), pbuf, len);
ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT);
// nlmsg_free(nl_skb); 发送的skb不需要内核模块去释放,也不能释放,否则会崩溃。内核会处理skb的释放,所以不会出现内存泄露问题
return ret;
} static void netlink_rcv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh = NULL;
char *umsg = NULL;
char *kmsg = "hello users!!!"; if(skb->len >= nlmsg_total_size(0))
{
nlh = nlmsg_hdr(skb);
umsg = NLMSG_DATA(nlh);
if(umsg)
{
printk("kernel recv from user: %s\n", umsg);
send_usrmsg(kmsg, strlen(kmsg));
}
}
} int test_netlink_init(void)
{
/* create netlink socket */
nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_MSG_FOR_SCHIPS, &cfg);
if(nlsk == NULL)
{
printk("netlink_kernel_create error !\n");
return -1;
}
printk("test_netlink_init\n"); return 0;
} void test_netlink_exit(void)
{
if (nlsk){
netlink_kernel_release(nlsk); /* release ..*/
nlsk = NULL;
}
printk("test_netlink_exit!\n");
} module_init(test_netlink_init);
module_exit(test_netlink_exit);

Makeflie

适用于Ubuntu,其他环境没有测试。

# Makefile for netlink

MODULE_NAME := nl
obj-m :=$(MODULE_NAME).o KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd) all:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean

运行结果

步骤:

  • 先将编译出来的Netlink内核模块插入到系统当中(insmod nl.ko)

  • 再运行应用程序

可以看到如下输出:

#./nl_app
create socket error: Protocol not supported # insmod nl.ko
[25024.276345]test_netlink_init
# ./nl_app
[25117.548350]kernel recv from user: hello netlink!!
send kernel:hello netlink!!
from kernel:hello users!!! ## 卸载以后,app 无法通信(正常)
#rmmod nl
[25124.541352]test_netlink_exit!
# ./nl_app
create socket error: Protocol not supported

ref :

Linux 中内核与应用程序的交互方式:netlink的更多相关文章

  1. linux中内核的一个不错的参数somaxconn

    导读:在linux中,/proc/sys/net/core/somaxconn这个参数,linux中内核的一个不错的参数somaxconn 看下其解析: 对于一个TCP连接,Server与Client ...

  2. 在Linux中运行Nancy应用程序

    最近在研究如何将.NET应用程序移植到非Windows操作系统中运行,逐渐会写一些文章出来.目前还没有太深的研究,所以这些文章大多主要是记录我的一些实验. 这篇文章记录了我如何利用NancyFx编写一 ...

  3. (转载)linux中设备文件配置程序udev详解

    如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev ...

  4. linux中VI编写C程序。。。

    在linux中编写C程序时不像编写shell那样开头要#!/bin/bash,但是在C程序中要指定头文件(头文件是指输入输出,宏等,而且要首先声明,也是必须要开始就声明的) 写好C代码后要给C文件赋予 ...

  5. (转)关于linux中内核编程中结构体的赋值操作(结构体指定初始化)

    网址:http://blog.chinaunix.net/uid-24807808-id-3219820.html 在看linux源码的时候,经常会看到类似于下面的结构体赋值的代码: struct d ...

  6. linux中内核延时函数 (转)

    第一类延时函数原型是:(忙等) void ndelay(unsigned long nsecs); void udelay(unsigned long usecs); void mdelay(unsi ...

  7. 『学了就忘』Linux软件包管理 — 49、拓展:Linux中通过脚本安装程序

    目录 1.脚本程序简介 2.Webmin安装 (1)简介 (2)安装 (3)使用 1.脚本程序简介 脚本程序包并不多见,所以在软件包分类中并没有把它列为一类.它更加类似于Windows下的程序安装,有 ...

  8. 在Linux中实现打印目录程序遇到问题及解决

    今日阅读Linux程序设计第四版时,书中给出了一段实例代码,功能为实现/home目录下各级目录结构,当然不一定非得是/home下目录才可以,任何一级目录都可以. 自己尝试在Ubuntu系统运行编译,实 ...

  9. linux中添加常用应用程序的桌面图标

    在网上随处可以找到怎么样把应用程序的图标放到桌面上,我刚用ubuntu时也是按照网上的做法,一步一步的做的,现将网上的做法复制下来: 桌面配置文件简述\label{sec:desktop file} ...

  10. Linux中编译或安装程序时提示No such file or directory

    deb系发行版本 Debian Ubuntu Linux Mint等 dpkg -S dpkg-query -S rpm系发行版本 RHEL CentOS等 yum provides rpm -qf ...

随机推荐

  1. join分析:shuffle hash join、broadcast hash join

    Join 背景介绍 Join 是数据库查询永远绕不开的话题,传统查询 SQL 技术总体可以分为简单操作(过滤操作.排序操作 等),聚合操作-groupby 以及 Join 操作等.其中 Join 操作 ...

  2. 【经典爬虫案例】用Python爬取微博热搜榜!

    目录 一.爬取目标 二.编写爬虫代码 2.1 前戏 2.2 获取cookie 2.3 请求页面 2.4 解析页面 2.5 转换热搜类别 2.6 保存结果 2.7 查看结果数据 三.获取完整源码 一.爬 ...

  3. Sermant在异地多活场景下的实践

    本文分享自华为云社区<Sermant在异地多活场景下的实践>,作者:华为云开源. Sermant社区在1.3.0和1.4.0版本相继推出了消息队列禁止消费插件和数据库禁写插件,分别用于解决 ...

  4. TVM 中的 Profiler 设计

    一.基本用法 首先看 Profiler 的用法: with ms.Profiler() as profiler: # .... 用户代码 print("Tuning Time:") ...

  5. The attempt was made from the following location: com.ruoyi.framework.config.ResourcesConfig.corsFilter(ResourcesConfig.java:57)

    报错信息: 8:42:12.529 [restartedMain] ERROR o.s.b.w.e.t.TomcatStarter - [onStartup,61] - Error starting ...

  6. python openstacksdk

    调用方法 参考地址 https://github.com/openstack/openstacksdk 注意事项 1.需要安装openstacksdk.我这里装的好像是1.5版本的.opentask接 ...

  7. Vue——模板语法

    Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层组件实例的数据.所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析 ...

  8. RCTF 2024 WEB wp

    RCTF 2024 WEB wp 前言 赛后复现,proxy发现自己真是个呆b... what_is_love 首先拿key1,sql语句处有注入,可以盲注拿key1的值 import request ...

  9. C# WinForm控件及其子控件转成图片(支持带滚动条的长截图)

    概述(Overview) 参考了网上的分析,感觉都不太理想:1.一个控件内如果包含多个子控件时没有考虑顺序问题:2.超出控件可显示区域时不能长截图,有滚动条会多余截取了滚动条.这个随笔旨在解决这个问题 ...

  10. 使用Docker安装Odoo 17(非Docker Compose)

    使用Docker安装Odoo 17(非Docker Compose) 前言 最近在学习Odoo,先是windows 安装企业版,多年不用windows的服务器操作系统,一看windows的ECS那么贵 ...