讨论,用户空间和内核空间如何通信,有什么方式,如何实现的?

bekars 2005-11-04 09:29:08
我现在知道两种:
1. ioctl ifconfig用的这种方式
2. sendmsg keepalived中见到过,设置端口ip地址

还有没有其他方式,并且现在我只能是去抄袭别人实现的代码,但是自己不清楚应该如何去用,如何查资料?

大家有没有这种感受啊?
...全文
966 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
IControlWorld 2005-11-22
  • 打赏
  • 举报
回复
按我的理解netlink应该就是在数据包通过网络协议栈的过程中对其进行截获从而实现内核和用户空间通信,这个完全可行并且实现起来不困难。
IControlWorld 2005-11-22
  • 打赏
  • 举报
回复
当然我也期待更好的方法
IControlWorld 2005-11-22
  • 打赏
  • 举报
回复
仅就bekars(涡轮增压)贴出的文章中的“内存共享 需要信号量辅助,而信号量又无法使用。 ”得出用户空间和内核空间无法通过内存共享是错误的, 当然使用通常意义上的信号量的确是不行的,但我们可以自己构造自己的信号量来保持两个空间的同步。 上面我的帖出的代码实际上很多是我自己写的,并且经过了测试和运用在以前的一个产品中了,我只是选取了其中的一部分贴出来而已。
bekars 2005-11-09
  • 打赏
  • 举报
回复
各位大侠,请问如何才能够明白为什么这样用啊!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
bekars 2005-11-09
  • 打赏
  • 举报
回复
看了通过NetLink获取内核中网络接口名称的一个实现,各个参数设置的眼花缭乱:


static int
netlink_request(struct nl_handle *nl, int family, int type)
{
int status;
struct sockaddr_nl snl;
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;

/* Cleanup the room */
memset(&snl, 0, sizeof (snl));
snl.nl_family = AF_NETLINK;

req.nlh.nlmsg_len = sizeof (req);
req.nlh.nlmsg_type = type;
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = ++nl->seq;
req.g.rtgen_family = family;

status = sendto(nl->fd, (void *) &req, sizeof (req)
, 0, (struct sockaddr *) &snl, sizeof (snl));
if (status < 0) {
FW_LOG("Netlink: sendto() failed: %s\n", strerror(errno));
return -1;
}
return 0;
}

/* Our netlink parser */
static int
netlink_parse_info(int (*filter) (struct sockaddr_nl *, struct nlmsghdr *),
struct nl_handle *nl)
{
int status;
int ret = 0;
int error;

while (1) {
char buf[4096];
struct iovec iov = { buf, sizeof buf };
struct sockaddr_nl snl;
struct msghdr msg =
{ (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 };
struct nlmsghdr *h;

status = recvmsg(nl->fd, &msg, 0);

if (status < 0) {
if (errno == EINTR)
continue;
if (errno == EWOULDBLOCK)
break;
FW_LOG("Netlink: Received message overrun\n");
continue;
}

if (status == 0) {
FW_LOG("Netlink: EOF\n");
return -1;
}

if (msg.msg_namelen != sizeof snl) {
FW_LOG("Netlink: Sender address length error: length %d\n", msg.msg_namelen);
return -1;
}

for (h = (struct nlmsghdr *) buf; NLMSG_OK(h, status);
h = NLMSG_NEXT(h, status)) {
/* Finish of reading. */
if (h->nlmsg_type == NLMSG_DONE)
return ret;

/* Error handling. */
if (h->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err =
(struct nlmsgerr *) NLMSG_DATA(h);
if (h->nlmsg_len <
NLMSG_LENGTH(sizeof (struct nlmsgerr))) {
FW_LOG("Netlink: error: message truncated\n");
return -1;
}
FW_LOG("Netlink: error: %s, type=(%u), seq=%u, pid=%d\n",
strerror(-err->error),
err->msg.nlmsg_type,
err->msg.nlmsg_seq, err->msg.nlmsg_pid);
return -1;
}

error = (*filter) (&snl, h);
if (error < 0) {
FW_LOG("Netlink: filter function error\n");
ret = error;
}
}

/* After error care. */
if (msg.msg_flags & MSG_TRUNC) {
FW_LOG("Netlink: error: message truncated\n");
continue;
}
if (status) {
FW_LOG("Netlink: error: data remnant size %d\n", status);
return -1;
}
}

return ret;
}


/* Netlink interface link lookup filter */
static int
netlink_if_link_filter(struct sockaddr_nl *snl, struct nlmsghdr *h)
{
struct ifinfomsg *ifi;
struct rtattr *tb[IFLA_MAX + 1];
interface *ifp;
int i, len;
char *name;

ifi = NLMSG_DATA(h);

if (h->nlmsg_type != RTM_NEWLINK)
return 0;

len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg));
if (len < 0)
return -1;

