• linux-自定义进程通信方式


    背景

            我们知道linux的进程的间通信的组件有管道,消息队列,socket, 信号量,共享内存等

            但是我们如果自己实现一套进程间通信的机制的话,要怎么做?了解android 开发的可能会知道,android里面有个binder机制,简单来说,就是一个进程往binder里面写数据,另一个进程从binder里面读出数据。

         

    原理 

    在这里插入图片描述

            我们首先需要注册一个字符设备文件叫/dev/channel, 同时需要为这个设备编写驱动,此时某个进程A向设备文件写数据,同时如果该设备可读,我们就通知另一个进程B去读该进程。 我们怎么知道该设备是否可读可写呢?使用poll来管理,因为该设备驱动属于一个IO, 打开一个设备就有fd, 有了fd我们就可以使用poll来管理。

    如:

    发送方:

     接收方:

     代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #ifndef CHANNEL_MAJOR
    14. #define CHANNEL_MAJOR 96
    15. #endif
    16. #ifndef CHANNEL_NR_DEVS
    17. #define CHANNEL_NR_DEVS 2
    18. #endif
    19. #ifndef CHANNEL_SIZE
    20. #define CHANNEL_SIZE 4096
    21. #endif
    22. #define ENABLE_POLL 1
    23. struct channel {
    24. char *data;
    25. unsigned long size;
    26. #if ENABLE_POLL
    27. wait_queue_head_t inq;
    28. #endif
    29. };
    30. static int channel_major = CHANNEL_MAJOR;
    31. module_param(channel_major, int, S_IRUGO);
    32. struct channel *channel_devp;
    33. struct cdev cdev;
    34. char have_data = 0;
    35. int channel_open (struct inode *inode, struct file *filp) {
    36. struct channel *channel;
    37. int num = MINOR(inode->i_rdev); //设备读了多少次
    38. if (num >= CHANNEL_NR_DEVS)
    39. return -ENODEV;
    40. channel = &channel_devp[num];
    41. filp->private_data = channel;
    42. return 0;
    43. }
    44. int channel_release (struct inode *inode, struct file *filp) {
    45. return 0;
    46. }
    47. #if ENABLE_POLL
    48. unsigned int channel_poll (struct file *filp, struct poll_table_struct *wait) {
    49. struct channel *channel = filp->private_data;
    50. unsigned int mask = 0;
    51. poll_wait(filp, &channel->inq, wait); // poll 阻塞
    52. if (have_data)
    53. mask |= (POLLIN | POLLRDNORM);
    54. return mask;
    55. }
    56. #endif
    57. int channel_mmap (struct file *filp, struct vm_area_struct *vma) {
    58. struct channel *channel = filp->private_data;
    59. vma->vm_flags |= VM_IO;
    60. vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP);
    61. if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(channel->data) >> PAGE_SHIFT,
    62. vma->vm_end-vma->vm_start, vma->vm_page_prot)) {
    63. return -EAGAIN;
    64. }
    65. return 0;
    66. }
    67. ssize_t channel_read (struct file *filp, char __user * buffer, size_t size, loff_t *ppos) {
    68. unsigned long p = *ppos;
    69. unsigned int count = size;
    70. int ret = 0;
    71. struct channel *channel = filp->private_data; // 读私有空间
    72. if (p >= CHANNEL_SIZE) return 0;
    73. if (count > CHANNEL_SIZE- p)
    74. count = CHANNEL_SIZE- p;
    75. #if ENABLE_POLL
    76. while (!have_data) {
    77. if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
    78. wait_event_interruptible(channel->inq, have_data);
    79. }
    80. #endif
    81. if (copy_to_user(buffer, (void*)(channel->data + p), count)) { //拷贝到用户空间
    82. ret = -EFAULT;
    83. } else {
    84. ret = strlen(buffer);
    85. channel->size -= ret;
    86. printk(KERN_INFO "read %d byte(s) from %ld\n", ret, p);
    87. }
    88. have_data = 0;
    89. return ret;
    90. }
    91. ssize_t channel_write (struct file *filp , const char __user * buffer, size_t size, loff_t *ppos) {
    92. int ret = 0;
    93. unsigned long p = *ppos;
    94. unsigned int count = size;
    95. struct channel *channel = filp->private_data; // 写道文件的私有空间
    96. if (p >= CHANNEL_SIZE) return 0;
    97. if (count > CHANNEL_SIZE- p)
    98. count = CHANNEL_SIZE- p;
    99. if (copy_from_user(channel->data +p, buffer, count)) { // 从user -> kernel
    100. return -EFAULT;
    101. } else {
    102. *ppos += count;
    103. ret = count;
    104. channel->size += count;
    105. *(channel->data+p + count) = '\0';
    106. printk(KERN_INFO "written %d byte(s) from %ld\n", count, p);
    107. }
    108. #if ENABLE_POLL
    109. have_data = 1;
    110. wake_up(&channel->inq);
    111. #endif
    112. return ret;
    113. }
    114. loff_t channel_llseek (struct file *filp, loff_t offset, int whence) { //偏移
    115. loff_t newpos;
    116. switch (whence)
    117. {
    118. case 0:
    119. newpos = offset;
    120. break;
    121. case 1:
    122. newpos = filp->f_pos + offset;
    123. break;
    124. case 2:
    125. newpos = CHANNEL_SIZE - 1 + offset;
    126. break;
    127. default:
    128. return -EINVAL;
    129. }
    130. if (newpos < 0 || newpos > CHANNEL_SIZE) return -EINVAL;
    131. filp->f_pos = newpos;
    132. return newpos;
    133. }
    134. static const struct file_operations channel_fops =
    135. {
    136. .owner = THIS_MODULE,
    137. .llseek = channel_llseek,
    138. .read = channel_read,
    139. .write = channel_write,
    140. .open = channel_open,
    141. .release = channel_release,
    142. .poll = channel_poll,
    143. .mmap = channel_mmap,
    144. };
    145. static int channel_init(void) {
    146. int reslut;
    147. int i;
    148. dev_t devno = MKDEV(channel_major, 0); // 创建一个主设备号为96,次设备号为0的设备
    149. if (channel_major) {
    150. reslut = register_chrdev_region(devno, CHANNEL_NR_DEVS, "channel"); // 注册设备
    151. } else {
    152. reslut = alloc_chrdev_region(&devno, 0, CHANNEL_NR_DEVS, "channel");
    153. }
    154. if (reslut < 0) return reslut;
    155. cdev_init(&cdev, &channel_fops); //初始化字符设备
    156. cdev.owner = THIS_MODULE;
    157. cdev_add(&cdev, MKDEV(channel_major, 0), CHANNEL_NR_DEVS); //添加到字符设备中
    158. channel_devp = kmalloc(CHANNEL_NR_DEVS *sizeof(struct channel), GFP_KERNEL); //为 我们的buffer 分配一块空间
    159. if (!channel_devp) {
    160. reslut = -ENOMEM;
    161. goto fail_malloc;
    162. }
    163. memset(channel_devp, 0, sizeof(struct channel));
    164. for (i = 0; i < CHANNEL_NR_DEVS; i++) {
    165. channel_devp[i].size = CHANNEL_SIZE;
    166. channel_devp[i].data = kmalloc(CHANNEL_SIZE, GFP_KERNEL);
    167. memset(channel_devp[i].data, 0, CHANNEL_SIZE);
    168. #if ENABLE_POLL
    169. init_waitqueue_head(&(channel_devp[i].inq));
    170. #endif
    171. }
    172. printk(KERN_INFO "ntychannel_init");
    173. return 0;
    174. fail_malloc:
    175. unregister_chrdev_region(devno, 1);
    176. return reslut;
    177. }
    178. static void channel_exit(void) {
    179. printk(KERN_INFO "channel_exit");
    180. cdev_del(&cdev);
    181. int i = 0;
    182. for (i = 0; i < CHANNEL_NR_DEVS; i++) {
    183. kfree(channel_devp[i].data);
    184. }
    185. kfree(channel_devp);
    186. unregister_chrdev_region(MKDEV(channel_major, 0), 2);
    187. }
    188. MODULE_AUTHOR("birate");
    189. MODULE_LICENSE("GPL");
    190. module_init(channel_init); // 设备初始化
    191. module_exit(channel_exit); //设备退出

    编写Makefile文件:

    1. obj-m += channel.o
    2. KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    3. all:
    4. make -C $(KERNELDIR) M=$(PWD) modules
    5. clean:
    6. make -C $(KERNELDIR) M=$(PWD) clean

    使用 make 命令。编译出我们需要的channel.ko文件。
    使用 insmod channel.ko, 向kernel中插入 我们的module
    使用mknod /dev/channel c 96 0, 创建一个/dev/channel 的字符设备,主设备号为96,次设备号为0;


    测试程序:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #define BUFFER_LENGTH 128
    11. int main () {
    12. int fd = open("/dev/channel", O_RDWR);
    13. if (fd < 0) {
    14. printf("open failed: errno : %s\n", strerror(errno));
    15. return -1;
    16. }
    17. char *buffer = (char *)malloc(BUFFER_LENGTH);
    18. memset(buffer, 0, BUFFER_LENGTH);
    19. char *start = mmap(NULL, BUFFER_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    20. fd_set rds;
    21. FD_ZERO(&rds);
    22. FD_SET(fd, &rds);
    23. while(1) {
    24. int ret = select(fd+1, &rds, NULL, NULL, NULL);
    25. if (ret < 0) {
    26. printf("select error\n");
    27. exit(1);
    28. }
    29. if (FD_ISSET(fd, &rds)) {
    30. #if 0
    31. strcpy(buffer, start);
    32. printf("channel: %s\n", buffer);
    33. #else
    34. read(fd, buffer, BUFFER_LENGTH);
    35. printf("channel: %s\n", buffer);
    36. #endif
    37. }
    38. }
    39. munmap(start, BUFFER_LENGTH);
    40. free(buffer);
    41. close(fd);
    42. return 0;
    43. }

             gcc -o channel_app channel_app.c , 编译出可执行文件,在一个进程中执行channel_app, 另一个进程使用echo " " > /dev/channel 去向设备文件中写就可以

    系统调用 system_call

     

    用户空间的 :

    1. static const struct file_operations channel_fops =
    2. {
    3. .owner = THIS_MODULE,
    4. .llseek = channel_llseek,
    5. .read = channel_read,
    6. .write = channel_write,
    7. .open = channel_open,
    8. .release = channel_release,
    9. .poll = channel_poll,
    10. .mmap = channel_mmap,
    11. };

    read,write等等是如何调用到内核空间的呢?

    Oldlinux.org -- Linux plinux - Early Linux Kernel Analysis and Comments

    《Linux操作系统实现原理》网页/Book-Lite/

    linux老版本下载:Index of /Linux.old/ 

            代码以0.11版本为例

            系统调用是一个软中断,中断号是0x80,它是上层应用程序与Linux系统内核进行交互通信的唯一接口。 

    如:tools\build.c

    调用open :

     open的源码路径lib\open.c

    1. /*
    2. * linux/lib/open.c
    3. *
    4. * (C) 1991 Linus Torvalds
    5. */
    6. #define __LIBRARY__
    7. #include
    8. #include
    9. int open(const char * filename, int flag, ...)
    10. {
    11. register int res;
    12. va_list arg;
    13. va_start(arg,flag);
    14. __asm__("int $0x80"
    15. :"=a" (res)
    16. :"0" (__NR_open),"b" (filename),"c" (flag),
    17. "d" (va_arg(arg,int)));
    18. if (res>=0)
    19. return res;
    20. errno = -res;
    21. return -1;
    22. }

    在汇编语言中调用0x80 ....进入系统调用

    在include\unistd.h中定义了

    1. #define __NR_setup 0 /* used only by init, to get system going */
    2. #define __NR_exit 1
    3. #define __NR_fork 2
    4. #define __NR_read 3
    5. #define __NR_write 4
    6. #define __NR_open 5 // open是5
    7. ......
    8. _syscall0(type,name) 其中 0 表示参数个数,type 表示返回值name表示函数名称
    9. #define _syscall0(type,name) \
    10. type name(void) \
    11. { \
    12. long __res; \
    13. __asm__ volatile ("int $0x80" \
    14. : "=a" (__res) \
    15. : "0" (__NR_##name)); \
    16. if (__res >= 0) \
    17. return (type) __res; \
    18. errno = -__res; \
    19. return -1; \
    20. }
    21. #define _syscall1(type,name,atype,a) \
    22. type name(atype a) \
    23. { \
    24. long __res; \
    25. __asm__ volatile ("int $0x80" \
    26. : "=a" (__res) \
    27. : "0" (__NR_##name),"b" ((long)(a))); \
    28. if (__res >= 0) \
    29. return (type) __res; \
    30. errno = -__res; \
    31. return -1; \
    32. }
    33. #define _syscall2(type,name,atype,a,btype,b) \
    34. type name(atype a,btype b) \
    35. { \
    36. long __res; \
    37. __asm__ volatile ("int $0x80" \
    38. : "=a" (__res) \
    39. : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b))); \
    40. if (__res >= 0) \
    41. return (type) __res; \
    42. errno = -__res; \
    43. return -1; \
    44. }
    45. #define _syscall3(type,name,atype,a,btype,b,ctype,c) \
    46. type name(atype a,btype b,ctype c) \
    47. { \
    48. long __res; \
    49. __asm__ volatile ("int $0x80" \
    50. : "=a" (__res) \
    51. : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
    52. if (__res>=0) \
    53. return (type) __res; \
    54. errno=-__res; \
    55. return -1; \
    56. }

    kernel\system_call.s

    1. _system_call:
    2. cmpl $nr_system_calls-1,%eax
    3. ja bad_sys_call
    4. push %ds
    5. push %es
    6. push %fs
    7. pushl %edx
    8. pushl %ecx # push %ebx,%ecx,%edx as parameters
    9. pushl %ebx # to the system call
    10. movl $0x10,%edx # set up ds,es to kernel space
    11. mov %dx,%ds
    12. mov %dx,%es
    13. movl $0x17,%edx # fs points to local data space
    14. mov %dx,%fs
    15. call _sys_call_table(,%eax,4)
    16. pushl %eax
    17. movl _current,%eax
    18. cmpl $0,state(%eax) # state
    19. jne reschedule
    20. cmpl $0,counter(%eax) # counter
    21. je reschedule

    调用了系统调用表    call _sys_call_table(,%eax,4),%eax表示下标

    include\linux\sys.h

    1. fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
    2. sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
    3. sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
    4. sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
    5. sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
    6. sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
    7. sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
    8. sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
    9. sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
    10. sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
    11. sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
    12. sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
    13. sys_setreuid,sys_setregid };

    可以看出系统调用表示一个函数数组,在include\unistd.h 定义了#define __NR_open   5,这里对应函数数组的下标5 :sys_open,调用内核函数:

    fs\open.c

    1. int sys_open(const char * filename,int flag,int mode)
    2. {
    3. struct m_inode * inode;
    4. struct file * f;
    5. int i,fd;
    6. mode &= 0777 & ~current->umask;
    7. for(fd=0 ; fd
    8. if (!current->filp[fd])
    9. break;
    10. if (fd>=NR_OPEN)
    11. return -EINVAL;
    12. current->close_on_exec &= ~(1<
    13. f=0+file_table;
    14. for (i=0 ; i
    15. if (!f->f_count) break;
    16. if (i>=NR_FILE)
    17. return -EINVAL;
    18. (current->filp[fd]=f)->f_count++;
    19. if ((i=open_namei(filename,flag,mode,&inode))<0) {
    20. current->filp[fd]=NULL;
    21. f->f_count=0;
    22. return i;
    23. }
    24. /* ttys are somewhat special (ttyxx major==4, tty major==5) */
    25. if (S_ISCHR(inode->i_mode))
    26. if (MAJOR(inode->i_zone[0])==4) {
    27. if (current->leader && current->tty<0) {
    28. current->tty = MINOR(inode->i_zone[0]);
    29. tty_table[current->tty].pgrp = current->pgrp;
    30. }
    31. } else if (MAJOR(inode->i_zone[0])==5)
    32. if (current->tty<0) {
    33. iput(inode);
    34. current->filp[fd]=NULL;
    35. f->f_count=0;
    36. return -EPERM;
    37. }
    38. /* Likewise with block-devices: check for floppy_change */
    39. if (S_ISBLK(inode->i_mode))
    40. check_disk_change(inode->i_zone[0]);
    41. f->f_mode = inode->i_mode;
    42. f->f_flags = flag;
    43. f->f_count = 1;
    44. f->f_inode = inode;
    45. f->f_pos = 0;
    46. return (fd);
    47. }

    那么  :

    1. static const struct file_operations channel_fops =
    2. {
    3. .owner = THIS_MODULE,
    4. .llseek = channel_llseek,
    5. .read = channel_read,
    6. .write = channel_write,
    7. .open = channel_open,
    8. .release = channel_release,
    9. .poll = channel_poll,
    10. .mmap = channel_mmap,
    11. };

    int channel_open (struct inode *inode, struct file *filp)

    inode:文件具体数据 file:路径属性

    是通过系统调用的

    int sys_open(const char * filename,int flag,int mode)  完成的

    主次设备号

            /dev目录下执行ls -l

            设备文件项的最后修改日期前的用逗号分割的两个数,对设备文件来说就是相应的主设备号和次设备号。

            第一个字符c表示字符设备,b表示块设备

            主设备号标识设备对应的驱动程序,次设备号由内核使用,用于正确确定设备文件所指的设备。依赖于驱动程序的编写方式,我们可以通过次设备号获得一个指向内核设备的直接指针,也可将次设备号当作设备本地数组的索引

    在代码中:

    1. static int channel_init(void) {
    2. int reslut;
    3. int i;
    4. dev_t devno = MKDEV(channel_major, 0); // 创建一个主设备号为96,次设备号为0的设备
    5. if (channel_major) {
    6. reslut = register_chrdev_region(devno, CHANNEL_NR_DEVS, "channel"); // 注册设备
    7. } else {
    8. reslut = alloc_chrdev_region(&devno, 0, CHANNEL_NR_DEVS, "channel");
    9. }
    10. if (reslut < 0) return reslut;
    11. cdev_init(&cdev, &channel_fops); //初始化字符设备
    12. cdev.owner = THIS_MODULE;
    13. cdev_add(&cdev, MKDEV(channel_major, 0), CHANNEL_NR_DEVS); //添加到字符设备中
    14. channel_devp = kmalloc(CHANNEL_NR_DEVS *sizeof(struct channel), GFP_KERNEL); //为 我们的buffer 分配一块空间
    15. if (!channel_devp) {
    16. reslut = -ENOMEM;
    17. goto fail_malloc;
    18. }
    19. memset(channel_devp, 0, sizeof(struct channel));
    20. for (i = 0; i < CHANNEL_NR_DEVS; i++) {
    21. channel_devp[i].size = CHANNEL_SIZE;
    22. channel_devp[i].data = kmalloc(CHANNEL_SIZE, GFP_KERNEL);
    23. memset(channel_devp[i].data, 0, CHANNEL_SIZE);
    24. #if ENABLE_POLL
    25. init_waitqueue_head(&(channel_devp[i].inq));
    26. #endif
    27. }
    28. printk(KERN_INFO "ntychannel_init");
    29. return 0;
    30. fail_malloc:
    31. unregister_chrdev_region(devno, 1);
    32. return reslut;
    33. }

    在调用过:

    1. sudo insmod channel.ko
    2. sudo mknod /dev/channel c 96 0
    3. cd /dev
    4. ls -l | grep channel

    可以看到注册的设备 

    sudo insmod channel.ko  实质是执行channel_init 函数

            1 向内核申请注册一个设备:主次设备号 register_chrdev_region

            2 初始化一个字符设备 cdev_init

            3 加入到内核 cdev_add

            4 初始化private_date

    open

            将file 和private_date关联起来

    1. int channel_open (struct inode *inode, struct file *filp) {
    2. struct channel *channel;
    3. int num = MINOR(inode->i_rdev); //设备读了多少次
    4. if (num >= CHANNEL_NR_DEVS)
    5. return -ENODEV;
    6. channel = &channel_devp[num];
    7. filp->private_data = channel;
    8. return 0;
    9. }

    rmmod与模块退出

    1. static void channel_exit(void) {
    2. printk(KERN_INFO "channel_exit");
    3. cdev_del(&cdev);
    4. int i = 0;
    5. for (i = 0; i < CHANNEL_NR_DEVS; i++) {
    6. kfree(channel_devp[i].data);
    7. }
    8. kfree(channel_devp);
    9. unregister_chrdev_region(MKDEV(channel_major, 0), 2);
    10. }

    write

    通过:

    root@ok-VirtualBox:/home/ok/channel# echo "111111111" > /dev/channel
    

    查看日志:dmesg

    可以看到写入数据的日志 

    从用户空间的buffer ->copy to -> channel.date

    1. //filp 文件属性
    2. //buffer 用户写入数据buffer 如:123\0
    3. //size 数据大小
    4. //ppos 偏移量
    5. ssize_t channel_write (struct file *filp , const char __user * buffer, size_t size, loff_t *ppos) {
    6. int ret = 0;
    7. unsigned long p = *ppos;
    8. unsigned int count = size;
    9. struct channel *channel = filp->private_data; // 写到文件的私有空间
    10. if (p >= CHANNEL_SIZE) return 0;
    11. //判断容量是否可以放下所有Buffer数据,防止数组越界
    12. if (count > CHANNEL_SIZE- p)
    13. count = CHANNEL_SIZE- p;
    14. //从用户空间的数据 copy to 内核空间, 返回0表示成功 ,执行else
    15. if (copy_from_user(channel->data +p, buffer, count)) { // 从user -> kernel
    16. return -EFAULT;
    17. } else {
    18. //修改相关标志位
    19. *ppos += count;
    20. ret = count;
    21. channel->size += count;
    22. *(channel->data+p + count) = '\0';
    23. printk(KERN_INFO "written %d byte(s) from %ld\n", count, p);
    24. }
    25. #if ENABLE_POLL
    26. have_data = 1;
    27. wake_up(&channel->inq);
    28. #endif
    29. return ret;
    30. }

    1 检查参数

    2 拷贝数据

    3 上下文参数调整

    read

    1. ssize_t channel_read (struct file *filp, char __user * buffer, size_t size, loff_t *ppos) {
    2. unsigned long p = *ppos;
    3. unsigned int count = size;
    4. int ret = 0;
    5. struct channel *channel = filp->private_data; // 读私有空间
    6. if (p >= CHANNEL_SIZE) return 0;
    7. if (count > CHANNEL_SIZE- p)
    8. count = CHANNEL_SIZE- p;
    9. #if ENABLE_POLL
    10. while (!have_data) {
    11. if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
    12. wait_event_interruptible(channel->inq, have_data);
    13. }
    14. #endif
    15. if (copy_to_user(buffer, (void*)(channel->data + p), count)) { //拷贝到用户空间
    16. ret = -EFAULT;
    17. } else {
    18. ret = strlen(buffer);
    19. channel->size -= ret;
    20. printk(KERN_INFO "read %d byte(s) from %ld\n", ret, p);
    21. }
    22. have_data = 0;
    23. return ret;
    24. }

    poll

    在应用层使用select 时,内核层会调用poll

    1. #if ENABLE_POLL
    2. unsigned int channel_poll (struct file *filp, struct poll_table_struct *wait) {
    3. struct channel *channel = filp->private_data;
    4. unsigned int mask = 0;
    5. poll_wait(filp, &channel->inq, wait); // poll 阻塞
    6. if (have_data)
    7. mask |= (POLLIN | POLLRDNORM);
    8. return mask;
    9. }
    10. #endif

  • 相关阅读:
    Colossal-AI
    Redis的最佳实践?看完不心动,算我输!!
    i.MX 6ULL 驱动开发 三:字符设备驱动框架实现和调试
    SpringBoot自动装配原理
    【刷题篇】反转链表
    文举论金:黄金原油全面走势分析策略独家指导
    LeetCode每日一题——791. 自定义字符串排序
    Django中的ajax细节
    调用 xlwings 创建多线程时报错 pywintypes.com_error: ( ‘应用程序调用一个已为另一线程整理的接口)解决方法
    【图解设计模式】适配器模式
  • 原文地址:https://blog.csdn.net/LIJIWEI0611/article/details/126110890