The GIOChannel data type aims to provide a portable method for using file
descriptors, pipes, and sockets, and integrating them into the
main event loop.
Currently full support is available on Unix platforms, though support for
Windows is only partially complete.
To create a new GIOChannel on Unix systems use g_io_channel_unix_new().
This works for plain file descriptors, pipes and sockets.
Once a GIOChannel has been created, it can be used in a generic manner
with the functions g_io_channel_read(), g_io_channel_write(),
g_io_channel_seek(), and g_io_channel_close().
To add a GIOChannel to the
main event loop
use g_io_add_watch() or g_io_add_watch_full(). Here you specify which events
you are interested in on the GIOChannel, and provide a function to be
called whenever these events occur.
GIOChannel instances are created with an initial reference count of 1.
g_io_channel_ref() and g_io_channel_unref() can be used to increment or
decrement the reference count respectively. When the reference count falls
to 0, the GIOChannel is freed. (Though it isn't closed automatically.)
Using g_io_add_watch() or g_io_add_watch_full() increments a channel's
reference count.
GTK+ contains the convenience function gtk_input_add_full()
which creates a GIOChannel from a file descriptor and adds it to the
main event loop.
The event source can later be removed with gtk_input_remove().
Similar functions can also be found in GDK.
GUI系统都是基于事件驱动的,其中必有一个事件循环过程来获取和处理事件。gtk也一样,gtk的事件循环过程是由glib提供的,而iochannel是glib中把IO事件集成到事件的一种手段。
iochannel可以把开发者指定的发生在 文件描述符、管道和socket之上的事件转换为glib的内部事件,从而可以在程序中用统一的方法来处理IO事件和用户交互。
iochannel支持的IO事件有 可读、可写、有紧急(urgent)数据到达、出错、挂断。由于iochannel是在 文件描述符、管道和socket 的基础上构建的,所以它提供的方法既包括这三者的共同点,也考虑到了这三者的不同之处。
那么怎样在基于gtk或者glib的程序中加入iochannel呢?
步骤一般为:
1. 创建一个文件描述符。
可以通过打开一个普通文件、创建管道或者打开socket来实现,结果都是得到一个文件描述符。
2. 创建iochannel,设置数据编码。
iochannel通过如下函数创建:
GIOChannel* g_io_channel_unix_new (int fd);
例如:
io_channel = g_io_channel_unix_new (fd);
创建之后可以设置数据编码。对于数据编码,我也不太明,一般我把编码设置为NULL,这样在使用iochannel提供的读写函数时就不会对数据进行任何处理。编码设置函数如下:
GIOStatus g_io_channel_set_encoding (GIOChannel *channel, const gchar *encoding, GError **error);
例如,把编码设置为NULL:
g_io_channel_set_encoding (io_channel, NULL, &err);
3. 把你所需要处理的发生文件描述符上的事件加到事件循环中。
通过如下函数把iochannel的指定事件加入到事件循环中:
guint g_io_add_watch (GIOChannel *channel, GIOCondition condition, GIOFunc func, gpointer user_data);
其中,GIOCondition包括G_IO_IN, G_IO_OUT, G_IO_PRI,G_IO_ERR, G_IO_HUP, G_IO_NVAL。可以通过对它们的或运算来同时指定多个事件,当然回调函数应该判断是哪个的事件引起回调。
iochannel的回调函数原型为:
gboolean (*GIOFunc) (GIOChannel *source, GIOCondition condition, gpointer data);
第二个参数便是引起回调的事件的值。
上面第1步的作用就好比建立一个按钮,而第2,3步的作用就好比用g_signal_connect()把一个事件加入事件循环。做好这3个工作,后面还有两个工作:
1. 编写回调函数。
在回调函数中,你可以采用iochannel提供的读写函数,也可以用g_io_channel_unix_get_fd()获得的文件描述符来进行平常的IO操作。
2. 退出事件循环,关闭iochannel。
在程序结束,或者文件描述符已经没用的时候,应该关闭iochannel。在关闭前必须先退出事件循环,用g_source_remove(source_id)完成退出动作。source_id是g_io_add_watch()的返回值。
跟着便可以关闭iochannel了,用g_io_channel_shutdown (io_channel, TRUE, NULL)来完成关闭动作。
关闭后iochannel所占内存还没有释放,用g_io_channel_unref (io_channel)来减少iochannel的参考计数器,使其为0,glib会自动释放该iochannel。
根据你的应用,我的建议是在连接到服务器之后利用socket的文件描述符建立iochannel,并且为除 数据可写(G_IO_OUT)外的其他事件都建立回调函数,加入事件循环。当有数据来时被动读取,发送数据时主动发送。
一个iochannel只能绑定一个socket。
服务器那端,用listen之后的fd (设为listen_fd) 建立一个iochannel。
当有连接来时,iochannel表现为listen_fd可读。在回调函数中accept,得到一个连接fd(设为connect_fd)。然后为每一个connect_fd建立一个iochannel。
简单的说就是listen_fd的回调函数是accept用的;connect_fd的回调函数是读写用的,每个connect_fd的回调函数都一样。
connect_fd关闭后关闭该iochannel。
关于关闭iochannel,可能要用g_idle_add()添加一个垃圾回收函数。
因为不能在connect_fd的回调函数中shutdown该iochannel。
客户端要注意connect的超时时间比较长,可能需要用到线程来解决这个问题。
想实现“在按键事件开始后不断的读串口,直到关断串口的按键事件启动”的话,用while是不可行的,单单加入非阻塞也不行,因为在while循环中你的程序将不会响应按键事件。
多线程是可以解决问题的,不过尽量不要使用。
用iochannel是最合适的。方法大致如下:
1. 以非阻塞方式打开串口,非阻塞是必须的,下面会提到原因。
fd = open("/dev/ttyS0",O_RDWR|O_NONBLOCK,0644);
2. 建立iochannel。
io_channel = g_io_channel_unix_new (fd);
g_io_channel_set_encoding (io_channel, NULL, &err); /* 应该可选 */
3. 把文件描述符可读的事件加入到程序的事件循环中:
source_id = g_io_add_watch (io_channel, G_IO_IN, read_ttyS, NULL);
4. 当这些做好后就可以用 read_ttyS() 来读取串口数据了。
你可以用 read() 来直接读串口数据,也可以用 glib 提供的iochannel读取函数读。
不过要注意的是必须要用循环读到出现没有数据可读以致返回错误时才能结束一次读操作。这是因为内核中有缓冲,要是一次读取没有把全部数据读完的话,本应该
在这次回调中读取的数据就要等到下一次才能读取了。串口的数据流量不大,不用这种处理办法可能也不会有问题,不过还是保险一点好。上面打开串口时使用非阻
塞方式就是为了这里可以把达到的数据完整读完。
5. 当停止读取的事件发生时,回调函数应该做如下工作:
* (1). 退出事件循环: g_source_remove (source_id);
* (2). 关闭 IO_Channel: g_io_channel_shutdown (io_channel, TRUE, NULL);
* (3). 释放 IO_Channel: g_io_channel_unref (io_channel);
关闭 iochannel 操作会把文件描述符关闭。
6. 其他:
打开和关闭 iochannel 的顺序不可变。
要是只想暂时不读文件妙算符,可以只退出事件循环。
不保证退出事件循环后到来的数据是否会在内核中缓存,不保证这段时间内的数据是否全部被缓存,所以当你退出事件循环再加入时要自己检查数据是否是你所需要的。(不保证是因为我没有做过试验)
iochannel不是什么新技术,它的基础是 select / poll,对比一下 g_io_add_watch 提供的事件选项和
select / poll 提供的就清楚了。关于 select / poll 请 man 2 select_tut或者 man 2 poll。
上面提到的方法对其他文件描述符都适用,我之前是把它用在socket上。
分享到:
相关推荐
通过深入理解并熟练运用 GLib,开发者可以编写出更强大、更稳定的程序,尤其是在开发 GTK+ 或其他基于 GLib 的应用程序时。这份手册将作为开发者的重要参考资料,提供详细的 API 文档和示例代码,帮助他们快速掌握 ...
核心应用程序支持部分详细讲解了Glib应用程序的核心组成,包括主事件循环、多线程、线程池、异步队列、模块的动态加载、内存分配、IO通道、错误报告、消息输出、调试函数、消息日志记录等。这些组件为开发者构建响应...
在这个离线API手册中,开发者可以找到关于Glib的所有关键组件和功能的信息,以便在没有网络连接的情况下进行编程。 Glib库主要包括以下几个核心领域: 1. **数据结构**:Glib提供了许多高效的数据结构,如GList、...
glib库是Linux平台下最常用的C语言函数库,它具有很好的可移植性和实用性。glib是 Gtk+库和Gnome的基础。glib可以在多个平台下使用,比如Linux、Unix、Windows等。glib为 许多标准的、常用的C语言结构提供了相应的...
在开发或升级依赖于GLib的项目时,理解glib2-devel的内容及其作用至关重要。这包括学习如何正确配置编译选项,使用`pkg-config`来确保链接到正确的库版本,以及熟悉GLib提供的各种数据结构、异步机制、信号处理等...
在压缩包子文件的文件名称列表中,只有"glib-html-2.6.6",这可能是GLib的HTML文档,包含了API参考、教程和其他开发者文档,用于帮助程序员理解和使用GLib的各种函数和特性。 综上所述,这个压缩包很可能包含了GLib...
5. **文件和IO操作**:GLib提供了高级的文件和IO操作函数,比如GFile、GIOChannel等,可以方便地处理文件读写、流式IO,以及异步IO操作。 6. **定时器**:GLib的GTimer和GMainLoop可以实现延迟执行和定时任务,这...
**glib 2.20 Windows 版本详解** ...对于Windows开发者,尤其是使用Visual Studio 2008的用户,理解glib的关键特性和正确配置方法至关重要,这将有助于提高开发效率并实现高质量的跨平台应用程序。
Glib中的slab分配器同样基于这样的思想,并在源码的gslice.c文件中实现了三种内存分配机制:slab、magazine以及纯粹的malloc。本文章主要研究了Glib-2.18.4版本中的slab算法的实现。slab分配器的总体结构包括了几个...
首先,GLib提供了丰富的数据结构,如链表、队列、树、哈希表等,这些数据结构在C语言中通常需要程序员自己实现,而GLib则简化了这一过程。例如,GList和GSlist提供了动态链表的功能,而GHashTable则支持键值对的快速...
- 要使用glib中的事件循环、线程管理等功能,需要在编译时链接glib的相关库。 ##### 2.15 object使用 - glib提供了一个简单的对象系统,通过`GObject`类可以实现对象的创建和管理,支持信号和槽机制。 ##### 2.16 ...
这些头文件定义了GLib的API,让开发者可以在他们的源代码中包含并使用GLib的功能。 6. **share**: 此目录通常包含一些共享资源,比如国际化文件(locale)、配置文件、示例代码或文档。例如,GLib的`gsettings`设置...
Glib库,全称为GLib库,是GTK+图形用户界面工具包的基础,它提供了一系列通用的C语言编程工具和...理解其功能和作用,以及如何正确管理和使用这些文件,对于进行C语言开发特别是涉及GTK+或其他基于Glib的项目至关重要。
GLib Reference Manual for GLib 2.16.1 GLib 参考手册
GLib是GObject系统的基础,它是Gnome桌面环境的核心组件之一,主要用于...通过深入学习"GLib详细编译步骤.doc"和"GLib 依赖库综合"中的内容,你可以更好地理解GLib的工作原理,以及如何在实际项目中充分利用其功能。
标题中的"u8glib库文件"指的是一个专为微控制器设计的开源图形库,主要用于驱动各种类型的OLED(有机发光二极管)显示屏。这个库提供了一个简单且高效的接口,使得开发者能够轻松地在嵌入式系统上实现图形显示。 ...
首先,我们要理解什么是glib。GLib是一个提供基础数据类型、字符串处理、内存管理、线程、定时器、事件源等功能的C库,它是GObject对象系统、GTK+ GUI库以及GStreamer多媒体框架等更高级别库的基础。在ARM平台上编译...
如果要了解Glib库的更多信息,可以参考其头文件glib.h,该文件容易理解,并且很多函数从字面上就能猜出它们的用途和用法。Glib的源代码同样也是非常好的学习材料。 Glib采用了一种半面向对象的编码风格,所有标识符...
u8glib 库用户参考手册 u8glib 库是一个功能强大的图形库,提供了多种图形绘制函数,包括位图绘制、文本绘制、图形绘制等。本手册将详细介绍 u8glib 库的使用方法和参数设置。 1. 初始化函数 begin() begin() ...