-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
彻底理解 select
中 fd_set
#4
Comments
本次调试机器:
// /usr/include/x86_64-linux-gnu/sys/select.h
/* The fd_set member is required to be an array of longs. */
typedef long int __fd_mask;
/* Some versions of <linux/posix_types.h> define this macros. */
#undef __NFDBITS
/* It's easier to assume 8-bit bytes than to get CHAR_BIT. */
#define __NFDBITS (8 * (int) sizeof (__fd_mask))
#define __FD_MASK(d) ((__fd_mask) (1UL << ((d) % __NFDBITS)))
/* fd_set for select and pselect. */
typedef struct
{
/* XPG4.2 requires this member name. Otherwise avoid the name
from the global namespace. */
#ifdef __USE_XOPEN
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif
} fd_set; 我简单整理一下,去掉和本系统无关的,结果如下: typedef long int __fd_mask; // 8
#define __NFDBITS (8 * (int) sizeof (__fd_mask) // 64
#define __FD_SETSIZE 1024
typedef struct
{
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS]; // 长度为 1024/64=16,类型为 long
} fd_set; 接着,我们看一个 demo: #include <stdio.h>
#include <sys/select.h>
void print_set1(fd_set *fdset);
void print_set2(fd_set *fdset);
int main()
{
fd_set fdset;
FD_ZERO(&fdset);
printf("sizeof long int: %ld\n", sizeof(long int)); // 8
printf("sizeof int: %ld\n", sizeof(int)); // 4
printf("sizeof short: %ld\n", sizeof(short)); // 2
FD_SET(1, &fdset);
FD_SET(2, &fdset);
FD_SET(3, &fdset);
FD_SET(7, &fdset);
print_set1(&fdset); // 10001110 -> 第 1 2 3 7 位分别设置成了 1
FD_SET(15, &fdset);
FD_SET(16, &fdset);
FD_SET(31, &fdset); // 10000000000000011000000010001110 -> 长度为 32
FD_SET(32, &fdset);
FD_SET(33, &fdset);
FD_SET(62, &fdset); // 100000000000000000000000000001110000000000000011000000010001110 0->长度为 63
print_set2(&fdset);
FD_SET(63, &fdset); // 1100000000000000000000000000001110000000000000011000000010001110 0 -> 长度为 64
print_set2(&fdset);
FD_SET(64, &fdset); // 1100000000000000000000000000001110000000000000011000000010001110 1 -> 长度还是 64,但产生了进位
print_set2(&fdset);
FD_SET(128, &fdset); // 1100000000000000000000000000001110000000000000011000000010001110 1 1-> 长度还是 63,但是在 64 和 128 的时候产生了进位
FD_SET(129, &fdset); // 1100000000000000000000000000001110000000000000011000000010001110 1 3-> 长度还是 63,但是在 64 和 128 的时候产生了进位
FD_SET(1023, &fdset); // 13835058070314647694 1 3 0 0 0 0 0 0 0 0 0 0 0 0 9223372036854775808
FD_SET(1024, &fdset); // 13835058070314647694 1 3 0 0 0 0 0 0 0 0 0 0 0 0 9223372036854775808
FD_SET(1025, &fdset); // 13835058070314647694 1 3 0 0 0 0 0 0 0 0 0 0 0 0 9223372036854775808
print_set2(&fdset);
int isset = FD_ISSET(7, &fdset);
printf("isset = %d\n", isset);// 输出 1,代表第 7 位被设置
FD_CLR(7, &fdset);
print_set2(&fdset); // 1100000000000000000000000000001110000000000000011000000000001110 1 3 0 0 0 0 0 0 0 0 0 0 0 0 9223372036854775808 -> 第 7 位的 1 变成了 0
isset = FD_ISSET(7, &fdset);
printf("isset = %d\n", isset);// 输出 0,代表第 7 位没有被设置
return 0;
}
void print_set2(fd_set *fdset)
{
int i;
for (i = 0; i < 16; i++)
{
printf("%llu ", (unsigned long long)fdset->__fds_bits[i]);
}
printf("\n");
}
void print_set1(fd_set *fdset)
{
int i;
for (i = 0; i < 16; i++)
{
printf("%ld ",fdset->__fds_bits[i]);
}
printf("\n");
}
接下来我们看 在调用 代码继续走到
再回头看 刚才的 #!/usr/local/env python3
# coding:utf-8
# get_fd_set_bin 返回 fd_set 表示的真实二进制(从右往左方向)
# every_fd_bits 表示数组中每个元素代表多少位
# set_array 表示 fd_set 的 long 数组
def get_fd_set_bin(every_fd_bits, set_array):
int_value = 0
for idx in range(len(set_array)):
int_value = int_value | (set_array[idx] << every_fd_bits * idx)
return bin(int_value)[2:] # 输出 "0bxxxxxx",为了方便展示,去掉前缀
# print_bin 将二进制按照 step 为一组打印
def print_bin(output, step=64):
le = len(output)
m = le % step
padding = step - m if m != 0 else 0
output = output.zfill(le + padding)
print(' '.join(''.join(output[idx * step:(idx + 1) * step]) for idx in range((le + padding) // step))) 在我们当前的例子中, # 输入(相当于设置了 1 2 3 7)
get_fd_set_bin(64, [142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
# 输出
0000000000000000000000000000000000000000000000000000000010001110
# 输入(相当于设置了 1 2 3 7 64)
get_fd_set_bin(64, [142,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
# 输出
0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000010001110 |
我用 Golang 做了简单实现: package main
import (
"math/big"
"strings"
)
type FdSet interface {
FD_CLR(fd int) error
FD_ISSET(fd int) (bool, error)
FD_SET(fd int) error
FD_ZERO() error
FdSetString() string
}
type fdType uint64
const (
maxFdSetSize = 1024
fdMask = 8 // sizeof long int in c
fdBits = 8 * fdMask
)
type FdSetGolang struct {
data [maxFdSetSize / fdBits]fdType
}
func NewGdSetGolang() *FdSetGolang {
return &FdSetGolang{data: [maxFdSetSize / fdBits]fdType{}}
}
func (fs *FdSetGolang) FD_CLR(fd int) error {
fs.clear(fs.getMask(fd))
return nil
}
func (fs *FdSetGolang) FD_ISSET(fd int) (bool, error) {
return fs.isSet(fs.getMask(fd)), nil
}
func (fs *FdSetGolang) FD_SET(fd int) error {
fs.set(fs.getMask(fd))
return nil
}
func (fs *FdSetGolang) FD_ZERO() error {
for i := range fs.data {
fs.data[i] = 0
}
return nil
}
func (fs *FdSetGolang) FdSetString() string {
tmp := make([]string, 0, len(fs.data))
for i := len(fs.data) - 1; i >= 0; i-- {
if v := fs.uintBin(uint64(fs.data[i])); v != "" {
tmp = append(tmp, v)
}
}
// 最左边的那个,可以将前面的 0 全部去掉
if len(tmp) > 0 {
t := tmp[0]
if t != "" {
for i := 0; i < len(t); i++ {
if t[i] != '0' {
tmp[0] = t[i:]
break
}
}
}
}
return "-->" + strings.Join(tmp, " ") + "<--"
}
func (fs *FdSetGolang) getMask(fd int) (idx int, n int) {
return fd / fdBits, fd % fdBits
}
// set 将数组下标为 idx 的数的从右往左数的第(n+1)位设置为 1
func (fs *FdSetGolang) set(idx int, n int) {
old := fs.data[idx]
fs.data[idx] = 1<<n | old
}
// clear 将数组下标为 idx 的数的从右往左数的第(n+1)位设置为 0
func (fs *FdSetGolang) clear(idx int, n int) {
if fs.isSet(idx, n) {
fs.data[idx] ^= 1 << n
}
}
func (fs *FdSetGolang) isSet(idx, n int) bool {
old := fs.data[idx]
this := 1 << n
if int(old)&this == this {
return true
}
return false
}
// uintBin 输出 n 的二进制表示
func (fs *FdSetGolang) uintBin(n uint64) string {
if n == 0 {
return ""
}
s := big.NewInt(0).SetUint64(n).Text(2)
return strings.Repeat("0", fdBits-len(s)) + s
} |
看源码,
fd_set
这个结构体实际上是一个 long 型的数组,但是数组的长度依赖于系统中typedef long int __fd_mask
的长度。当我去调试的时候,经常打印出一些很奇怪的值,有时候还会溢出。此 issue 旨在抛开
select
相关的功能,彻底搞明白fd_set
的存储原理、FD_SET()
等函数到底实现了什么效果。The text was updated successfully, but these errors were encountered: