『 Linux 』文件与网络套接字的内部关系

文章目录

    • 回顾进程控制块
    • socket与文件的关系
    • wait_queue_head_t
    • 文件与套接字相关的调用方法
    • 系统中的套接字
    • 网络协议栈与方法集
    • 报文的管理


回顾进程控制块

每个进程都存在着自己的PCB结构体,即task_struct结构体,这个结构体是用来描述一个进程的;

/* 已省略部分代码 */
struct task_struct {volatile long state;	/* Process state */void *stack;            /* Stack pointer */atomic_t usage;         /* Use count */unsigned int flags;	/* Per process flags */int prio, static_prio, normal_prio; /* Priority levels */unsigned int rt_priority;   /* Real-time priority */const struct sched_class *sched_class; /* Scheduling class */struct sched_entity se;     /* Scheduling entity for CFS */struct sched_rt_entity rt;  /* Scheduling entity for real-time tasks */unsigned char fpu_counter;  /* FPU usage counter */pid_t pid;                  /* Process ID */pid_t tgid;                 /* Thread group ID */struct mm_struct *mm, *active_mm; /* Memory management info */int exit_state;             /* State on exit */int exit_code, exit_signal; /* Exit code and signal */struct task_struct *real_parent; /* Real parent process */struct list_head children;   /* List of my children */struct list_head sibling;    /* Linkage in my parent's children list */char comm[TASK_COMM_LEN];    /* Task name */struct files_struct *files;  /* Open files info */cpumask_t cpus_allowed;      /* Allowed CPUs for this task */sigset_t blocked, real_blocked; /* Blocked signals */struct signal_struct *signal;/* Signal handlers */struct sighand_struct *sighand; /* Signal handling structure */
#ifdef CONFIG_CGROUPSstruct css_set *cgroups;     /* Control groups info */
#endif
#ifdef CONFIG_FUTEXstruct robust_list_head __user *robust_list; /* List of robust futexes */
#endif
#ifdef CONFIG_PERF_EVENTSstruct perf_event_context *perf_event_ctxp; /* Performance event context */
#endifatomic_t fs_excl;            /* Holding fs exclusive resources */
};

在PCB结构体,即struct task_struct结构体中存在着一个指向struct files_struct结构体的指针,而在struct files_struct结构体中存在一个数组struct file * fd_array[NR_OPEN_DEFAULT]为文件描述符表;

struct files_struct {/** read mostly part*/atomic_t count;struct fdtable *fdt;struct fdtable fdtab;/** written part on a separate cache line in SMP*/spinlock_t file_lock ____cacheline_aligned_in_smp;int next_fd;struct embedded_fd_set close_on_exec_init;struct embedded_fd_set open_fds_init;struct file * fd_array[NR_OPEN_DEFAULT]; // 文件描述符表
};

其中该数组中对应的每个下标即为文件描述符,数组中的每个结构都象征着该进程所打开的各种文件;

struct file {struct path		    f_path;             // 文件路径const struct file_operations	*f_op; // 文件操作函数指针spinlock_t		    f_lock;            // 用于同步的自旋锁atomic_long_t	    f_count;           // 引用计数unsigned int 	    f_flags;           // 文件打开标志fmode_t			    f_mode;            // 文件模式loff_t			    f_pos;             // 当前文件位置(偏移量)struct fown_struct	f_owner;           // 文件所有者const struct cred	*f_cred;           // 安全凭证struct file_ra_state	f_ra;            // 预读状态信息void			    *private_data;     // 私有数据,供驱动程序使用struct address_space *f_mapping;        // 关联的地址空间对象
};

在该结构体中存在指针const struct file_operations *f_op用来存放一系列的文件操作函数;

struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);
};

而对应的磁盘文件的文件缓冲区则在struct file结构体中的struct address_space *f_mapping部分;


socket与文件的关系

struct file结构体中存在一个指针为void *private_data;

这个指针主要是用于私有数据,供驱动程序或其他文件使用;

而在创建网络套接字时所创建的大批数据结构中将会创建一个类型为struct socket{}的结构体对象;

struct socket {socket_state		state;kmemcheck_bitfield_begin(type);short			type;kmemcheck_bitfield_end(type);unsigned long		flags;/** Please keep fasync_list & wait fields in the same cache line*/struct fasync_struct	*fasync_list;wait_queue_head_t	wait;struct file		*file;struct sock		*sk;const struct proto_ops	*ops;
};

对应的如果进程所打开的文件为网络文件,对应的void *private_data指针将会指向struct socket结构体对象;

而对应的struct socket结构体中的struct file *file指针将会回指向回文件描述符表struct file * fd_array[NR_OPEN_DEFAULT]数组中对应文件描述符指针所指向的struct file结构体对象;

如下图所示;

这个结构也印证了 “Linux下一切接文件” 的哲学;


wait_queue_head_t

struct socket结构中存在着一个 wait_queue_head_t wait;

其中这个wait是一个等待队列头部,通常用于管理在此套接字上可能阻塞等待的进程;

这个等待队列主要用于处理套接字的异步通知和阻塞操作,如:

  • 数据接收

    当进程试图从尚无数据的套接字读取数据时它可能会被放入这个等待队列并处于睡眠状态,直至有数据到达;

  • 连接过程

    对于某些类型的套接字(如TCP),在连接建立过程中,进程可能会在这个队列上阻塞,直至连接成功建立或发生错误;

  • 发送空间

    当发送缓冲区满时,试图发送数据的进程可能会在这个队列上休眠,直到缓冲区中有足够的空间进行数据发送;

假设使用浏览器在线观看视频,网速突然变慢或者是视频数据没有到达时,浏览器将希望从套接字收取更多的数据来显示视频,但由于数据尚未到达,浏览器操作数据的进程可能需要等待,对应的这个浏览器进程将会被添加到该套接字socket结构中的wait队列中进行等待;

当网络数据到达兵被操作系统处理后,相关部分的代码会检查有哪些进程在soket中的wait队列上等待,随后唤醒因等待数据到达而阻塞的进程;


文件与套接字相关的调用方法

struct socket结构体中存在一个指针 const struct proto_ops *ops;

这个指针所指向的通常为协议对应的操作方法;

struct proto_ops {int		family;struct module	*owner;int		(*release)   (struct socket *sock);int		(*bind)	     (struct socket *sock,struct sockaddr *myaddr,int sockaddr_len);int		(*connect)   (struct socket *sock,struct sockaddr *vaddr,int sockaddr_len, int flags);int		(*socketpair)(struct socket *sock1,struct socket *sock2);int		(*accept)    (struct socket *sock,struct socket *newsock, int flags);int		(*getname)   (struct socket *sock,struct sockaddr *addr,int *sockaddr_len, int peer);unsigned int	(*poll)	     (struct file *file, struct socket *sock,struct poll_table_struct *wait);int		(*ioctl)     (struct socket *sock, unsigned int cmd,unsigned long arg);int	 	(*compat_ioctl) (struct socket *sock, unsigned int cmd,unsigned long arg);int		(*listen)    (struct socket *sock, int len);int		(*shutdown)  (struct socket *sock, int flags);int		(*setsockopt)(struct socket *sock, int level,int optname, char __user *optval, unsigned int optlen);int		(*getsockopt)(struct socket *sock, int level,int optname, char __user *optval, int __user *optlen);int		(*compat_setsockopt)(struct socket *sock, int level,int optname, char __user *optval, unsigned int optlen);int		(*compat_getsockopt)(struct socket *sock, int level,int optname, char __user *optval, int __user *optlen);int		(*sendmsg)   (struct kiocb *iocb, struct socket *sock,struct msghdr *m, size_t total_len);int		(*recvmsg)   (struct kiocb *iocb, struct socket *sock,struct msghdr *m, size_t total_len,int flags);int		(*mmap)	     (struct file *file, struct socket *sock,struct vm_area_struct * vma);ssize_t		(*sendpage)  (struct socket *sock, struct page *page,int offset, size_t size, int flags);ssize_t 	(*splice_read)(struct socket *sock,  loff_t *ppos,struct pipe_inode_info *pipe, size_t len, unsigned int flags);
};

而在struct file结构体中也同样存在对应的方法集指针const struct file_operations *f_op;

这两个方法集对应着不同的上下文和目的;

其中const struct file_operations *f_op指针所指向的方法集中定义类文件操作的一系列接口方法,如打开open,读取read,写入write释放release等等;

const struct proto_ops *ops对应的方法集则是特定于网络协议套接字操作的方法,对应结构体中包含了一系列的函数指针,这些函数指针定义了网络套接字在不同协议栈(如TCP,UDP等)下的行为,包括套接字的创建create,连接connect,发送sendmsg,接收recvmsg等操作;


系统中的套接字

struct soket结构体中还有一个指针 struct sock *sk将会指向一个类型为struct sock的数据结构,而sock结构体即为网络的开始;

struct sock {// ...rwlock_t		sk_dst_lock;atomic_t		sk_rmem_alloc;atomic_t		sk_wmem_alloc;atomic_t		sk_omem_alloc;int			sk_sndbuf;struct sk_buff_head	sk_receive_queue;struct sk_buff_head	sk_write_queue;// ...
}

struct sock结构体中存在两个队列,分别为接收队列与发送队列,即该段代码中的sk_receive_queuesk_write_queue;

而在Linux内核中,采用了结构体间接嵌套的方式实现了类似继承的功能,其中struct sock结构体即为基类,对应的两个派生类的结构体分别为struct tcp_sockstruct udp_sock;

继承关系如下:

  • struct tcp_sock

    struct tcp_sock结构体中定义了一个struct inet_connection_sock结构体对象,继承于该结构体(类);

    struct tcp_sock {/* inet_connection_sock has to be the first member of tcp_sock */struct inet_connection_sock	inet_conn;// ...
    }
    

    struct inet_connection_sock中包含一个struct inet_sock类型的结构体变量,继承于该结构体(类);

    struct inet_connection_sock {/* inet_sock has to be the first member! */struct inet_sock	  icsk_inet;// ...
    }
    

    而实际struct inet_sock结构体中才包含了一个struct sock类型对象,struct tcp_sock结构体类型间接继承struct sock;

    struct inet_sock {/* sk and pinet6 has to be the first two members of inet_sock */struct sock		sk;// ...
    }
    

  • struct udp_sock

    struct tcp_sock结构不同,struct udp_sock较其要少一层嵌套;

    struct udp_sock结构体中存在一个struct inet_sock类型对象,这表示着struct udp_sock直接继承于struct inet_sock(往前的继承参考struct tcp_sock类型);

对应的继承图即为:

通常应用层在创建对应的套接字时创建的为TCPsocket或者是UDPsocket;

而在调用socket()时所传的参数SOCK_STREAMSOCK_DGRAM则为不同协议的选择;

当选择到对应的选项时将自动实例化一个struct tcp_sock对象或者是struct udp_sock对象;

而对应的struct socket结构体中的struct sock *sk指针将根据选项指向对应的struct tcp_sock对象或者是struct udp_sock对象;

而系统中的struct socket类型中的一些成员属性将辨别所使用的套接字属于TCP还是UDP,当系统需要访问实际套接字中的某些成员属性时只需要进行强制类型转换即可;

即以C语言的方式实现多态;

当进行收发数据时只需要使用struct sock结构中的sk_receive_queuesk_write_queue即可;


网络协议栈与方法集

以TCP/IP四层协议模型为例,分别为应用层,传输层,网络层与数据链路层;

而协议栈的本质就是:

  • 用特定数据结构表述的协议的约定
  • 和特定协议匹配的方法集

在上文中提到了两种方法集,分别为struct file结构体中的const struct file_operations *f_op方法集与struct socket结构体中的 const struct proto_ops *ops方法集;

当对应的struct file中不表示指向磁盘文件而是网络套接字时,对应的const struct file_operations *f_op方法集也不再指向磁盘文件的操作方法,而是指向网络相关的操作方法;

struct socket结构体中的 const struct proto_ops *ops方法集指向的也是网络相关的操作方法;

通常情况下f_op方法集在文件指向网络时,主要的操作作用域是对上的,通常情况下在进行网络通信时,数据的生产位置是位于用户层/应用层的,而f_op方法集则是在这时候通过一些接口将数据从用户层/应用层拷贝至内核层;

而方法集ops则负责将数据由内核发送至网络或者是发送队列中(对UDP而言将直接发送给网络传送给对端,TCP则是需要拷贝至发送队列中);


报文的管理

在任何一端机器,无论是发送方还是接收方,注定系统中将存在大量的报文,而报文同样是需要被管理的,管理的方式同样采用 “先描述再组织” 的方式;

在上文中提到了对应的发送队列与接收队列,即struct sock结构中的sk_receive_queuesk_write_queue,其对应的类型都为struct sk_buff_head;

	struct sk_buff_head	sk_receive_queue;struct sk_buff_head	sk_write_queue;

