dayreading2

来源:互联网 发布:多合一建站源码 编辑:程序博客网 时间:2024/06/09 18:48

udp connect:

标准的udp客户端开了套接口后,一般使用sendto和recvfrom函数来发数据,实际上,udp发送数据有两种方法供大家选用的:

方法一: 
socket----->sendto()或recvfrom() 
方法二: 
socket----->connect()----->send()或recv().(此时sendto,recvfrom仍可用)
从定义可以看出,sendto和recvfrom在收发时指定地址,而send和recv则没有,那么他们的地址是在那里指定的呢,答案就在于connect函数。
在udp编程中,如果你只往一个地址发送,那么你可以使用send和recv,在使用它们之前用connect把它们的目的地址指定一下就可以了。connect函数在udp中就是这个作用,用它来检测udp端口的是否开放的、没有被使用的。
给UDP套接口调用connect,与TCP不同的是:没有三路握手过程。内核只是检查是否存在立即可知的错误(返回一个ICMP)(例如一个显然不可达的目的地),记录对端的IP地址和端口号(取自传递给connect的套接口地址结构),然后立即返回到调用进程.

UDP中可以多次调用connect,TCP只能调用一次connect.  

UDP多次调用connect有两种用途:1,指定一个新的ip&port连结. 2,断开和之前的ip&port的连结.

指定新连结,直接设置connect第二个参数即可.

断开连结,需要将connect第二个参数中的sin_family设置成 AF_UNSPEC即可

5:采用connect的UDP发送接受报文可以调用send,write和recv,read操作.当然也可以调用sendto,recvfrom.

调用sendto的时候第五个参数必须是NULL,第六个参数是0.调用recvfrom,recv,read系统调用只能获取到先前connect的ip&port发送的报文. 

UDP中使用connect的好处:1:会提升效率.前面已经描述了.2:高并发服务中会增加系统稳定性.原因:假设client A 通过非connect的UDP与server B,C通信.B,C提供相同服务.为了负载均衡,我们让A与B,C交替通信.A 与 B通信IPa:PORTa <----> IPb:PORTb;

A 与 C通信IPa:PORTa' <---->IPc:PORTc 

假设PORTa 与 PORTa'相同了(在大并发情况下会发生这种情况),那么就有可能出现A等待B的报文,却收到了C的报文.导致收报错误.解决方法内就是采用connect的UDP通信方式.在A中创建两个udp,然后分别connect到B,C.

 int PASCAL FAR sendto( SOCKET s, const char FAR* buf, int len, int flags,
  const struct sockaddr FAR* to, int tolen);

MSG_DONTROUTE   指明数据不选径。一个WINDOWS套接口供应商可以忽略此标志;参见2.4节中关于SO_DONTROUTE的讨论。
MSG_OOB     发送带外数据(仅适用于SO_STREAM;参见2.2.3节)

 int PASCAL FAR recvfrom( SOCKET s, char FAR* buf, int len, int flags,
  struct sockaddr FAR* from, int FAR* fromlen);

在套接口的所设选项之上,还可用标志位flag来影响函数的执行方式。也就是说,本函数的语义既取决于套接口选项,也取决于标志位参数。标志位可取下列值:
  值 意义
  MSG_PEEK 查看当前数据。数据将被复制到缓冲区中,但并不从输入队列中删除。
  MSG_OOB 处理带外数据(参见2.2.3节具体讨论)。


exit _exit() 区别,结合行缓冲和全缓冲解释.

在linux的标准库函数中,有一套称作高级I/O的函数,我们熟知的printf 、fopen 、fread 、fwrite都在此列,他们也被称作缓冲I/O。其特征是对应每一个打开的文件,都存在一个缓冲区, 在内存中都有一片缓冲区,每次读文件会多读若干条记录,这样下次读文件时就可以直接从内存的缓存中取出,每次写文件时也仅仅是写入到内存的缓冲区,等待满足一定的条件(达到一定的数量,或者遇到特定字符,如换行和文件结束符EOF),再将缓冲区的内容一次性的写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦,如果有些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,他们还只是保存在缓冲区内,这时我们用_exit函数直接将程序关闭,缓冲区中的数据就会丢失,反之,如果向保证数据的完整性,就一定要使用exit函数。

