FIOSETOWN和FIOGETOWN用来设置文件描述符的进程ID或进程组ID,在《Unix网络编程》卷一中有下面的图
这个图是在第二版的16.3节,图中FIOSETOWN和FIOGETOWN操作时第三个参数所需要的类型均int类型,我感觉这个地方不太准确(个人理解,或者跟作者的想法不同,或者作者这个地方出现了遗漏)。因为FIOSETOWN和FIOGETOWN操作时,第三个参数的类型应该分别为const int * 和int *。这个地方还有一个地方,个人感觉不妥,FIOGETOWN和FIOSETOWN这两个归于“文件”操作之列,但是在调用ioctl并且指定FIOGETOWN或FIOSETOWN时,如果第一个参数是普通文件的描述符,接口会报ENOTTY错误。所以根据测试的结果FIOGETOWN、FIOSETOWN应该只能作用于套接字。另外一个依据是在FIOGETOWN、FIOSETOWN定义的头文件中的注释,其完整的头文件如下所示:
#ifndef __ASM_GENERIC_SOCKIOS_H
#define __ASM_GENERIC_SOCKIOS_H
/* Socket-level I/O control calls. */
#define FIOSETOWN 0x8901
#define SIOCSPGRP 0x8902
#define FIOGETOWN 0x8903
#define SIOCGPGRP 0x8904
#define SIOCATMARK 0x8905
#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */
#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */
#endif /* __ASM_GENERIC_SOCKIOS_H */
注释“/* Socket-level I/O control calls. */”的意思就是说这些操作是在套接字级别(直译,英文太烂),所以大胆认为书本中这个地方处理的不是很恰当。当然stevens大师的作品的经典毋庸置疑,我也是看他的书本入门的,这里只是把一些疑问和自己的想法跟大家分享一下,希望相互学习,共同进步。下面给出我的测试程序和输出结果。
一、 使用普通文件描述符
测试程序如下:
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdint.h>
int main(void)
{
int own = -1;
int fd = -1;
pid_t pid;
pid = getpid();
if ((fd = open("test_file", O_RDWR)) < 0) {
fprintf(stderr, "open: %s, errno = %d\n",
strerror(errno), errno);
return -1;
}
if (ioctl(fd, FIOGETOWN, &own) < 0) {
fprintf(stderr, "ioctl get: %s, errno = %d\n",
strerror(errno), errno);
return -1;
}
printf("pid = %d, own = %d\n", pid, own);
if (ioctl(fd, FIOSETOWN, &pid) < 0) {
fprintf(stderr, "ioctl set: %s, errno = %d\n",
strerror(errno), errno);
return -1;
}
return 0;
}
其中test_file是我建立的一个普通文件,从代码中可以看出这个fd是通过打开一个文件获取的,然后直接执行ioctl操作,其结果如下所示:
ioctl get: Inappropriate ioctl for device, errno = 25
错误码25就是错误ENOTTY,出现这个错误的原因是“The specified request does not apply to the kind of object that the descriptor d references”或者“d is not associated with a character special device”(d是第一个参数)。所以从这里可以看出,FIOSETOWN和FIOGETOWN不能作用于普通文件描述符,其余其他的文件类型管道、符号链接等读者可以自行测试,我也不知道可不可以。
二、使用套接字
测试程序如下:
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdint.h>
int main(void)
{
int own = -1;
int fd = -1;
pid_t pid;
pid = getpid();
fd = socket(AF_INET, SOCK_STREAM, 0);
if (ioctl(fd, FIOGETOWN, &own) < 0) {
fprintf(stderr, "ioctl get: %s, errno = %d\n",
strerror(errno), errno);
fprintf(stderr, "ioctl get: %s\n", strerror(ENOTTY));
return -1;
}
printf("pid = %d, own = %d\n", pid, own);
if (ioctl(fd, FIOSETOWN, &pid) < 0) {
fprintf(stderr, "ioctl set: %s, errno = %d\n",
strerror(errno), errno);
return -1;
}
return 0;
}
这个程序和上面唯一的不同就是文件描述符的获取,这个的文件描述符是套接字,其输出结构如下所示:
pid = 2194, own = 0
没有任何错误输出。这里还有一个细节需要注意,新创建的套接字其所有者是进程0,这个不知道是因为初始化的问题还是因为这个套接字是进程0创建的。这个问题,只有看内核源码才能解答了。