该类型的定义为:

struct sk_buff_head {/* These two members must be first. */struct sk_buff	*next;struct sk_buff	*prev;__u32		qlen;spinlock_t	lock;
};

这个类型实际上是发送/接收队列的头部,在这个结构体中存在两个指针,struct sk_buff *nextstruct sk_buff *prev,以双链表的形式将报文进行管理,而实际上struct sk_buff则是用来描述报文的类型;

struct sk_buff {/* These two members must be first. */struct sk_buff *next;struct sk_buff *prev;struct sock *sk;               // Associated socketstruct net_device *dev;        // Associated network deviceunsigned int len;              // Total length of dataunsigned int data_len;         // Payload lengthchar cb[48];                   // Control buffer for private use__u32 priority;                // Packet priority__u16 protocol;                // Protocol (e.g., ETH_P_IP, ETH_P_ARP)// ...void (*destructor)(struct sk_buff *skb); // Destructor function// ...__u32			mark;__u16			vlan_tci;sk_buff_data_t		transport_header;sk_buff_data_t		network_header;sk_buff_data_t		mac_header;/* These elements must be at the end, see alloc_skb() for details.  */sk_buff_data_t		tail;sk_buff_data_t		end;unsigned char		*head,*data;unsigned int		truesize;atomic_t		users;
};

通常情况下在操作系统中,报文为了确保数据完整都是一块较为连续的问题,而为了能够确保清楚报文在物理内存中的具体位置,struct sk_buff中存在各种指针来维护与区别报文的具体位置;

为了整个报文在协议栈中层与层之间进行流动,采用统一的方式对报文进行描述组织,每一层在进行封装与解包时只需要移动指针即可;

同时因为接收和发送的动作本质上就是一种生产者和消费者模型,即需要在队列中放置数据或者读取数据,所以在该类型中定义了一把锁,spinlock_t lock;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/476316.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

科研实验室的数字化转型:Spring Boot系统

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理实验室管理系统的相关信息成为必然。开发合…

【前端】CSS修改div滚动条样式

示例 分别是滚动条默认样式和修改后的样式 代码 <div class"video-list"><div class"list-item" onclick"videoinfo(100)"><img src"/index/images/coverimg/方和谦.png"><div class"txt">国医大…

【AIGC】如何使用高价值提示词Prompt提升ChatGPT响应质量

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | 提示词Prompt应用实例 文章目录 &#x1f4af;前言&#x1f4af;提示词英文模板&#x1f4af;提示词中文解析1. 明确需求2. 建议额外角色3. 角色确认与修改4. 逐步完善提示5. 确定参考资料6. 生成和优化提示7. 生成最终响…

centos安装jenkins

本机使用虚拟机centos 7.9.2009 安装gitlab&#xff0c;本机的虚拟机ip地址是 192.168.60.151&#xff0c; 步骤记录如下 1、下载jenkins&#xff0c;安装jenkins之前需要安装jdk jdk和jenkins的版本对应关系参考&#xff1a;Redhat Jenkins Packages Index of /redhat-stable…

使用redis-shake工具进行redis的数据同步

前言&#xff1a; 工作中将常遇到测试环境和正式环境的数据同步或者需要进行数据迁移&#xff0c;对于mysql数据库的方案倒是不少&#xff0c;但是redis中如何快速便捷的迁移呢&#xff1f;答案是阿里云提供的:redis-shake RedisShake是阿里云基于豌豆荚开源的redis-port进行…

04 —— Webpack打包CSS代码

加载器css-loader &#xff1a;解析css代码 webpack 中文文档 | webpack中文文档 | webpack中文网 加载器style-loader&#xff1a;把解析后的css代码插入到DOM style-loader | webpack 中文文档 | webpack中文文档 | webpack中文网 准备css代码&#xff0c;放到src/login目…

Nacos实现IP动态黑白名单过滤

一些恶意用户&#xff08;可能是黑客、爬虫、DDoS 攻击者&#xff09;可能频繁请求服务器资源&#xff0c;导致资源占用过高。因此我们需要一定的手段实时阻止可疑或恶意的用户&#xff0c;减少攻击风险。 本次练习使用到的是Nacos配合布隆过滤器实现动态IP黑白名单过滤 文章…

