Linux 中内核与应用程序的交互方式:netlink
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 :
- https://www.jianshu.com/p/6810f42b9f8f
- https://www.cnblogs.com/arnoldlu/p/9532254.html
- https://blog.csdn.net/wangpengqi/article/details/9988797
Linux 中内核与应用程序的交互方式:netlink的更多相关文章
- linux中内核的一个不错的参数somaxconn
导读:在linux中,/proc/sys/net/core/somaxconn这个参数,linux中内核的一个不错的参数somaxconn 看下其解析: 对于一个TCP连接,Server与Client ...
- 在Linux中运行Nancy应用程序
最近在研究如何将.NET应用程序移植到非Windows操作系统中运行,逐渐会写一些文章出来.目前还没有太深的研究,所以这些文章大多主要是记录我的一些实验. 这篇文章记录了我如何利用NancyFx编写一 ...
- (转载)linux中设备文件配置程序udev详解
如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev ...
- linux中VI编写C程序。。。
在linux中编写C程序时不像编写shell那样开头要#!/bin/bash,但是在C程序中要指定头文件(头文件是指输入输出,宏等,而且要首先声明,也是必须要开始就声明的) 写好C代码后要给C文件赋予 ...
- (转)关于linux中内核编程中结构体的赋值操作(结构体指定初始化)
网址:http://blog.chinaunix.net/uid-24807808-id-3219820.html 在看linux源码的时候,经常会看到类似于下面的结构体赋值的代码: struct d ...
- linux中内核延时函数 (转)
第一类延时函数原型是:(忙等) void ndelay(unsigned long nsecs); void udelay(unsigned long usecs); void mdelay(unsi ...
- 『学了就忘』Linux软件包管理 — 49、拓展:Linux中通过脚本安装程序
目录 1.脚本程序简介 2.Webmin安装 (1)简介 (2)安装 (3)使用 1.脚本程序简介 脚本程序包并不多见,所以在软件包分类中并没有把它列为一类.它更加类似于Windows下的程序安装,有 ...
- 在Linux中实现打印目录程序遇到问题及解决
今日阅读Linux程序设计第四版时,书中给出了一段实例代码,功能为实现/home目录下各级目录结构,当然不一定非得是/home下目录才可以,任何一级目录都可以. 自己尝试在Ubuntu系统运行编译,实 ...
- linux中添加常用应用程序的桌面图标
在网上随处可以找到怎么样把应用程序的图标放到桌面上,我刚用ubuntu时也是按照网上的做法,一步一步的做的,现将网上的做法复制下来: 桌面配置文件简述\label{sec:desktop file} ...
- Linux中编译或安装程序时提示No such file or directory
deb系发行版本 Debian Ubuntu Linux Mint等 dpkg -S dpkg-query -S rpm系发行版本 RHEL CentOS等 yum provides rpm -qf ...
随机推荐
- 如何通过前后端交互的方式制作Excel报表
前言 Excel拥有在办公领域最广泛的受众群体,以其强大的数据处理和可视化功能,成了无可替代的工具.它不仅可以呈现数据清晰明了,还能进行数据分析.图表制作和数据透视等操作,为用户提供了全面的数据展示和 ...
- .NET静态代码织入——肉夹馍(Rougamo)发布3.0
肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...
- ansible(5)--ansible的script模块
1. script模块 作用:在远程主机运行本地的脚本: 调用格式: -m script -a "/PATH/TO/SCRIPT_FILE": 参数: creates:如果其后跟的 ...
- IDEA的Ctrl+Enter补全代码失效
前景提示 IDEA有个ctrl+enter可以补全代码的功能,但是,今天突然失效了,原来是这个问题. 修改配置 进入setting修改 进入Edit-->找到Intertions,搜素Intro ...
- 4G EPS 中的 Control Plane
目录 文章目录 目录 前文列表 控制平面 归属环境部分 无线接入网络部分 核心网络 EPS CP 中的 GTP-C UP 中的 GTP-U Tunnel 两端的 F-TEID 需要通过 CP 的信令流 ...
- prometheus使用2
参考不错的 Prometheus监控实战之node_exporter详解: https://blog.csdn.net/ygq13572549874/article/details/129115350 ...
- jq 工具及其常用用法
在处理 JSON 数据时,我们经常需要在命令行中进行过滤.查询和编辑的操作.jq 是一个强大的命令行 JSON 处理工具,它可以让我们轻松地对 JSON 数据进行各种操作.本文将简要介绍 jq 的基本 ...
- 一套完整的中小级别的企业级监控prometheus
一 相信有很多博客都已经详细的说明了prometheus的作用以及相关的作用以及原理,这里不在赘述,仅仅从部署和配置2个方面来记录一下,为公司产品组搭建的prometheus告警平台的过程以及踩过 ...
- Kubernetes 数据存储:从理论到实践的全面指南
本文深入解析 Kubernetes (K8S) 数据存储机制,探讨其架构.管理策略及最佳实践.文章详细介绍了 K8S 数据存储的基础.架构组成.存储卷管理技巧,并通过具体案例阐述如何高效.安全地管理数 ...
- cmder右键打开方式
第一步: 新打开一个cmder窗口 第二步: 输入: Cmder.exe /register user 或 Cmder.exe /register all 第三步: 回车执行命令