在写资源管理器的时候,一般会用到处理close()来进行一些收尾工作,比如释放内存等等,查看QNX代码resmgr_io_funcs_t结构体对close()的处理有两个相关函数
typedef struct _resmgr_io_funcs {
unsigned nfuncs;
int (*read)(resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);
int (*write)(resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb);
int (*close_ocb)(resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb);
int (*stat)(resmgr_context_t *ctp, io_stat_t *msg, RESMGR_OCB_T *ocb);
int (*notify)(resmgr_context_t *ctp, io_notify_t *msg, RESMGR_OCB_T *ocb);
int (*devctl)(resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb);
int (*unblock)(resmgr_context_t *ctp, io_pulse_t *msg, RESMGR_OCB_T *ocb);
int (*pathconf)(resmgr_context_t *ctp, io_pathconf_t *msg, RESMGR_OCB_T *ocb);
int (*lseek)(resmgr_context_t *ctp, io_lseek_t *msg, RESMGR_OCB_T *ocb);
int (*chmod)(resmgr_context_t *ctp, io_chmod_t *msg, RESMGR_OCB_T *ocb);
int (*chown)(resmgr_context_t *ctp, io_chown_t *msg, RESMGR_OCB_T *ocb);
int (*utime)(resmgr_context_t *ctp, io_utime_t *msg, RESMGR_OCB_T *ocb);
int (*openfd)(resmgr_context_t *ctp, io_openfd_t *msg, RESMGR_OCB_T *ocb);
int (*fdinfo)(resmgr_context_t *ctp, io_fdinfo_t *msg, RESMGR_OCB_T *ocb);
int (*lock)(resmgr_context_t *ctp, io_lock_t *msg, RESMGR_OCB_T *ocb);
int (*space)(resmgr_context_t *ctp, io_space_t *msg, RESMGR_OCB_T *ocb);
int (*shutdown)(resmgr_context_t *ctp, io_shutdown_t *msg, RESMGR_OCB_T *ocb);
int (*mmap)(resmgr_context_t *ctp, io_mmap_t *msg, RESMGR_OCB_T *ocb);
int (*msg)(resmgr_context_t *ctp, io_msg_t *msg, RESMGR_OCB_T *ocb);
int (*reserved)(resmgr_context_t *ctp, void *msg, RESMGR_OCB_T *ocb);
int (*dup)(resmgr_context_t *ctp, io_dup_t *msg, RESMGR_OCB_T *ocb);
int (*close_dup)(resmgr_context_t *ctp, io_close_t *msg, RESMGR_OCB_T *ocb);
int (*lock_ocb)(resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb);
int (*unlock_ocb)(resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb);
int (*sync)(resmgr_context_t *ctp, io_sync_t *msg, RESMGR_OCB_T *ocb);
int (*power)(resmgr_context_t *ctp, io_power_t *msg, RESMGR_OCB_T *ocb);
int (*acl)(resmgr_context_t *ctp, io_acl_t *msg, RESMGR_OCB_T *ocb);
int (*pause)(resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb);
int (*unpause)(resmgr_context_t *ctp, io_pulse_t *msg, RESMGR_OCB_T *ocb);
} resmgr_io_funcs_t;
close_ocb和close_dup,查看QNX官方文档,对这两个函数定义是这样的:
close_dup是处理_IO_CLOSE 消息;而close_ocb是销毁OCB,释放分配给OCB的内存。
OCB结构包含有关每个文件描述符的信息。 当客户端执行open()调用并获取文件描述符(而不是错误指示)时,资源管理器将创建一个OCB并将其与客户端关联。 只要客户端打开了文件描述符,该OCB就会一直存在。据此分析close_ocb对close()的处理更彻底,那么他们的区别到底在哪?
再次查看官方对于open() dup() close()的处理:
假设用户程序执行以下代码:
fd = open ("/dev/device", O_RDONLY);
...
fd2 = dup (fd);
...
fd3 = dup (fd);
...
close (fd3);
...
close (fd2);
...
close (fd);
官方对这段代码的执行过程解释是:用户程序先对应open()函数生成一个open connect消息,然后对应两个dup()函数调用生成两个_IO_DUP消息。当用户执行close()函数的时候,会生成三个close消息。
因为dup()函数生成文件描述的复制,而没有申请新的上下文消息,所以当close消息到来的时候,由于没有申请新的上下文所以不需要释放内存。只有最后一次的close消息才释放OCB内存。
实际上,OCB和文件描述符是一对匹配的。 只要客户端调用I / O函数,资源管理器库就会自动关联OCB,并将其与消息一起传递给由I / O函数表项指定的I / O函数。 这就是为什么I / O函数都将ocb参数传递给它们的原因。 最后,用户程序将关闭文件描述符(通过close()),这将导致资源管理器将OCB与文件描述符和客户端分离。 用户程序的dup()函数只是增加一个引用计数。 在这种情况下,仅当引用计数达到零时(即,当相同数量的close()对应open()和dup()时,OCB才从文件描述符和客户机分离。
简单总结:close_dup 是对_IO_CLOSE消息进行处理,而close_ocb是对整个打开的文件进行销毁(包括释放内存等等),当没有fd的复制引用时,到来的_IO_CLOSE消息才对OCB销毁。
可能还不太理解,那么写个简单代码看一看运行结果:
首先把资源管理器代码框架搭起来,并将close_dup和close_ocb函数重写,下面贴出部分重写的代码
iofunc_func_init(_RESMGR_CONNECT_NFUNCS,&connect_funcs,_RESMGR_IO_NFUNCS,&io_funcs);
io_funcs.read = io_read;
io_funcs.write = io_write;
io_funcs.close_dup = io_close_dup;
io_funcs.close_ocb = io_close_ocb;
connect_funcs.open = io_open;
int io_open(resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra)
{
printf("Open the Resource Manger\n");
return iofunc_open_default(ctp,msg,handle,extra);
}
int io_close_dup(resmgr_context_t *ctp, io_close_t *msg, RESMGR_OCB_T *ocb)
{
printf("Close the dup\n");
return iofunc_close_dup_default(ctp, msg, ocb);
}
int io_close_ocb(resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb)
{
printf("Close the ocb\n");
return iofunc_close_ocb_default(ctp, reserved, ocb);
}
重写的函数只是多了个打印信息,主要还是进行的默认操作。
用户程序如下:
char buffer[10];
char buffer2[]="off";
int main(int argc, char *argv[]) {
printf("Welcome to the QNX Momentics IDE\n");
int filedes, dup_filedes;
filedes= open( "/dev/uartlite0",O_RDWR);
if( filedes != -1 )
{
dup_filedes = dup( filedes );
if( dup_filedes != -1 )
{
/* process file */
/* ... */
read(dup_filedes,buffer,5);
printf("%s received\n",buffer);
write(dup_filedes,buffer2,sizeof(buffer2));
close( dup_filedes );
}
write(filedes,buffer2,sizeof(buffer2));
read(filedes,buffer,5);
printf("%s received\n",buffer);
close(filedes);
return EXIT_SUCCESS;
}
return EXIT_SUCCESS;
}
执行完用户程序后打印信息:
Welcome to the QNX Momentics IDE
Open the Resource Manger
receive 5 bytes = 'ABCDE' expected read 5
ABCDE received
Received 4 bytes = 'off'
send 4 bytes = 'off' expected send 4
Close the dup
guan le yige
Received 4 bytes = 'off'
send 4 bytes = 'off' expected send 4
receive 5 bytes = 'ABCDE' expected read 5
ABCDE received
Close the dup
Close the ocb
通过打印信息可以更清楚close_dup和close_ocb的区别。
总结:在没有使用dup()函数复制文件描述fd的情况下,使用close_dup和close_ocb进行对close的操作没有区别。而如果使用了dup()对文件描述进行了复制,在执行close()后,先对复制的文件描述进行销毁,也就是系统调用close_dup()函数,对文件描述销毁但不消除上下文,当最后一次关闭(也就是所有复制的文件描述已销毁后)执行时,系统会先调用close_dup()再调用close_ocb(),close_ocb对上下文消息销毁,释放分配给OCB的内存。若是要通过对close()来对资源管理器关闭时进行一些缓冲区、内存释放等收尾工作还是尽量重写close_ocb()函数。
---------------------
作者:恰_同学少年
来源:CSDN
原文: https://blog.csdn.net/qq_34322603/article/details/79000636