/* Interface name lookup */
memset(tb, 0, sizeof (tb));
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
if (tb[IFLA_IFNAME] == NULL)
return -1;
name = (char *) RTA_DATA(tb[IFLA_IFNAME]);

/* Return if loopback */
if (ifi->ifi_type == ARPHRD_LOOPBACK)
return 0;

/* Fill the interface structure */
ifp = (interface *) MALLOC(sizeof (interface));
memcpy(ifp->ifname, name, strlen(name));
ifp->ifindex = ifi->ifi_index;
ifp->flags = ifi->ifi_flags;
ifp->mtu = *(int *) RTA_DATA(tb[IFLA_MTU]);
ifp->hw_type = ifi->ifi_type;

if (tb[IFLA_ADDRESS]) {
int hw_addr_len = RTA_PAYLOAD(tb[IFLA_ADDRESS]);

if (hw_addr_len > IF_HWADDR_MAX)
FW_LOG("MAC address for %s is too large: %d\n", name, hw_addr_len);
else {
ifp->hw_addr_len = hw_addr_len;
memcpy(ifp->hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), hw_addr_len);
for (i = 0; i < hw_addr_len; i++)
if (ifp->hw_addr[i] != 0)
break;
if (i == hw_addr_len)
ifp->hw_addr_len = 0;
else
ifp->hw_addr_len = hw_addr_len;
}
}

/* Queue this new interface */
if_add_queue(ifp);
return 0;
}
mrelay 2005-11-09
  • 打赏
  • 举报
回复
通过驱动程序吧。
bekars 2005-11-09
  • 打赏
  • 举报
回复
3.2.2 netlink 套接字

在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,同时还使用 netlink 实现了 ip queue 工具,但 ip queue 的使用有其局限性,不能自由地用于各种中断过程。内核的帮助文档和其他一些 Linux 相关文章都没有对 netlink 套接字在中断过程和用户空间通信的应用上作详细的说明,使得很多用户对此只有一个模糊的概念。

netlink 套接字的通信依据是一个对应于进程的标识,一般定为该进程的 ID。当通信的一端处于中断过程时,该标识为 0。当使用 netlink 套接字进行通信,通信的双方都是用户态进程,则使用方法类似于消息队列。但通信双方有一端是中断过程,使用方法则不同。netlink 套接字的最大特点是对中断过程的支持,它在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程,而是通过另一个软中断调用用户事先指定的接收函数。工作原理如图【3】。

图【3】


很明显,这里使用了软中断而不是内核线程来接收数据,这样就可以保证数据接收的实时性。

当 netlink 套接字用于内核空间与用户空间的通信时,在用户空间的创建方法和一般套接字使用类似,但内核空间的创建方法则不同。图【4】是 netlink 套接字实现此类通信时创建的过程。

图【4】


以下举一个 netlink 套接字的应用示例。示例实现了从 netfilter 的 NF_IP_PRE_ROUTING 点截获的 ICMP 数据报,在将数据报的相关信息传递到一个用户态进程,由用户态进程将信息打印在终端上。源码在文件 imp2.tar.gz中。内核模块代码(分段详解):

(一)模块初始化与卸载

static struct sock *nlfd;
struct
{
__u32 pid;
rwlock_t lock;
}user_proc;

/*挂接在 netfilter 框架的 NF_IP_PRE_ROUTING 点上的函数为 get_icmp()*/
static struct nf_hook_ops imp2_ops =
{
.hook = get_icmp, /*netfilter 钩子函数*/
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
.priority = NF_IP_PRI_FILTER -1,
};

static int __init init(void)
{
rwlock_init(&user_proc.lock);

/*在内核创建一个 netlink socket,并注明由 kernel_recieve() 函数接收数据
这里协议 NL_IMP2 是自定的*/
nlfd = netlink_kernel_create(NL_IMP2, kernel_receive);
if(!nlfd)
{
printk("can not create a netlink socket\n");
return -1;
}
/*向 netfilter 的 NF_IP_PRE_ROUTING 点挂接函数*/
return nf_register_hook(&imp2_ops);
}

static void __exit fini(void)
{
if(nlfd)
{
sock_release(nlfd->socket);
}
nf_unregister_hook(&imp2_ops);
}

module_init(init);
module_exit(fini);



其实片断(一)的工作很简单,模块加载阶段先在内核空间创建一个 netlink 套接字,再将一个函数挂接在 netfilter 框架的 NF_IP_PRE_ROUTING 钩子点上。卸载时释放套接字所占的资源并注销之前在 netfilter 上挂接的函数。

(二)接收用户空间的数据

DECLARE_MUTEX(receive_sem);
01: static void kernel_receive(struct sock *sk, int len)
02: {
03: do
04: {
05: struct sk_buff *skb;
06: if(down_trylock(&receive_sem))
07: return;
08:
09: while((skb = skb_dequeue(&sk-<receive_queue)) != NULL)
10: {
11: {
12: struct nlmsghdr *nlh = NULL;
13: if(skb-<len <= sizeof(struct nlmsghdr))
14: {
15: nlh = (struct nlmsghdr *)skb-<data;
16: if((nlh-<nlmsg_len <= sizeof(struct nlmsghdr))
17: && (skb-<len <= nlh-<nlmsg_len))
18: {
19: if(nlh-<nlmsg_type == IMP2_U_PID)
20: {
21: write_lock_bh(&user_proc.pid);
22: user_proc.pid = nlh-<nlmsg_pid;
23: write_unlock_bh(&user_proc.pid);
24: }
25: else if(nlh-<nlmsg_type == IMP2_CLOSE)
26: {
27: write_lock_bh(&user_proc.pid);
28: if(nlh-<nlmsg_pid == user_proc.pid) user_proc.pid = 0;
29: write_unlock_bh(&user_proc.pid);
30: }
31: }
32: }
33: }
34: kfree_skb(skb);
35: }
36: up(&receive_sem);
37: }while(nlfd && nlfd-<receive_queue.qlen);
38: }



如果读者看过 ip_queue.c 或 rtnetlink.c中的源码会发现片断(二)中的 03~18 和 31~38 是 netlink socket 在内核空间接收数据的框架。在框架中主要是从套接字缓存中取出全部的数据,然后分析是不是合法的数据报,合法的 netlink 数据报必须有nlmsghdr 结构的报头。在这里笔者使用了自己定义的消息类型:IMP2_U_PID(消息为用户空间进程的ID),IMP2_CLOSE(用户空间进程关闭)。因为考虑到 SMP,所以在这里使用了读写锁来避免不同 CPU 访问临界区的问题。kernel_receive() 函数的运行在软中断环境。

(三)截获 IP 数据报

static unsigned int get_icmp(unsigned int hook,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph = (*pskb)->nh.iph;
struct packet_info info;

if(iph->protocol == IPPROTO_ICMP) /*若传输层协议为 ICMP*/
{
read_lock_bh(&user_proc.lock);
if(user_proc.pid != 0)
{
read_unlock_bh(&user_proc.lock);
info.src = iph->saddr; /*记录源地址*/
info.dest = iph->daddr; /*记录目的地址*/
send_to_user(&info); /*发送数据*/
}
else
read_unlock_bh(&user_proc.lock);
}
return NF_ACCEPT;
}



(四)发送数据

static int send_to_user(struct packet_info *info)
{
int ret;
int size;
unsigned char *old_tail;
struct sk_buff *skb;
struct nlmsghdr *nlh;
struct packet_info *packet;

size = NLMSG_SPACE(sizeof(*info));

/*开辟一个新的套接字缓存*/
skb = alloc_skb(size, GFP_ATOMIC);
old_tail = skb->tail;

/*填写数据报相关信息*/
nlh = NLMSG_PUT(skb, 0, 0, IMP2_K_MSG, size-sizeof(*nlh));
packet = NLMSG_DATA(nlh);
memset(packet, 0, sizeof(struct packet_info));

/*传输到用户空间的数据*/
packet->src = info->src;
packet->dest = info->dest;
/*计算经过字节对其后的数据实际长度*/
nlh->nlmsg_len = skb->tail - old_tail;
NETLINK_CB(skb).dst_groups = 0;

read_lock_bh(&user_proc.lock);
ret = netlink_unicast(nlfd, skb, user_proc.pid, MSG_DONTWAIT); /*发送数据*/
read_unlock_bh(&user_proc.lock);
return ret;

nlmsg_failure: /*若发送失败,则撤销套接字缓存*/
if(skb)
kfree_skb(skb);
return -1;
}



片断(四)中所使用的宏参考如下:

/*字节对齐*/
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
/*计算包含报头的数据报长度*/
#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))
/*字节对齐后的数据报长度*/
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
/*填写相关报头信息,这里使用了nlmsg_failure标签,所以在程序中要定义*/
#define NLMSG_PUT(skb, pid, seq, type, len) \
({ if (skb_tailroom(skb) < (int)NLMSG_SPACE(len)) goto nlmsg_failure; \
__nlmsg_put(skb, pid, seq, type, len); })
static __inline__ struct nlmsghdr *
__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len)
{
struct nlmsghdr *nlh;
int size = NLMSG_LENGTH(len);

nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
nlh->nlmsg_type = type;
nlh->nlmsg_len = size;
nlh->nlmsg_flags = 0;
nlh->nlmsg_pid = pid;
nlh->nlmsg_seq = seq;
return nlh;
}
/*跳过报头取实际数据*/
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
/*取 netlink 控制字段*/
#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))



运行示例时,先编译 imp2_k.c 模块,然后使用 insmod 将模块加载入内核。再运行编译好的 imp2_u 命令,此时就会显示出本机当前接收的 ICMP 数据报的源地址和目的地址。用户可以使用 Ctrl+C 来终止用户空间的进程,再次启动也不会带来问题。

4 总结
本文从内核态代码的不同运行环境来实现不同方法的内核空间与用户空间的通信,并分析了它们的实际效果。最后推荐使用 netlink 套接字实现中断环境与用户态进程通信,因为 netlink 套接字是专为此类通信定制的。

bekars 2005-11-09
  • 打赏
  • 举报
回复
贴一篇供大家学习

Linux 系统内核空间与用户空间通信的实现与分析

多数的 Linux 内核态程序都需要和用户空间的进程交换数据,但 Linux 内核态无法对传统的 Linux 进程间同步和通信的方法提供足够的支持。本文总结并比较了几种内核态与用户态进程通信的实现方法,并推荐使用 netlink 套接字实现中断环境与用户态进程通信。

1 引言
Linux 是一个源码开放的操作系统,无论是普通用户还是企业用户都可以编写自己的内核代码,再加上对标准内核的裁剪从而制作出适合自己的操作系统。目前有很多中低端用户使用的网络设备的操作系统是从标准 Linux 改进而来的,这也说明了有越来越多的人正在加入到 Linux 内核开发团体中。

一个或多个内核模块的实现并不能满足一般 Linux 系统软件的需要,因为内核的局限性太大,如不能在终端上打印,不能做大延时的处理等等。当我们需要做这些的时候,就需要将在内核态采集到的数据传送到用户态的一个或多个进程中进行处理。这样,内核态与用户空间进程通信的方法就显得尤为重要。在 Linux 的内核发行版本中没有对该类通信方法的详细介绍,也没有其他文章对此进行总结,所以本文将列举几种内核态与用户态进程通信的方法并详细分析它们的实现和适用环境。

2 Linux 内核模块的运行环境与传统进程间通信
在一台运行 Linux 的计算机中,CPU 在任何时候只会有如下四种状态:

【1】 在处理一个硬中断。

【2】 在处理一个软中断,如 softirq、tasklet 和 bh。

【3】 运行于内核态,但有进程上下文,即与一个进程相关。

【4】 运行一个用户态进程。

其中,【1】、【2】和【3】是运行于内核空间的,而【4】是在用户空间。其中除了【4】,其他状态只可以被在其之上的状态抢占。比如,软中断只可以被硬中断抢占。

Linux 内核模块是一段可以动态在内核装载和卸载的代码,装载进内核的代码便立即在内核中工作起来。Linux 内核代码的运行环境有三种:用户上下文环境、硬中断环境和软中断环境。但三种环境的局限性分两种,因为软中断环境只是硬中断环境的延续。比较如表【1】。

表【1】


内核态环境 介绍 局限性
用户上下文 内核态代码的运行与一用户空间进程相关,如系统调用中代码的运行环境。 不可直接将本地变量传递给用户态的内存区,因为内核态和用户态的内存映射机制不同。
硬中断和软中断环境 硬中断或软中断过程中代码的运行环境,如 IP 数据报的接收代码的运行环境,网络设备的驱动程序等。 不可直接向用户态内存区传递数据;
代码在运行过程中不可阻塞。

Linux 传统的进程间通信有很多,如各类管道、消息队列、内存共享、信号量等等。但它们都无法介于内核态与用户态使用,原因如表【2】。

表【2】


通信方法 无法介于内核态与用户态的原因
管道(不包括命名管道) 局限于父子进程间的通信。
消息队列 在硬、软中断中无法无阻塞地接收数据。
信号量 无法介于内核态和用户态使用。
内存共享 需要信号量辅助,而信号量又无法使用。
套接字 在硬、软中断中无法无阻塞地接收数据。

3 Linux内核态与用户态进程通信方法的提出与实现


3.1 用户上下文环境
运行在用户上下文环境中的代码是可以阻塞的,这样,便可以使用消息队列和 UNIX 域套接字来实现内核态与用户态的通信。但这些方法的数据传输效率较低,Linux 内核提供 copy_from_user()/copy_to_user() 函数来实现内核态与用户态数据的拷贝,但这两个函数会引发阻塞,所以不能用在硬、软中断中。一般将这两个特殊拷贝函数用在类似于系统调用一类的函数中,此类函数在使用中往往"穿梭"于内核态与用户态。此类方法的工作原理路如图【1】。

图【1】


其中相关的系统调用是需要用户自行编写并载入内核。 imp1.tar.gz是一个示例,内核模块注册了一组设置套接字选项的函数使得用户空间进程可以调用此组函数对内核态数据进行读写。源码包含三个文件,imp1.h 是通用头文件,定义了用户态和内核态都要用到的宏。imp1_k.c 是内核模块的源代码。imp1_u.c 是用户态进程的源代码。整个示例演示了由一个用户态进程向用户上下文环境发送一个字符串,内容为"a message from userspace\n"。然后再由用户上下文环境向用户态进程发送一个字符串,内容为"a message from kernel\n"。

3.2 硬、软中断环境
比起用户上下文环境,硬中断和软中断环境与用户态进程无丝毫关系,而且运行过程不能阻塞。

3.2.1 使用一般进程间通信的方法

我们无法直接使用传统的进程间通信的方法实现。但硬、软中断中也有一套同步机制--自旋锁(spinlock),可以通过自旋锁来实现中断环境与中断环境,中断环境与内核线程的同步,而内核线程是运行在有进程上下文环境中的,这样便可以在内核线程中使用套接字或消息队列来取得用户空间的数据,然后再将数据通过临界区传递给中断过程。基本思路如图【2】。

图【2】


因为中断过程不可能无休止地等待用户态进程发送数据,所以要通过一个内核线程来接收用户空间的数据,再通过临界区传给中断过程。中断过程向用户空间的数据发送必须是无阻塞的。这样的通信模型并不令人满意,因为内核线程是和其他用户态进程竞争CPU接收数据的,效率很低,这样中断过程便不能实时地接收来自用户空间的数据。

月吻长河 2005-11-08
  • 打赏
  • 举报
回复
共享内存的方式太龌龊了
smltiger 2005-11-08
  • 打赏
  • 举报
回复
操,直接操作/dev/kmem吧
QQ:28286880
bekars 2005-11-08
  • 打赏
  • 举报
回复
IControlWorld(大师) Thank you
IControlWorld 2005-11-07
  • 打赏
  • 举报
