• 重要结构体

  • struct socket 结构体

	// 普通的 BSD 标准 socket 结构体
// socket_state: socket 状态, 连接?不连接?
// type: socket type (%SOCK_STREAM, etc)
// flags: socket flags (%SOCK_NOSPACE, etc)
// ops: 专用协议的socket的操作
// file: 与socket 有关的指针列表
// sk: 负责协议相关结构体,这样就让这个这个结构体和协议分开。
// wq: 等待队列
struct socket {
socket_state state; kmemcheck_bitfield_begin(type);
short type;
kmemcheck_bitfield_end(type); unsigned long flags; struct socket_wq __rcu *wq; struct file *file;
struct sock *sk;
const struct proto_ops *ops;
};
  • struct socket 的创建

	// socket() 本质上是 glibc 中的函数,执行的实际上是 sys_socketcall() 系统调用。
// sys_socketcall() 几乎是所有的socket函数的入口,
// 也就是 bind,connect 等函数都是需要asmlinkage long sys_socketcall(int call, unsigned long __user *args); 、、sys_socketcall() 作为入口,函数如下: // include/linux/syscalls.h
asmlinkage long sys_socketcall(int call, unsigned long __user *args);
	// net/socket.c
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
unsigned long a[AUDITSC_ARGS];
unsigned long a0, a1;
int err;
unsigned int len; if (call < 1 || call > SYS_SENDMMSG)
return -EINVAL; len = nargs[call];
if (len > sizeof(a))
return -EINVAL; /* copy_from_user should be SMP safe. */
if (copy_from_user(a, args, len))
return -EFAULT; err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
if (err)
return err; a0 = a[0];
a1 = a[1];
// 判断,然后运行相对应的函数
switch (call) {
case SYS_SOCKET: // 这里就是 sys_socket(),
err = sys_socket(a0, a1, a[2]);
break;
case SYS_BIND:
err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
break;
case SYS_CONNECT:
err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
break;
case SYS_LISTEN:
err = sys_listen(a0, a1);
break;
// ... ...
default:
err = -EINVAL;
break;
}
return err;
}
	// include/linux/syscalls.h
asmlinkage long sys_socket(int, int, int);
// net/socket.c
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
int retval;
struct socket *sock;
int flags; /* Check the SOCK_* constants for consistency. */
BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK); flags = type & ~SOCK_TYPE_MASK;
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return -EINVAL;
type &= SOCK_TYPE_MASK; if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
// 这里创建了 socket 结构体
retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
goto out;
// 与文件系统进行关联
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval < 0)
goto out_release; out:
/* It may be already another descriptor 8) Not kernel problem. */
return retval; out_release:
sock_release(sock);
return retval;
}
  • sock_create() 函数

	// include/linux/net.h
int sock_create(int family, int type, int proto, struct socket **res); // net/socket.c
int sock_create(int family, int type, int protocol, struct socket **res)
{
return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}
EXPORT_SYMBOL(sock_create); // include/linux/net.h
int __sock_create(struct net *net, int family, int type, int proto,
struct socket **res, int kern);
// net/socket.c
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 avoid
deadlock in module load.
*/ // 检查用的是PF_INET 其实这个都是兼容的。
if (family == PF_INET && type == SOCK_PACKET) {
static int warned;
if (!warned) {
warned = 1;
pr_info("%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.
*/ // ----> sock_alloc 接下面
sock = sock_alloc();
if (!sock) {
net_warn_ratelimited("socket: no more sockets\n");
return -ENFILE; /* Not exactly a match, but its the
closest posix thing */
} sock->type = type; // ... ...
return 0;
// ... ...
}
EXPORT_SYMBOL(__sock_create);
  • sock_alloc() 函数解析,被上面的 __sock_create() 函数调用

	// net/socket.c
static 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); kmemcheck_annotate_bitfield(sock, type);
inode->i_ino = get_next_ino();
inode->i_mode = S_IFSOCK | S_IRWXUGO; // 模式
inode->i_uid = current_fsuid(); // 获取当前的uid
inode->i_gid = current_fsgid(); // 获取当前的gid
inode->i_op = &sockfs_inode_ops; // 操作 this_cpu_add(sockets_in_use, 1);
return sock;
}
// 申请一个 socket 结构体 ,名字为 sock
// 申请一个新的节点和一个新的 socket 项目, 绑定他们两个并且初始化
// 如果申请inode 失败返回 NULL, 或者返回sock
	// 接下来我们再看到 SOCKET_I(inode);