exit()函数与_exit()函数最大的区别就在于exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是图中的"清理I/O缓冲"一项。

exit()函数定义在stdlib.h中,而_exit()定义在unistd.h中。exit()_exit()都用于正常终止一个函数。但_exit()直接是一个sys_exit系统调用,而exit()则通常是普通函数库中的一个函数。它会先执行一些清除操作,例如调用执行各终止处理函数、关闭所有标准IO等,然后调用sys_exit


linux进程调度策略:

 linux进程的调度时机大致分为两种情况: 一种是进程自愿调度;另一种是发生强制性调度。 首先,自愿的调度随时都可以进行。在内核空间中,进程可以通过schedule()启动一次调度;在用户空间中,可以通过系统调用pause()达到同样的目的。如果要为自愿的暂停行为加上时间限制,在内核中使用schedule_time(),而在用户空间则使用nanosleep()系统调用。

Linux支持SCHED_FIFOSCHED_RRSCHED_OTHER的调度策略。

           linux用函数goodness()统一计算进程(包括普通进程和实时进程)的优先级权值,该权值衡量一个处于可运行状态的进程值得运行的程度,权值越大,进程优先级越高。 每个进程的task_struct结构中,与goodness()计算权值相关的域有以下四项:policynice(2.2版内核该项为priority)counterrt_priority。其中,policy是进程的调度策略,其可用来区分实时进程和普通进程,实时进程优先于普通进程运行。nice从最初的UNIX沿用而来,表示进程的静态负向优先级,其取值范围为19~-20,以-20优先级最高。counter表示进程剩余的时间片计数值,由于counter在计算goodness()时起重要作用,因此,counter也可以看作是进程的动态优先级。rt_priority是实时进程特有的,表示实时优先级。

           首先,linux根据调度策略policy从整体上区分实时进程和普通进程。对于policySCHED_OTHER的普通进程,linux采用动态优先级调,其优先级权值取决于(20-nice)和进程当前的剩余时间片计数counter之和。进程创建时,子进程继承父进程的nice值,而父进程的counter值则被分为二半,子进程和父进程各得一半。时间片计数器每次清零后由(20-nice)经过换算重新赋值。字面上看,nice是“优先级”、counter是“计数器”的意思,然而实际上,它们表达的是同个意思:nice决定了分配给该进程的时间片计数,nice优先级越高的进程分到的时间片越长,用户通过系统调用nice()或setpriority()改变进程静态优先级nice值的同时,也改变了该进程的时间片长度;counter表示该进程剩余的时间片计数值,而nicecounter综合起来又决定进程可运行的优先级权值。在进程运行过程中,counter不断减少,而nice保持相对不变;当一个普通进程的时间片用完以后,并不马上根据nicecounter进行重新赋值,只有所有处于可运行状态的普通进程的时间片都用完了以后(counter等于0),才根据nicecounter重新赋值,这个普通进程才有了再次被调度的机会。这说明,普通进程运行过程中,counter的减小给了其它进程得以运行的机会,直至counter减为0时才完全放弃对CPU的使用,这就相当于优先级在动态变化,所以称之为动态优先调度。

                  对于实时进程,linux采用了两种调度策略,即SCHED_FIFO(先来先服务调度)SCHED_RR(时间片轮转调度)。因为实时进程具有一定程度的紧迫性,所以衡量一个实时进程是否应该运行,采用了一个比较固定的标准,即参考rt_priority的值。用函数goodness()计算进程的优先级权值时,对实时进程是在1000的基础上加上rt_priority的值,而非实时进程的动态优先级综合起来的调度权值始终在以下,所以goodness()的优先级权值计算方法确保实时进程的调度权值始终比所有的非实时进程都要大,这就保证了实时进程的优先运行。实时进程的counternice都与其优先级权值无关,这和普通进程是有区别的,实时进程task_struct中的counternice只与SCHED_RR调度策略进程的时间片计数相关

调度程序运行时,要在所有处于可运行状态的进程之中选择最值得运行的进程投入运行。选择进程的依据是什么呢?在每个进程的task_struct 结构中有这么四项: policy, priority , counter, rt_priority 
这四项就是调度程序选择进程的依据.其中,policy是进程的调度策略,用来区分两种进程-实时和普通;priority是进程(实时和普通)的优先 级;counter 是进程剩余的时间片,它的大小完全由priority决定;rt_priority是实时优先级,这是实时进程所特有的,用于实时进程间的选择。 
首先,Linux 根据policy从整体上区分实时进程和普通进程,因为实时进程和普通进程度调度是不同的,它们两者之间,实时进程应该先于普通进程而运行,然后,对于同一类型的不同进程,采用不同的标准来选择进程: 
对于普通进程,Linux采用动态优先调度,选择进程的依据就是进程counter的大小。进程创建时,优先级priority被赋一个初值,一般为 0~70之间的数字,这个数字同时也是计数器counter的初值,就是说进程创建时两者是相等的。字面上看,priority是"优先级"、 counter是"计数器"的意思,然而实际上,它们表达的是同一个意思-进程的"时间片"。Priority代表分配给该进程的时间片,counter 表示该进程剩余的时间片。在进程运行过程中,counter不断减少,而priority保持不变,以便在counter变为0的时候(该进程用完了所分 配的时间片)对counter重新赋值。当一个普通进程的时间片用完以后,并不马上用priority对counter进行赋值,只有所有处于可运行状态 的普通进程的时间片(p->counter==0)都用完了以后,才用priority对counter重新赋值,这个普通进程才有了再次被调度的 机会。这说明,普通进程运行过程中,counter的减小给了其它进程得以运行的机会,直至counter减为0时才完全放弃对CPU的使用,这就相对于 优先级在动态变化,所以称之为动态优先调度。至于时间片这个概念,和其他不同操作系统一样的,Linux的时间单位也是"时钟滴答",只是不同操作系统对 一个时钟滴答的定义不同而已(Linux为10ms)。进程的时间片就是指多少个时钟滴答,比如,若priority为20,则分配给该进程的时间片就为 20个时钟滴答,也就是20*10ms=200ms。Linux中某个进程的调度策略(policy)、优先级(priority)等可以作为参数由用户 自己决定,具有相当的灵活性。内核创建新进程时分配给进程的时间片缺省为200ms(更准确的,应为210ms),用户可以通过系统调用改变它。 
对于实时进程,Linux采用了两种调度策略,即FIFO(先来先服务调度)和RR(时间片轮转调度)。因为实时进程具有一定程度的紧迫性,所以衡量一个 实时进程是否应该运行,Linux采用了一个比较固定的标准。实时进程的counter只是用来表示该进程的剩余时间片,并不作为衡量它是否值得运行的标 准。实时进程的counter只是用来表示该进程的剩余时间片,并不作为衡量它是否值得运行的标准,这和普通进程是有区别的。上面已经看到,每个进程有两 个优先级,实时优先级就是用来衡量实时进程是否值得运行的。 
这一切看来比较麻烦,但实际上Linux中的实现相当简单。Linux用函数goodness()来衡量一个处于可运行状态的进程值得运行的程度。该函数 综合了上面提到的各个方面,给每个处于可运行状态的进程赋予一个权值(weight),调度程序以这个权值作为选择进程的唯一依据。 
Linux根据policy的值将进程总体上分为实时进程和普通进程,提供了三种调度算法:一种传统的Unix调度程序和两个由POSIX.1b(原名为 POSIX.4)操作系统标准所规定的"实时"调度程序。但这种实时只是软实时,不满足诸如中断等待时间等硬实时要求,只是保证了当实时进程需要时一定只 把CPU分配给实时进程。 