回复
上面是两部分代码,分别是用户空间和内核空间程序的代码,只拷一些重要的,编译一下,这个两个程序运行在不同的空间就可以通信,好好研究一下
IControlWorld 2005-11-07
  • 打赏
  • 举报
回复
/*corem.c for zero-copy*/

#define __KERNEL__

#define MODULE

#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/wrapper.h>
#include <asm/page.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>

#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif

#define PAGES_ORDER 9
#define PAGES 512
#define MEM_WIDTH 1500

struct MEM_DATA
{
//int key;
unsigned short width;/*缓冲区宽度*/
unsigned short length;/*缓冲区长度*/
//unsigned short wtimes;/*写进程记数,预留,为以后可以多个进程写*/
//unsigned short rtimes;/*读进程记数,预留,为以后可以多个进程读*/
unsigned short wi;/*写指针*/
unsigned short ri;/*读指针*/
} *mem_data;

struct MEM_PACKET
{
unsigned int len;
unsigned char packetp[MEM_WIDTH - 4];/*sizeof(unsigned int) == 4*/
};
unsigned long su1_2;

void del_mem()
{
int pages = 0;
char *addr;
addr = (char *)su1_2;
while (pages <=PAGES -1)
{
mem_map_unreserve(virt_to_page(addr));
addr = addr + PAGE_SIZE;
pages++;
}
free_pages(su1_2,PAGES_ORDER);
}
void init_mem()
{
int i;
int pages = 0;
char *addr;
char *buf;
struct MEM_PACKET * curr_pack;

su1_2 = __get_free_pages(GFP_KERNEL,PAGES_ORDER);
printk("[%x]\n",su1_2);
addr = (char *)su1_2;
while (pages <= PAGES -1)
{
mem_map_reserve(virt_to_page(addr));
addr = addr + PAGE_SIZE;
pages++;
}
mem_data = (struct MEM_DATA *)su1_2;
mem_data[0].ri = 1;
mem_data[0].wi = 1;
mem_data[0].length = PAGES*4*1024 / MEM_WIDTH;
mem_data[0].width = MEM_WIDTH;
/* initial su1_2 */
for(i=1;i<=mem_data[0].length;i++)
{
buf = (void *)((char *)su1_2 + MEM_WIDTH * i);
curr_pack = (struct MEM_PACKET *)buf;
curr_pack->len = 0;
}
}
int put_mem(char *aBuf,unsigned int pack_size)
{
register int s,i,width,length,mem_i;
char *buf;
struct MEM_PACKET * curr_pack;

s = 0;
mem_data = (struct MEM_DATA *)su1_2;
width = mem_data[0].width;
length = mem_data[0].length;
mem_i = mem_data[0].wi;
buf = (void *)((char *)su1_2 + width * mem_i);

for (i=1;i<length;i++){
curr_pack = (struct MEM_PACKET *)buf;
if (curr_pack->len == 0){
memcpy(curr_pack->packetp,aBuf,pack_size);
curr_pack->len = pack_size;;
s = mem_i;
mem_i++;
if (mem_i >= length)
mem_i = 1;
mem_data[0].wi = mem_i;
break;
}
mem_i++;
if (mem_i >= length){
mem_i = 1;
buf = (void *)((char *)su1_2 + width);
}
else buf = (char *)su1_2 + width*mem_i;
}

if(i >= length)
s = 0;
return s;
}

int read_procaddr(char *buf,char **start,off_t offset,int count,int *eof,void *data)
{
sprintf(buf,"%u\n",__pa(su1_2));
*eof = 1;
return 9;
}

int init_module(void)
{
put_pkt2mem_n = 0;
init_mem();
put_mem("data1dfadfaserty",16);
put_mem("data2zcvbnm",11);
put_mem("data39876543210poiuyt",21);
create_proc_read_entry("nf_addr",0,NULL,read_procaddr,NULL);
return 0;
}

void cleanup_module(void)
{
del_mem();
remove_proc_entry("nf_addr",NULL);

}
IControlWorld 2005-11-07
  • 打赏
  • 举报
回复
通过共享内存(文件)的方式可行

/*user.c*/

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