SAP PI/PO Proxy2JDBC SQL_QUERY动态接口示例

目录 背景&#xff1a; 完整demo步骤&#xff1a; IR: ID: SPROXY: 测试代码&#xff1a; 注意点&#xff1a; 背景&#xff1a; 中途临时帮客户项目做其他功能&#xff0c;项目上有部分开发项需要通过PO去第三方数据库取数&#xff0c;项目上的开发对PO不太熟&#xf…

如何使用本地大模型做数据分析

工具&#xff1a;interpreter --local 样本数据&#xff1a; 1、启动分析工具 2、显示数据文件内容 输入&#xff1a; 显示/Users/wxl/work/example_label.csv 输出&#xff1a;(每次输出的结果可能会不一样&#xff09; 3、相关性分析 输入&#xff1a; 分析客户类型与成…

中间件--laravel进阶篇

laravel版本11.31,这中间件只有3种,分别是全局中间件,路由中间件,控制器中间件。相比thinkphp8,少了一个应用中间件。 一、创建中间件 laravel创建中间件可以使用命令的方式创建,非常方便。比如php artisan make:middleware EnsureTokenIsValid。EnsureTokenIsValid是中间…

一维卷积神经网络(1D-CNN)

一维卷积神经网络&#xff08;1D Convolutional Neural Network, 1D CNN&#xff09;是卷积神经网络的一种变体&#xff0c;专门用于处理序列数据&#xff0c;如时间序列、文本等。 一、基本结构 一维卷积神经网络的基本结构与二维卷积神经网络&#xff08;2D CNN&#xff09;类…

Java中的TreeSet集合解析

记一下java流处理的操作 1.去重&#xff0c;按照billTypeCode去重 list list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getBillTypeCode()))), ArrayList::new)); 排序&#x…

vue中mixin(混入)的使用

目录 mixin(混入) 使用方式 第一步定义混合 ​编辑 第二步使用混入 局部混入 全局混合 mixin(混入) 功能&#xff1a;可以把多个组件共用的配置提取成一个混入对象 使用方式 第一步定义混合 { data(){....}, methods:{....} .... } 第二步使用混入 …

vue中路由缓存

vue中路由缓存 问题描述及截图解决思路关键代码及打印信息截图 问题描述及截图 在使用某一平台时发现当列表页码切换后点击某一卡片进入详情页后&#xff0c;再返回列表页时页面刷新了。这样用户每次看完详情回到列表页都得再重新输入自己的查询条件&#xff0c;或者切换分页到…

Easyexcel(1-注解使用)

相关文章链接&#xff1a; Easyexcel&#xff08;1-注解使用&#xff09; 版本依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.3</version> </dependency>ExcelProperty…

Vue3 -- mock数据完整配置并调试【项目集成6】

引言&#xff1a; ‌Mock在前端开发中的作用主要是模拟后端接口数据&#xff0c;以便前端开发者能够提前进行页面和功能的开发、调试&#xff0c;而无需等待后端提供真实的接口数据‌。Mock数据可以加速前后端开发的协同&#xff0c;避免因数据延迟导致的开发阻塞‌。【摘自百…

开源许可协议

何同学推动了开源协议的认识&#xff0c;功不可没&#xff0c;第一次对开源有了清晰的认识&#xff0c;最宽松的MIT开源协议 源自OSC开源社区&#xff1a;何同学使用开源软件“翻车”&#xff0c;都别吵了&#xff01;扯什么违反MIT

数据结构(顺序栈——c语言实现)

栈的基本概念&#xff1a; 栈是限制在一端进行插入操作和删除操作的线性表&#xff08;俗称堆栈&#xff09;&#xff0c;允许进行操作的一端称为“栈顶”&#xff0c;另一固定端称为“栈底”&#xff0c;当栈中没有元素时称为“空栈” 特点&#xff1a;先进后出&#xff08;FI…

【智谱清言-注册_登录安全分析报告】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

[Realtek sdk-3.4.14b] RTL8197FH-VG新增jffs2分区操作说明

sdk说明 ** Gateway/AP firmware v3.4.14b – Aug 26, 2019**  Wireless LAN driver changes as:  Refine WiFi Stability and Performance  Add 8812F MU-MIMO  Add 97G/8812F multiple mac-clone  Add 97G 2T3R antenna diversity  Fix 97G/8812F/8814B MP issu…