非实时进程有两种优先级,一种是静态优先级,另一种是动态优先级。实时进程又增加了第三种优先级,实时优先级。优先级是一些简单的整数,为了决定应该允许哪一个进程使用CPU的资源,用优先级代表相对权值-优先级越高,它得到CPU时间的机会也就越大。 
   静态优先级(priority)-不随时间而改变,只能由用户进行修改。它指明了在被迫和其他进程竞争CPU之前,该进程所应该被允许的时间片的最大值(但很可能的,在该时间片耗尽之前,进程就被迫交出了CPU)。 
   动态优先级(counter)-只要进程拥有CPU,它就随着时间不断减小;当它小于0时,标记进程重新调度。它指明了在这个时间片中所剩余的时间量。 
   实时优先级(rt_priority)-指明这个进程自动把CPU交给哪一个其他进程;较高权值的进程总是优先于较低权值的进程。如果一个进程不是实时进程,其优先级就是0,所以实时进程总是优先于非实时进程的(但实际上,实时进程也会主动放弃CPU)。 

当policy分别为以下值时: 
1) SCHED_OTHER:这是普通的用户进程,进程的缺省类型,采用动态优先调度策略,选择进程的依据主要是根据进程goodness值的大小。这种进程在运行时,可以被高goodness值的进程抢先。 
2) SCHED_FIFO:这是一种实时进程,遵守POSIX1.b标准的FIFO(先入先出)调度规则。它会一直运行,直到有一个进程因I/O阻塞,或者主 动释放CPU,或者是CPU被另一个具有更高rt_priority的实时进程抢先。在Linux实现中,SCHED_FIFO进程仍然拥有时间片-只有 当时间片用完时它们才被迫释放CPU。因此,如同POSIX1.b一样,这样的进程就象没有时间片(不是采用分时)一样运行。Linux中进程仍然保持对 其时间片的记录(不修改counter)主要是为了实现的方便,同时避免在调度代码的关键路径上出现条件判断语句 if (!(current->policy&SCHED_FIFO)){...}-要知道,其他大量非FIFO进程都需要记录时间片,这种多余 的检测只会浪费CPU资源。(一种优化措施,不该将执行时间占10%的代码的运行时间减少到50%;而是将执行时间占90%的代码的运行时间减少到 95%。0.9+0.1*0.5=0.95>0.1+0.9*0.9=0.91) 
3) SCHED_RR:这也是一种实时进程,遵守POSIX1.b标准的RR(循环round-robin)调度规则。除了时间片有些不同外,这种策略与 SCHED_FIFO类似。当SCHED_RR进程的时间片用完后,就被放到SCHED_FIFO和SCHED_RR队列的末尾。 
只要系统中有一个实时进程在运行,则任何SCHED_OTHER进程都不能在任何CPU运行。每个实时进程有一个rt_priority,因此,可以按照rt_priority在所有SCHED_RR进程之间分配CPU。其作用与SCHED_OTHER进程的priority作用一样。只 有root用户能够用系统调用sched_setscheduler,来改变当前进程的类型(sys_nice,sys_setpriority)。 此外,内核还定义了SCHED_YIELD,这并不是一种调度策略,而是截取调度策略的一个附加位。如同前面说明的一样,如果有其他进程需要CPU,它就提示调度程序释放CPU。特别要注意的就是这甚至会引起实时进程把CPU释放给非实时进程。 真正执行调度的函数是schedule(void),它选择一个最合适的进程执行,并且真正进行上下文切换,使得选中的进程得以执行。而 reschedule_idle(struct task_struct *p)的作用是为进程选择一个合适的CPU来执行,如果它选中了某个CPU,则将该CPU上当前运行进程的need_resched标志置为1,然后向它 发出一个重新调度的处理机间中断,使得选中的CPU能够在中断处理返回时执行schedule函数,真正调度进程p在CPU上执行。在 schedule()和reschedule_idle()中调用了goodness()函数。goodness()函数用来衡量一个处于可运行状态的进 程值得运行的程度。

0 0
原创粉丝点击