内核态的socket编程

来源:互联网 发布:360mac版 dmg 编辑:程序博客网 时间:2024/06/11 19:03

记不清从哪个kernel版本开始,内核态就不能直接使用系统调用了。当然,socket的系统调用也不能用了。不过好在kernel提供了一组内核态的socket API。在net/socket.c文件中,可以看到这么几个导出符号:

EXPORT_SYMBOL(kernel_sendmsg);EXPORT_SYMBOL(kernel_recvmsg);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_getsockname);EXPORT_SYMBOL(kernel_getpeername);EXPORT_SYMBOL(kernel_getsockopt);EXPORT_SYMBOL(kernel_setsockopt);EXPORT_SYMBOL(kernel_sendpage);EXPORT_SYMBOL(kernel_sock_ioctl);EXPORT_SYMBOL(kernel_sock_shutdown);

基本上,在用户态的socket的API,在内核态都有对应的API。


下面是一个项目中的代码,socket操作我加了注视。不用去关心代码功能,只要看一下socket部分的操作即可,非常简单。

#include <linux/socket.h>#include <linux/net.h>#include <linux/in.h>struct fsg_common{....        struct socket *encrypt_sock;         struct msghdr encrypt_msg;        struct sockaddr_in encrypt_servaddr;        struct data_pkt *encrypt_pkt;}static int encrypt_socket_init(struct fsg_common *common){struct socket *sock;int ret;char *dst_addr = "111.111.111.111";/* init servaddr */memset(&common->encrypt_servaddr, 0, sizeof(common->encrypt_servaddr));common->encrypt_servaddr.sin_family = AF_INET;common->encrypt_servaddr.sin_port = htons(9999);//kernel态的IP地址转换函数,有两个函数in4_pton和in6_pton,分别用于IPv4和IPv6in4_pton(dst_addr, strlen(dst_addr), (u8*)&common->encrypt_servaddr.sin_addr, '\0', NULL);//内核态udp通信地址使用struct msghdr封装common->encrypt_msg.msg_name = &common->encrypt_servaddr;common->encrypt_msg.msg_namelen = sizeof(common->encrypt_servaddr);//创建套接字,类似socket()函数,返回的sock指针就是后面数据传递用的套接字了ret = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);if (ret < 0 || NULL == sock) {ERROR(common, "init encrypt board socket fail\n");return ret;}common->encrypt_sock = sock;common->encrypt_pkt = kmalloc(sizeof(struct data_pkt), GFP_KERNEL);if (NULL == common->encrypt_pkt)return -1;return 0;}static void encrypt_socket_close(struct fsg_common *common){//关闭套接字,类似用户态的close()函数sock_release(common->encrypt_sock);common->encrypt_sock = NULL;}static int encrypt_data(struct fsg_common *common, u8* data, unsigned int size, loff_t offset){struct socket *sock = common->encrypt_sock; struct data_pkt *pkt = common->encrypt_pkt;struct sockaddr *pservaddr = (struct sockaddr *)&common->encrypt_servaddr;int servlen = sizeof(common->encrypt_servaddr);if (unlikely(NULL == sock || NULL == pkt || NULL == pservaddr))return -1;memset(pkt, 0, sizeof(struct data_pkt));unsigned int copied_size = 0;struct kvec vec;struct msghdr msg;memset(&msg, 0, sizeof(msg));while (copied_size < size) {pkt->res=0xffffffff;pkt->file_offset = offset; memcpy(pkt->data, data + copied_size, SECTOR_SIZE);//配置udp发送数据地址及长度vec.iov_base = (void *)pkt;vec.iov_len = sizeof(struct data_pkt);ret = kernel_sendmsg(sock, &common->encrypt_msg, &vec, 1, sizeof(struct data_pkt));printk("send data(return :%d):\n", ret);//接收udp报文ret = kernel_recvmsg(sock, &msg, &vec, 1, sizeof(struct data_pkt), 0);printk("recv data(return :%d):\n", ret);memcpy(data+copied_size, pkt->data, 512);copied_size += 512;}   return 0;}
可以看出来,kernel态的udp通信除了具体API及参数不同,过程和用户态是完全一样的。