// include/net/sock.h
static inline struct socket *SOCKET_I(struct inode *inode)
{
return &container_of(inode, struct socket_alloc, vfs_inode)->socket;
}
// 然后我们发现,返回的是 inode 内的socket 结构体。 // 我们可以分析一个 container_of() 这个是怎么定义的。
// include/linux/kernel.h
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
// typeof 将 ptr 的指针临时保存起来为 __mptr
// 然后用这个 __mptr 指针减去下面的 member 的便宜量。
// 得到的就是 type 这个结构体的头指针。
// offsetof include/linux/stddef.h
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#endif // 反正这里有点难理解,最后得到的结果是 type 这个结构体的头指针。 // 所以说 SOCKET_I() 得到的是 struct socket_alloc 的头指针
// include/net/sock.h
struct socket_alloc {
struct socket socket;
struct inode vfs_inode;
};
  • 回到 __sock_create() 继续分析

	// net/socket.c   --> __sock_create()
#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
	如果在 make menuconfig 中选上 编译成模块的选项,则会运行上面这个部分。
里面先是检查对应的协议族的操作表是否已经安装,如果没有安装则使用 request_module 进行安装,现在都是在 TCP/IP协议下进行分析,所以 family 是 AF_INET , 也就是 2 , 所以实际检查的全局变量是 net_families[2], 这个全局变量是在系统初始化时由 net/ipv4/af_inet.c 文件进行安装,具体代码是:
	// net/ipv4/af_inet.c
static int __init inet_init(void)
{
struct inet_protosw *q;
struct list_head *r;
int rc = -EINVAL; sock_skb_cb_check_size(sizeof(struct inet_skb_parm));
// 各个协议的注册
rc = proto_register(&tcp_prot, 1);
if (rc)
goto out; rc = proto_register(&udp_prot, 1);
if (rc)
goto out_unregister_tcp_proto; rc = proto_register(&raw_prot, 1);
if (rc)
goto out_unregister_udp_proto; rc = proto_register(&ping_prot, 1);
if (rc)
goto out_unregister_raw_proto; /*
* Tell SOCKET that we are alive...
*/ (void)sock_register(&inet_family_ops); #ifdef CONFIG_SYSCTL
ip_static_sysctl_init();
#endif /*
* Add all the base protocols.
*/
// 各个协议的添加,添加不成功则报错
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
pr_crit("%s: Cannot add ICMP protocol\n", __func__);
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
pr_crit("%s: Cannot add UDP protocol\n", __func__);
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
pr_crit("%s: Cannot add TCP protocol\n", __func__);
#ifdef CONFIG_IP_MULTICAST
if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
pr_crit("%s: Cannot add IGMP protocol\n", __func__);
#endif /* Register the socket-side information for inet_create. */
for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r);
// 把这个关键性的链接表一个个注册上去
		// ******************************************************
// inetsw_array 结构体数组数组, 这里面有包含每个的协议,比如说tcp_prot
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_dgram_ops,
.flags = INET_PROTOSW_REUSE,
},
// ... ...
} // tcp_prot ---> net/ipv4/tcp_ipv4.c
struct proto tcp_prot = {
.name = "TCP",
.owner = THIS_MODULE,
.close = tcp_close,
.connect = tcp_v4_connect,
.disconnect = tcp_disconnect,
.accept = inet_csk_accept,
.ioctl = tcp_ioctl,
.init = tcp_v4_init_sock, // 这是init 函数会在后面被调用
.destroy = tcp_v4_destroy_sock,
.shutdown = tcp_shutdown,
.setsockopt = tcp_setsockopt,
.getsockopt = tcp_getsockopt,
.recvmsg = tcp_recvmsg,
.sendmsg = tcp_sendmsg,
.sendpage = tcp_sendpage,
.backlog_rcv = tcp_v4_do_rcv,
.release_cb = tcp_release_cb,
.hash = inet_hash,
.unhash = inet_unhash,
.get_port = inet_csk_get_port,
.enter_memory_pressure = tcp_enter_memory_pressure,
.stream_memory_free = tcp_stream_memory_free,
.sockets_allocated = &tcp_sockets_allocated,
.orphan_count = &tcp_orphan_count,
.memory_allocated = &tcp_memory_allocated,
.memory_pressure = &tcp_memory_pressure,
.sysctl_mem = sysctl_tcp_mem,
.sysctl_wmem = sysctl_tcp_wmem,
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp_sock),
.slab_flags = SLAB_DESTROY_BY_RCU,
.twsk_prot = &tcp_timewait_sock_ops,
.rsk_prot = &tcp_request_sock_ops,
.h.hashinfo = &tcp_hashinfo,
.no_autobind = true,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
#ifdef CONFIG_MEMCG_KMEM
.init_cgroup = tcp_init_cgroup,
.destroy_cgroup = tcp_destroy_cgroup,
.proto_cgroup = tcp_proto_cgroup,
#endif
};
EXPORT_SYMBOL(tcp_prot);
// ***********************************************************
	    for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
