在实际中,有些时候我们底层驱动有数据发生时,需要立即通知应用层获取数据。当然网上的方法有很多种,比如select、poll、sysctl、sysfs、procfs、netlink等,这篇文章是介绍内核态的socket和用户态的socket通信。
内核态socket编程和用户态的socket编程流程一样,但接口API不同,但和用户态的API是对应关系,在net/socket.c中可以看到内核导出符号:
EXPORT_SYMBOL(sock_create_kern);
EXPORT_SYMBOL(sock_release);
EXPORT_SYMBOL(kernel_bind);
EXPORT_SYMBOL(kernel_listen);
EXPORT_SYMBOL(kernel_accept);
EXPORT_SYMBOL(kernel_connect);
EXPORT_SYMBOL(kernel_sendmsg);
EXPORT_SYMBOL(kernel_recvmsg);
这里还实现了linux的系统调用,后续讲解Linux如何实现系统调用。这里直接来一个内核态的server和用户态的client例程便于理解。为了方便学习理解,我直接把内核态的server编译为.ko文件,并在ko中开辟一个内核线程。
模块功能:建立套接字, 绑定端口,监听端口,等待连接,接收数据。
#include
#include
#include
#include
static struct task_struct *task; //内核线程任务头
//内核线程服务函数,重点!!!!
static int socket_threadfn(void *data)
{
struct socket *sock, *nsock;
struct sockaddr_in addr;
int err;
//建立套接字
err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
if(err < 0)
{
printk("sock_create_kern failed.\n");
return -1;
}
//绑定端口
memset(&addr, '\0', sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(8888);
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (err < 0)
{
printk("kernel_bind failed.\n");
sock_release(sock);
return -1;
}
//监听端口
err = kernel_listen(sock, 1024);
if (err < 0)
{
printk("kernel_listen failed.\n");
sock_release(sock);
return -1;
}
//等待连接
err = kernel_accept(sock, &nsock, 0);
if (err < 0)
{
printk("kernel_accept failed.\n");
sock_release(sock);
return -1;
}
//任务主循环
while (!kthread_should_stop())
{
struct msghdr msg = {NULL,};
struct kvec iov;
char buffer[1024];
int len, buflen = sizeof(buffer);
iov.iov_base = buffer;
iov.iov_len = (size_t)buflen;
//等待sk_bufer中数据可读
wait_event_interruptible(*sk_sleep(nsock->sk),
!skb_queue_empty(&nsock->sk->sk_receive_queue) || kthread_should_stop());
if(!skb_queue_empty(&nsock->sk->sk_receive_queue))
{
len = kernel_recvmsg(nsock, &msg, &iov, 1, buflen, MSG_DONTWAIT);
if(len<0)
printk("receiving message error\n");
else
printk("receiving: %s\n", buffer);
}
}
sock_release(nsock);
sock_release(sock);
return 0;
}
int __init test_server_init(void)
{
task = kthread_run(socket_threadfn, NULL, "listen thread");
return 0;
}
void __exit test_server_exit(void)
{
}
module_init(test_server_init);
module_exit(test_server_exit);
MODULE_LICENSE("GPL");
server-driver-objs := server.o
obj-m:=server-driver.o
PWD:=$(shell pwd)
#这里是你自己已经编译好的内核源码路径
KDIR:=/home/vinda/work/RockLivetQ/linux-3.6
all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
$(RM) -r *.ko *.order *.symvers *.cmd *.o *.mod.c *.tmp_versions .*.cmd .tmp_versions
在设备终端安装驱动,insmod server-derver.ko,启动内核态的服务。
模块功能:建立套接字,连接端口,发送数据。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char* argv[])
{
struct sockaddr_in serverAddr;
char buffer[1024];
int sock, retl;
sock = socket(PF_INET,SOCK_STREAM , 0);
assert(sock >= 0);
bzero(&serverAddr,sizeof(serverAddr));
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(8888);
serverAddr.sin_family = AF_INET;
retl = connect(sock, (const struct sockaddr *)&serverAddr, (socklen_t)sizeof(struct sockaddr));
assert(retl >= 0);
retl=0;
while(1)
{
retl++;
sprintf(buffer, "This is frame number %d", retl);
send(sock, buffer, strlen(buffer), 0);
sleep(1);
}
return 0;
}
.PHONY: clean All
CC = arm-linux-gcc
All: client
client:client.o
$(CC) -o $@ $^
clean:
@$(RM) *.o client
内核态的socket编程还是很好理解的。希望对你们有帮助,谢谢!