TCP socket api详解 续
文章目录
- 守护进程
- 怎么做到?
- setsid
- 返回值
- dev/null字符文件
- daemon
- TCP协议
退出的时候呢?
会话有很多后台任务,bash肯定会退,那后台会话怎么办呢?
理论上也要退的,但实际上关了bash,bash肯定要退,所有进程的父进程都是bash他就自动托孤,但他们依旧属于同一个会话,所以后台会话还是在系统里被保留下来了,他是没有问题的依旧可以跑的。这写后台进程收到了用户登录和退出的影响的,因为ppid发生改变
图中后台进程关闭bash前后对比
如果我们今天不想受到任何用户登录和注销的影响—守护进程化
注销:Windows中也有类似会话,注销会把会话关闭,前后台会话都关闭,再重启就不卡了。
守护进程
我们要把后台进程转化为
自成进程组+自成会话的进程 --守护进程
所以和曾经打开的bash会话中从包含关系变成并列关系,此时你爱怎么样怎么样和我无关,不再受用户登录和注销的影响了。
怎么做到?
setsid
创建一个会话,谁调用这个setsid就把进程的组ID 设置为会话的id
相当于让一个进程独立成会话
返回值
成功返回新的会话ID,一般都是调用进程的pid,否则-1被返回
函数特点:你要创建新会话,调用进程不能是进程组的组长。
组长是多个进程中的第一个
但是组员可以
基本进程本身自己就是组成,自成进程组,也是组长。
那我们就没办法去调用setsid呢?
我怎么保证自己不是组长呢?
默认可执行程序启动就是组长,pid = pgid
换句话说只要我不是第一个进程,那我就一定不是组长啊。
所以创建守护进程最核心的一部就是fork,如果是父进程直接退出,子进程setsid就一定能成功了。
这其实就是守护进程最核心的点了。
所以守护进程的本质也是孤儿进程!
因为它的父进程直接就退了,所以守护进程立马要被系统领养,他唯一和孤儿进程不一样的是他很坚强,他把自己设置为独立的会话。就不受任何用户登录注销影响。
有些程序需要在根目录,所以有更改守护进程的路径选项
在初始化init ,启动的时候调用这个函数,进去的时候是父进程出去的时候就是子进程,以子进程的身份往后执行。
关键是后续代码有很多输出打印日志,还可能有标准输入,标准错误。
作为守护进程就不应该往显示器打印了。
所以需要关闭标准输入,标准输出,标准错误
那你往已经关闭的输出输出流打印,printf函数不都出错了吗,函数调用全都失败的。
我们决不允许出现这样的错误。
如果你能做到让日志往文件里写,那就和0,1,2就没关系,所以正常。
可是服务器里不用日志还有大量cout cerr 难道就看着他们出错吗,即便不出错,我写个守护进程还有把打印都删掉吗?
怎么样平滑的处理这个问题?
dev/null字符文件
相当于垃圾桶
写进这个文件的数据全部会被丢弃。
堵不如疏,不如将标准输入输出错误全部重定向至/dev/null
如果你想把日志保留,那就让日志往文件里面写。
守护进程没终端,也不会从键盘里面获取了。
谁规定守护进程打印信息必须从显示器上看,如果一个服务已经启动了,这个服务的错误消息不应该在显示器上读,因为他必须把自己的日志信息写到文件里。
打开这个dev/null 文件 把三个流重定向到这个文件中,这个文件后面也不用了就关了,因为文件描述符表已经重定向完成了。
ls /proc/pid -l 内存级进程可视化
ls /proc/4945/fd -l 这不文件描述符表吗哈哈,不过是内存级的所以会闪动,磁盘上没有对应的镜像文件,345是客户端来连我的
那守护进程怎么关呢,直接kill
为什么我们能用shell来访问云服务呢?
因为系统里默认有一个22号服务也就是ssh
shell向sshd -D发起请求,经过网络执行再把结果返回
一般进程如果是守护进程 名字应该以d结尾
难道要让我自己搞这个守护进程吗,有没有好点的做法,不要让我自己写,系统有没有默认的呢?
daemon
默认参数0,0就可以了,和我们的deamon一样
TCP协议
无论三次握手,四次挥手,代码层面感知不到,断开连接你也看不到你只是调close
应用层只是调用connect 发起一次握手,等connect返回就可以了,返回之前双方系统会自动进行三次协商,握手成功至此建立连接成功,握手成功之后调用accept,调用期间没有三次握手没有连接,那么accept就要阻塞,只要三次握手成功了accept才会返回,accept返回之后其实是把底层完成三次握手的连接拿上来,至此我们就获取了一个新的连接,这个连接用新的文件描述符来表征。
第二个呢我们在通信的时候双方都要close关闭不需要的文件描述符,一个关闭一个确认,调一次close会触发两次挥手。相当于我给对方发消息,对方说好的。这叫做两次挥手,连起来就是4次挥手。
三次握手四次挥手如何去理解
三次握手相当于TCP可靠性的一种,他在正式通信前先要三次握手把双方的连接维护好,连接建立好之后,后面你调用的read,write 其实就是基于这个连接来数据通信的
你在学校里,突然发现一个女孩就是我要的白月光朱砂痣,人家也看到了你,你呢要么高帅,你俩对眼了,男生跑过去就和人家说,做我女朋友吧,这是你给对方发的,人家就回你说,好呀,什么时候开始呢?你说,就现在。
至此我们就以最小的沟通成本,顺利的就有了男朋友女朋友。这叫做三次握手。
确定男女朋友关系,后面正常吃饭自习见家长,可是和你处了一段时间,你这货又不上课天天打游戏,脾气还大不思进取,女孩觉得你不太适合她。
女孩就和你说,我们分手吧,你一听你就来气,我付出了那么多,你说好的。
当然也有舔狗同学不和女生分手,你为什么要和我分手?一直给女方发消息,女孩就是不给你回,单向通信的信道已经关闭,你可以给她发,但是这种不太好。
单方面分手是分不了的,必须征得双方的同意,比如结婚是需要离婚的。
所以我们分手吧,你说好的,同意分手。
你跟我分手了对不对,我也要跟你分手,对方说好的。
至此双方明确表明了双方意见,并且互相征得对方的同意
这就叫做协商分手成功。
断开连接是双方的事情,所以四次挥手。
建立连接一定是一方主动(connect)发起三次握手。
TCP通信时全双工的
你再给我发消息时我也可以给你发消息,这两件事情可以同时进行。
TCP是如何做到全双工通信的呢?
TCP底层会提供发送缓冲区,接收缓冲区,一旦创建好了网络套接字,在OS内部针对特定文件描述符,OS给上层就用的sockfd进行读写时,OS就会给你读写缓冲区。
你在用TCP的时候,别人也在用TCP
客户端是一对缓冲区,服务端也是有的。
当我们在网络通信时,你自己也定义了上层缓冲区一个读的一个写的,所以你现在想发消息的时候,通过文件描述符把上层缓冲区数据拷贝到发送缓冲区,然后发送缓冲区会把数据通过网络交给对方的接受缓冲区,所以对方就能读到接受缓冲区的数据了,
因为TCP是既有接受又有发送。所以意味着使用这个文件描述符进行发送的时候,对方也可以使用他的缓冲区把数据通过文件描述符拷贝到自己的发送缓冲区,把数据通过网络拷贝到你的接受缓冲区,再交给上层缓冲区。
说明实际通信时,TCP为什么能做到全双工呢?
因为他的接受和发送缓冲区是独立的,对双方也一样,所以多线程对同一个fd进行读写时,他是不会互相影响的,因为资源是隔离的。
也就是未来对于fd我既可以创建多线程对他读也可以对他写,因为资源底层是分离的,对方也是分离的。
read/write这样的接口本质是一种拷贝函数
把用户缓冲区数据拷贝到发送缓冲区,或者把内核数据拷贝到用户缓冲区。
缓冲区的数据什么时候发,发多少,出错了怎么办,完全由TCP自主决定。
所以把TCP叫做传输控制协议,控制在网络发送时发送主动权由TCP来决定。
所以服务器端客户端可以连我,服务器端可不可以收到来自很多客户的链接呢或者正在保持很多链接?
如何理解连接?
那么有这么多连接,OS要不要管理这些连接呢?要管理,先描述在组织。
所以三次握手成功之后,双发需要在各自内核里创建连接结构体,所以有了链接结构体,我们的OS,所谓的接受发送缓冲区就可以认为在连接结构体里的。最后每一个连接,一个连接结构体对象,一百个连接就100个连接结构体对象,所以把100个结构体对象用链表连接起来,对连接的管理转化成了对链表的增删查改。