inet_register_protosw(q); // 各个协议模块的初始化
/*
* Set the ARP module up
*/ arp_init(); /*
* Set the IP module up
*/ ip_init(); tcp_v4_init(); /* Setup TCP slab cache for open requests. */
tcp_init(); /* Setup UDP memory threshold */
udp_init(); /* Add UDP-Lite (RFC 3828) */
udplite4_register(); ping_init(); /*
* Set the ICMP layer up
*/ if (icmp_init() < 0)
panic("Failed to create the ICMP control socket.\n"); /*
* Initialise the multicast router
*/
#if defined(CONFIG_IP_MROUTE)
if (ip_mr_init())
pr_crit("%s: Cannot init ipv4 mroute\n", __func__);
#endif if (init_inet_pernet_ops())
pr_crit("%s: Cannot init ipv4 inet pernet ops\n", __func__);
/*
* Initialise per-cpu ipv4 mibs
*/ if (init_ipv4_mibs())
pr_crit("%s: Cannot init ipv4 mibs\n", __func__); ipv4_proc_init(); ipfrag_init(); dev_add_pack(&ip_packet_type); ip_tunnel_core_init(); rc = 0;
out:
return rc;
out_unregister_raw_proto:
proto_unregister(&raw_prot);
out_unregister_udp_proto:
proto_unregister(&udp_prot);
out_unregister_tcp_proto:
proto_unregister(&tcp_prot);
goto out;
} fs_initcall(inet_init);
  • 很粗浅的看完协议那一部分之后我们回到 __sock_create()

	// net/socket.c
// 看到 这个回调函数的调用
err = pf->create(net, sock, protocol, kern);
if (err < 0)
goto out_module_put;
	// 先看一个 inet_protosw 结构体
