进程与进程间通信方式IPC:
1. 早期unix IPC:管道,FIFO,信号
2. System V IPC(贝尔实验室): system V消息队列,system V信号灯,system V共享内存
3. socket IPC (BSD)
4. Posix IPC: Posix消息队列,Posix信号灯,Posix共享内存
线程间通信:
1. 全局变量
2. 线程锁
3. pthread_time_wait
进程与内核间通信
1. ioctl()
2. 共享内存:mmap()
3. netlink
4. 其他
其他: 内核启动参数;模块参数;sysfs;procfs;debugfs;relayfs;sysctl;系统调用;copy_from_user()/copy_to_user();get_user()/put_user();信号/信号量/管道等等
netlink可以实现
1. 内核向进程广播(如热插拔事件)
2. 进程与内核双向通信
3. 进程与进程通信。
参考:
《Linux内核设计与实现》(Linux Kernel Development)第3版 》
Linux 用户态与内核态的交互——netlink 篇:http://bbs.chinaunix.net/thread-2162796-1-1.html
《Linux 系统内核空间与用户空间通信的实现与分析》 陈鑫 http://www-128.ibm.com/developerworks/cn/linux/l-netlink/?ca=dwcn-newsletter-linux
《在 Linux 下用户空间与内核空间数据交换的方式》 杨燚 http://www-128.ibm.com/developerworks/cn/linux/l-kerns-usrs/
http://blog.csdn.net/wangpengqi/article/details/9969599
上: http://blog.chinaunix.net/uid-23069658-id-3400761.html
中: http://blog.chinaunix.net/uid-23069658-id-3405954.html
下: http://blog.chinaunix.net/uid-23069658-id-3409786.html
3.10的内核比2.6的内核,netlink接口函数发生了一些变化。
// 内核态
netlink_kernel_create()
netlink_unicast()
netlink_broadcast()
// 用户态
socket()
bind()
sendmsg()
recvmsg()
close()
进程A和子系统1之间是单播通信,进程B、C和子系统2是多播通信。上图还向我们说明了一个信息。从用户空间传递到内核的数据是不需要排队的,即其操作是同步完成;而从内核空间向用户空间传递数据时需要排队,是异步的。
Netlink消息由两部分组成:消息头和有效数据载荷,且整个Netlink消息是4字节对齐。消息头为固定的16字节,消息体长度可变,采用TLV(Type-Length-Value)格式
// Kernel
#define NETLINK_ATC_SUSPEND 30
#define PM_NETLINK_GROUP 1
#define PM_NETLINK_SUSPEND_KEY "gpio_suspend"
#define ENTER_SUSPEND_GPIO 1 // GPIO1
static struct sock *netlink_sock = NULL;
static int netlink_broadcast(char *msg)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
int len = strlen(msg)+1;
printk(KERN_INFO "[%s %d] msg=[%s]\n", __FUNCTION__, __LINE__, msg);
if(!msg || !netlink_sock){
return -1;
}
skb = nlmsg_new(len, GFP_KERNEL);
if(!skb){
printk(KERN_ERR "[%s %d] \n", __FUNCTION__, __LINE__);
return -1;
}
nlh = nlmsg_put(skb, 0, 0, 0, len, 0);
NETLINK_CB(skb).portid = 0;
NETLINK_CB(skb).dst_group = PM_NETLINK_GROUP;
memcpy(NLMSG_DATA(nlh), msg, len);
printk(KERN_ERR "[%s %d] netlink broadcast msg [%s]\n", __FUNCTION__, __LINE__, msg);
return netlink_broadcast(netlink_sock, skb, 0, PM_NETLINK_GROUP, GFP_KERNEL);
}
static irqreturn_t suspend_gpio_irq_handler(int irq, void *data)
{
struct gpio_desc *desc = (struct gpio_desc *)data;
int gpio = desc_to_gpio(desc);
char msg[64];
int ret = 0;
snprintf(msg, sizeof(msg), "%s%d", PM_NETLINK_SUSPEND_KEY, gpio);
ret = atc_netlink_broadcast(msg);
return IRQ_HANDLED;
}
static int suspend_gpio_request_irq(void)
{
int gpio = ENTER_SUSPEND_GPIO;
struct gpio_desc *desc = gpio_to_desc(gpio);
int irq = gpiod_to_irq(desc);
if(irq > 0 && gpio_is_valid(gpio)){
#if 0
if(!gpio_suspend_workqueue) {
gpio_suspend_workqueue = create_singlethread_workqueue("gpio_wakeup");
INIT_WORK(&atc_gpio_suspend_work, atc_gpio_suspend_work_func);
}
#else
if(!netlink_sock){
struct netlink_kernel_cfg cfg = {
.input = NULL,
};
extern struct net init_net;
netlink_sock = netlink_kernel_create(&init_net, NETLINK_ATC_SUSPEND, &cfg);
if(!netlink_sock){
printk(KERN_ERR "[ATC %s %d] netlink_kernel_create fail\n", __FUNCTION__, __LINE__);
return -1;
}
}
#endif
gpio_direction_input(gpio);
irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
/* Regester IRQ */
if(request_irq(irq, suspend_gpio_irq_handler, IRQF_SHARED,
PM_NETLINK_SUSPEND_KEY, desc)
) {
printk(KERN_ERR "[%s %d] request_irq fail %d\n", __FUNCTION__, __LINE__, irq);
}
}
else{
printk(KERN_ERR "[%s %d] irq(%d) or gpio is not valid\n", __FUNCTION__, __LINE__, irq);
}
return 0;
}
// User
static void *pm_thread (void *arg)
{
struct sockaddr_nl src_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
struct msghdr msg;
int sock_fd, retval;
int fd_count = 0;
fd_set rfds, efds;
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SUSPEND);
if(sock_fd < 0){
goto exit;
}
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = PF_NETLINK;
src_addr.nl_pid = 0;
src_addr.nl_groups = 1 << (PM_NETLINK_GROUP - 1);
retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
if (retval < 0) {
goto exit;
}
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(PM_NETLINK_PAYLOAD_MAX));
if (!nlh) {
printf("malloc nlmsghdr %d-%s", errno, strerror(errno));
goto exit;
}
memset(nlh, 0, NLMSG_SPACE(PM_NETLINK_PAYLOAD_MAX));
iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(PM_NETLINK_PAYLOAD_MAX);
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
FD_ZERO(&rfds);
FD_ZERO(&efds);
fd_count = sock_fd;
while (!pm_thread_quit) {
FD_SET(sock_fd, &rfds);
FD_SET(sock_fd, &efds);
retval = select(fd_count + 1, &rfds, NULL, &efds, NULL);
if (retval < 0) {
goto exit;
} else if (retval == 0) {
} else if (FD_ISSET(sock_fd, &rfds)){
if (recvmsg(sock_fd, &msg, 0) > 0) {
const char *s = (char*)NLMSG_DATA(nlh);
int len = strlen(PM_NETLINK_SUSPEND_KEY);
if(!strncmp(s, PM_NETLINK_SUSPEND_KEY, len)) {
int pin = atoi(s+len);
usleep(100000);
if(gpio_get_value(pin) == 1) {
printf("state");
}
}
}
}
}
exit:
if(sock_fd > 0)
close(sock_fd);
pm_thread_id = 0;
return NULL;
}
net_link.c
----------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#define NETLINK_TEST 21
struct sock *nl_sk = NULL;
EXPORT_SYMBOL_GPL(nl_sk);
void nl_data_ready (struct sk_buff *__skb)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
u32 pid;
int rc;
int len = NLMSG_SPACE(1200);
char str[100];
printk("net_link: data is ready to read.\n");
skb = skb_get(__skb);
if (skb->len >= NLMSG_SPACE(0)) {
nlh = nlmsg_hdr(skb);
printk("net_link: recv %s.\n", (char *)NLMSG_DATA(nlh));
memcpy(str,NLMSG_DATA(nlh), sizeof(str));
pid = nlh->nlmsg_pid; /*pid of sending process */
printk("net_link: pid is %d\n", pid);
kfree_skb(skb);
skb = alloc_skb(len, GFP_ATOMIC);
if (!skb){
printk(KERN_ERR "net_link: allocate failed.\n");
return;
}
nlh = nlmsg_put(skb,0,0,0,1200,0);
NETLINK_CB(skb).pid = 0; /* from kernel */
memcpy(NLMSG_DATA(nlh), str, sizeof(str));
printk("net_link: going to send.\n");
rc = netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
if (rc < 0) {
printk(KERN_ERR "net_link: can not unicast skb (%d)\n", rc);
}
printk("net_link: send is ok.\n");
}
return;
}
static int test_netlink(void) {
nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE);
if (!nl_sk) {
printk(KERN_ERR "net_link: Cannot create netlink socket.\n");
return -EIO;
}
printk("net_link: create socket ok.\n");
return 0;
}
int init_module()
{
test_netlink();
return 0;
}
void cleanup_module( )
{
if (nl_sk != NULL){
sock_release(nl_sk->sk_socket);
}
printk("net_link: remove ok.\n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kidoln");
----------------------------------------------------------------------
sender.c
----------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
int main(int argc, char* argv[])
{
sock_fd = socket(PF_NETLINK, SOCK_RAW, 21);
memset(&msg, 0, sizeof(msg));
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
src_addr.nl_groups = 0; /* not in mcast groups */
bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */
nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
/* Fill the netlink message header */
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid(); /* self pid */
nlh->nlmsg_flags = 0;
/* Fill in the netlink message payload */
strcpy(NLMSG_DATA(nlh), "Hello you!");
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
printf(" Sending message. ...\n");
sendmsg(sock_fd, &msg, 0);
/* Read message from kernel */
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
printf(" Waiting message. ...\n");
recvmsg(sock_fd, &msg, 0);
printf(" Received message payload: %s\n",NLMSG_DATA(nlh));
/* Close Netlink Socket */
close(sock_fd);
}
----------------------------------------------------------------------
Makefile
----------------------------------------------------------------------
MODULE_NAME :=net_link
obj-m :=$(MODULE_NAME).o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
gcc -o sender sender.c
clean:
rm -fr *.ko *.o *.cmd sender $(MODULE_NAME).mod.c
----------------------------------------------------------------------