#define PAGES 512
#define MEM_WIDTH 1500
struct MEM_DATA
{
//int key;
unsigned short width;/*缓冲区宽度*/
unsigned short length;/*缓冲区长度*/
//unsigned short wtimes;/*写进程记数,预留,为以后可以多个进程写*/
//unsigned short rtimes;/*读进程记数,预留,为以后可以多个进程读*/
unsigned short wi;/*写指针*/
unsigned short ri;/*读指针*/
} * mem_data;

struct MEM_PACKET
{
unsigned int len;
unsigned char packetp[MEM_WIDTH - 4];/*sizeof(unsigned int) == 4*/
};

int get_mem(char *aMem,char *aBuf,unsigned int *size)
{
register int i,s,width,length,mem_i;
char *buf;
struct MEM_PACKET * curr_pack;

s = 0;
mem_data = (void *)aMem;
width = mem_data[0].width;
length = mem_data[0].length;
mem_i = mem_data[0].ri;
buf = (void *)(aMem + width * mem_i);

curr_pack = (struct MEM_PACKET *)buf;
if (curr_pack->len != 0){/*第一个字节为0说明该部分为空*/
memcpy(aBuf,curr_pack->packetp,curr_pack->len);
*size = curr_pack->len;
curr_pack->len = 0;
s = mem_data[0].ri;
mem_data[0].ri++;
if(mem_data[0].ri >= length)
mem_data[0].ri = 1;
goto ret;
}

for (i=1;i<length;i++){
mem_i++;/*继续向后找,最糟糕的情况是把整个缓冲区都找一遍*/
if (mem_i >= length)
mem_i = 1;
buf = (void *)(aMem + width*mem_i);
curr_pack = (struct MEM_PACKET *)buf;
if (curr_pack->len == 0)
continue;
memcpy(aBuf,curr_pack->packetp,curr_pack->len);
*size = curr_pack->len;
curr_pack->len = 0;
s = mem_data[0].ri = mem_i;
mem_data[0].ri++;
if(mem_data[0].ri >= length)
mem_data[0].ri = 1;
break;
}

ret:
return s;
}

int main()
{
char *su1_2;
char receive[1500];
int i,j;
int fd;
int fd_procaddr;
unsigned int size;
char addr[9];
unsigned long ADDR;

j = 0;
/*open device 'mem' as a media to access the RAM*/
fd=open("/dev/mem",O_RDWR);
fd_procaddr = open("/proc/nf_addr",O_RDONLY);
read(fd_procaddr,addr,9);
ADDR = atol(addr);
close(fd_procaddr);
printf("%u[%8lx]\n",ADDR,ADDR);
/*Map the address in kernel to user space, use mmap function*/
su1_2 = mmap(0,PAGES*4*1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, ADDR);
perror("mmap");
while(1)
{
bzero(receive,1500);
i = get_mem(su1_2,receive,&size);
if (i != 0)
{
j++;
printf("%d:%s[size = %d]\n",j,receive,size);
}
else
{
printf("there have no data\n");
munmap(su1_2,PAGES*4*1024);
close(fd);
break;
}
}
while(1);
}
bekars 2005-11-07
  • 打赏
  • 举报
回复
具体实现和Linux的Socket相关,还没有搞明白,再看。。。。。。
bekars 2005-11-07
  • 打赏
  • 举报
回复
现在有些眉目了,从2.4内核之后,用户空间和内核空间的消息传递是通过标准的netlink实现的,这样的好处是可以用在硬、软中断环境中,不会阻塞。
copy_from_user, copy_to_user会引起阻塞。
zengwh 2005-11-06
  • 打赏
  • 举报
回复
中断
系统调用
信号
bekars 2005-11-04
  • 打赏
  • 举报
回复
copy_from_user, copy_to_user是内核访问用户内存空间的方法。
如果你要从用户空间设置内核中(如网卡设备)的设备参数时需要用到socket和内核通信,这部分我不太明白,知道主要由ioctl和sendmsg,但是不知道如何用。
slone 2005-11-04
  • 打赏
  • 举报
回复
copy_from_user, copy_to_user 不是吗?
zengwh 2005-11-04
  • 打赏
  • 举报
回复
gz.......................
加载更多回复(3)

4,441

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 内核源代码研究区
社区管理员
  • 内核源代码研究区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