// include/net/protocol.h
/* 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. */
}; // 上面的 create 函数对应的是 net/ipv4/af_inet.c 里面的 inet_create 函数
// net/ipv4/af_inet.c
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();
// 遍历 inetsw[] 数组,其实就是次数而已
list_for_each_entry_rcu(answer, &inetsw[sock->type], list) { err = 0;
// 检查对应的协议,然后再选择合适的协议
/* Check the non-wild match. */
// 找到对应的协议,如果找到对应的协议,但是protocol 不是 IPPRORO_IP,则直接退出
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;
}
// 如果没有加载模块的保护措施
if (unlikely(err)) {
if (try_loading_module < 2) {
rcu_read_unlock();
/*
* Be more specific, e.g. net-pf-2-proto-132-type-1
* (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)
*/
if (++try_loading_module == 1)
request_module("net-pf-%d-proto-%d-type-%d",
PF_INET, protocol, sock->type);
/*
* Fall back to generic, e.g. net-pf-2-proto-132
* (net-pf-PF_INET-proto-IPPROTO_SCTP)
*/
else
request_module("net-pf-%d-proto-%d",
PF_INET, protocol);
goto lookup_protocol;
} else
goto out_rcu_unlock;
} err = -EPERM;
// 检查通用性,只有root 权限然后使用原始套接字
if (sock->type == SOCK_RAW && !kern &&
!ns_capable(net->user_ns, CAP_NET_RAW))
goto out_rcu_unlock; // 对socket 的操作集合进行了互联。
sock->ops = answer->ops;
answer_prot = answer->prot;
answer_flags = answer->flags;
rcu_read_unlock(); WARN_ON(!answer_prot->slab); err = -ENOBUFS;
/* 此处调用sk_alloc分配一个struct sock,该结构体庞大,其作用是网络层对socket的表示,意思就是IP协议下有很多东西比如IP地址,网卡接口,端口等等信息需要再socket层中有所体现从而使编程者方便使用,然后就利用指针等形式把内容进行一定程度上的映射。sk_alloc首先对sock->proto和sock_creator进行设置,设置成当前协议对应的proto调用sk_prot_alloc()根据是否提供了slab缓存而判断是使用slab缓存还是通用缓存。只要分配成功,则调用sock_lock_init()对缓存进行初始化,主要是对sock锁、等待队列以及进程数据结构中的网络空间结构进行分配。初始化完了后调用sock_net_set()函数对网络空间结构进行记录,然后最后增加一个net计数器。至此回到inet_create,判断是否成功分配 */
sk = 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; // 返回一个 struct inet_sock 的指针给 inet
inet = inet_sk(sk);
// 判断是不是面向连通
inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0; inet->nodefrag = 0; // 判断是不是原始套接字,如果是,新建IP头部。
if (SOCK_RAW == sock->type) {
inet->inet_num = protocol;
if (IPPROTO_RAW == protocol)
inet->hdrincl = 1;
}
// 判断是否采用路径 MTU 发现算法
if (net->ipv4.sysctl_ip_no_pmtu_disc)
inet->pmtudisc = IP_PMTUDISC_DONT;
else
inet->pmtudisc = IP_PMTUDISC_WANT; inet->inet_id = 0; // 进一步初始化结构体 sk (struct sock)
// sock_init_data: 初始化接收,发送,错误信息队列,三个队列都是双向链表,属于sk_buff_head 结构体,其中会把 sk_buff 结构体串联在一起,初始化数据包发送定时器,变量,(主要是函数指针)
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. */
sk->sk_prot->hash(sk);
} // 这里,就是调用了协议里面的 init 函数 tcp_v4_init_sock
if (sk->sk_prot->init) {
err = sk->sk_prot->init(sk);
if (err)
sk_common_release(sk);
}
out:
return err;
out_rcu_unlock:
rcu_read_unlock();
goto out;
}
  • tcp_v4_init_sock 函数

	static int tcp_v4_init_sock(struct sock *sk)
{
// 强制转换类型
struct inet_connection_sock *icsk = inet_csk(sk);
// 调用这个进行初始化 ,里面就时关于tcp 的一些初始化了,到此为止
tcp_init_sock(sk);
// ipv4 专用操作
icsk->icsk_af_ops = &ipv4_specific; #ifdef CONFIG_TCP_MD5SIG
tcp_sk(sk)->af_specific = &tcp_sock_ipv4_specific;
#endif return 0;
}
  • 到此, sock_create 分析完毕

  • 最后回到 SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)

	// net/socket.c
// 刚才分析完毕
retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
goto out;
// socket 映射到文件系统
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval < 0)
goto out_release;
	// net/socket.c
static int sock_map_fd(struct socket *sock, int flags)
{
struct file *newfile;
int fd = get_unused_fd_flags(flags);
if (unlikely(fd < 0))
return fd; // 申请一个 sock file 节点
newfile = sock_alloc_file(sock, flags, NULL);
if (likely(!IS_ERR(newfile))) {
fd_install(fd, newfile);
return fd;
} put_unused_fd(fd);
return PTR_ERR(newfile);
}
// 这里所展现的意思是,把socket当成一个文件节点进行操作,open, read,write ,ioctl 等

Linux kernel 之 socket 创建过程分析的更多相关文章

  1. Linux Kernel ‘/net/socket.c’本地信息泄露漏洞

    漏洞名称: Linux Kernel ‘/net/socket.c’本地信息泄露漏洞 CNNVD编号: CNNVD-201312-037 发布时间: 2013-12-04 更新时间: 2013-12- ...

  2. 上层应用与wpa_supplicant,wpa_supplicant与kernel 相关socket创建交互分析

    单独拿出来,分析以下上层应用与wpa_supplicant   wpa_supplicant与kernel 的socket交互. 关联上层应用与wpa_supplicant的socket的创建.连接流 ...

  3. Linux下进程的创建过程分析(_do_fork do_fork详解)--Linux进程的管理与调度(八)

    Unix标准的复制进程的系统调用时fork(即分叉),但是Linux,BSD等操作系统并不止实现这一个,确切的说linux实现了三个,fork,vfork,clone(确切说vfork创造出来的是轻量 ...

  4. Linux内核Socket实现之------Socket创建(2) 文件描述符

    转载请注明:http://blog.chinaunix.net/uid-20788636-id-4408276.html 1.2 sock_map_fd函数 在用户空间创建了一个socket后,返回值 ...

  5. karottc A Simple linux-virus Analysis、Linux Kernel <= 2.6.37 - Local Privilege Escalation、CVE-2010-4258、CVE-2010-3849、CVE-2010-3850

    catalog . 程序功能概述 . 感染文件 . 前置知识 . 获取ROOT权限: Linux Kernel <= - Local Privilege Escalation 1. 程序功能概述 ...

  6. Intel 80x86 Linux Kernel Interrupt(中断)、Interrupt Priority、Interrupt nesting、Prohibit Things Whthin CPU In The Interrupt Off State

    目录 . 引言 . Linux 中断的概念 . 中断处理流程 . Linux 中断相关的源代码分析 . Linux 硬件中断 . Linux 软中断 . 中断优先级 . CPU在关中断状态下编程要注意 ...

  7. Linux 设备文件的创建和mdev

    引子 本文是嵌入式企鹅圈开篇--<linux字符设备驱动剖析>的姐妹篇,在上述文章里面我们具体描写叙述了字符设备驱动框架涉及的驱动注冊.通过设备文件来訪问驱动等知识.并明白通过device ...

  8. 深入linux kernel内核配置选项

    ============================================================================== 深入linux kernel内核配置选项 ...

  9. linux kernel 字符设备详解

    有关Linux kernel 字符设备分析: 参考:http://blog.jobbole.com/86531/ 一.linux kernel 将设备分为3大类,字符设备,块设备,网络设备. 字符设备 ...

随机推荐

  1. Minitab系列 前言

    Matlab 在有些时候太过去强大,强大到几乎所有的数学问题都可以解决. 在这里,Matlab就像数学版的Vim,对于一般的使用者还是不想编程,想要更加的直接直观的数学处理工具,这里向大家介绍mini ...

  2. CSS--使用Animate.css制作动画效果

    一 使用Animate.css动画 // 通过@import引入外部CSS资源; // 引入线上图片及JS文件; // 通过更改CSS类名生成不同类型的CSS3动画;   <!DOCTYPE h ...

  3. Vue 爬坑之路(十二)—— vue-cli 3.x 搭建项目

    Vue Cli 3 官方文档:https://cli.vuejs.org/zh/guide/ 一.安装 @vue/cli 更新到 3.x 之后,vue-cli 的包名从 vue-cli 改成了 @vu ...

  4. 2018-02-06 编程猫IDE体验:对Scratch的改进

    前两天偶遇编程猫推介(为什么没有中文的编程?), 第一眼感觉像Scratch, 求证之下确实, 并且据说有改良. 今天非常粗浅地尝试一下, 限于水平没有做出很炫的效果, 不过颇有些发现. 首先上最终效 ...

  5. 开发Hexo主题(一)

    Hexo是一款静态博客工具,由Node.js编写 在博客根目录的themes下,新建文件夹(文件夹名为主题名) 主题目录结构如图 这里使用模版引擎为Ejs 在Layout目录下新建index.ejs文 ...

  6. sqlserver配置实践

    对于一套新的sqlserver服务器,我们首先要对它做一些必要的优化配置,确保在生产上比较长的时间段内可以比较稳定的,良好的运行. 新的sqlserver服务器上安装的sqlserver版本,可以选择 ...

  7. Salesforce的公式和验证规则

    公式 在Salesforce中,有些功能不需要从数据库中直接读取的数据,而是基于这些数据之间的关系来做出判断.这种情况下就要用到"公式"功能. 公式的概念和Excel中的公式类似, ...

  8. Android之移动热修复

    阿里云推出了移动热修复服务,听说这个服务傻瓜式接入,性能相对较好,对新技术比较好奇的我决定尝试一下. 1.首先,需要开通这个服务,创建应用 2.然后,在项目中接入服务.按照文档所述 第一步:gradl ...

  9. Flume Channel Selector

    Flume 基于Channel Selector可以实现扇入.扇出. 同一个数据源分发到不同的目的,如下图. 在source上可以定义channel selector: 1 2 3 4 5 6 7 8 ...

  10. SQLServer限制IP,限制用户,限制SSMS登录

    SQL Server不像Mysql那样原生支持限制IP登录. 但可以使用Login触发器来实现. 以下为使用Login触发器实现限制用户u_user_r在指定IP192.168.1.205使用SSMS ...