热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OS:读者写者问题(写者优先+LINUX+多线程+互斥量+代码)(转)

最近想自己写个简单的WEBSERVER,为了先练练手,熟悉下在LINUX系统使用基本的进程、线程、互斥等,就拿以前学过的OS问题开开刀啦。记得当年学读者写者问题,尤其是写者优先的时
最近想自己写个简单的 WEB SERVER ,为了先练练手,熟悉下在LINUX系统使用基本的进程、线程、互斥等,就拿以前学过的 OS 问题开开刀啦。记得当年学读者写者问题,尤其是写者优先的时候,那是真心纠结啊。刚才还觉得理解了,过一会儿又糊涂了。现在重新再看,还是容易纠结。没办法,用得少。我把读者优先和写者优先都实现了一下。选择性重看了小部分《unix高程》使用了多线程+互斥量实现。
 

二. 互斥量与信号量

互斥量如其名,同一时间只能被一个线程占有,实现线程间对某种数据结构的互斥访问。试图对一个已经加锁的互斥量加锁,会导致线程阻塞。允许多个线程对同一个互斥量加锁。当对互斥量解锁时,阻塞在该互斥量上的线程会被唤醒,它们竞争对该互斥量加锁,加锁成功的线程将停止阻塞,剩余的加锁失败于是继续阻塞。注意到,谁将竞争成功是无法预料的,这一点就类似于弱信号量。(强信号量把阻塞在信号量上的进程按时间排队,先进先出)
 
互斥量区别于信号量的地方在于,互斥量只有两种状态,锁定和非锁定。它不像信号量那样可以赋值,甚至可以是负值。共性方面,我所体会到的就一句话,都是用来实现互斥的。至于其它区别或联系,用不上,不作研究。
 
 

三. 读者优先

只要有一个读者正在读,那么后续的读者都能立即读,不管有多少写者在等待。可能导致写者饥饿。

1. 读者

1) 写者写时,不可读
2) 有别的读者正在读,可读

2. 写者

1) 有读者正在读,不可写
2) 有写者正在写,不可写
3) 无读者正在读,无写者正在写,可写

四. 写者优先

当新的写者希望写时,不允许该写者后续的读者访问数据区,但必须保证之前的读者读完。

1. 读者特点

1) 有写者正在写或者等待写,须等到没有写者才能读
2) 没有写者,可以读

2. 写者特点

1) 写者与写者互斥。当其它写者正在写时,其它写者不能写。
2) 写者与读者互斥。之前只有读者在读,当写者出现时,必须等到之前的读者都读完才能写。这尊重了之前读者的意愿。
3) 写者可以有条件地插读者的队。当前有写者正写,有读者在等,这时来了新写者,新写者可以在那些读者之前执行。这不尊重

3. 写者实现

首先,写者的代码应该是这样一种形式,才能保证同一时刻只有一个写者修改数据:
 
  1. 1 while(1)  
    2 {  
    3     pthread_mutex_lock(&writeLock);  
    4     {//临界区,限制只有一个写者修改数据  
    5         write();  
    6     }  
    7     pthread_mutex_unlock(&writeLock);  
    8 }  

     


考虑到写者对读者的影响是:当任何写者想写时,读者都必须被阻塞;并且,写者阻塞了读者并停止阻塞之前,后续的任何写者都会优先于读者执行。这就如同有一个写者队列,当第一个写者入队时,读者完全被阻塞,直到最后一个写者离开队列。
据此,可以用 writerCnt 来统计写者的数量,而用互斥量 accessWriterCnt 来互斥各线程对 writerCnt 的访问。代码应作如下调整:
 
  1.  1 while(1)  
     2 {  
     3     pthread_mutex_lock(&accessWriterCnt);  
     4     {//临界区,希望修改 writerCnt,独占 writerCnt  
     5         writerCnt++;  
     6         if(writerCnt == 1){  
     7             //读者与写者互斥;使用readerLock来描述;  
     8             pthread_mutex_lock(&readerLock);  
     9         }  
    10     }  
    11     pthread_mutex_unlock(&accessWriterCnt);  
    12       
    13       
    14     pthread_mutex_lock(&writeLock);  
    15     {//临界区,限制只有一个写者修改数据  
    16         write();  
    17     }  
    18     pthread_mutex_unlock(&writeLock);  
    19       
    20     pthread_mutex_lock(&accessWriterCnt);  
    21     {//临界区,希望修改 writerCnt,独占 writerCnt  
    22         writerCnt--;  
    23         if(writerCnt == 0){  
    24             //阻止后续的读者加入待读队列  
    25             pthread_mutex_unlock(&readerLock);  
    26         }  
    27     }  
    28     pthread_mutex_unlock(&accessWriterCnt);  
    29     sleep(W_SLEEP);  
    30 }  

     

4. 读者的实现

读者的实现首先必须保证支持多个读者同时读,因此下面的代码不合适:
 
 
  1. 1 while(1)  
    2 {  
    3     pthread_mutex_lock(&readerLock);  
    4     {//临界区  
    5         read();  
    6     }  
    7     pthread_mutex_unlock(&readerLock);  
    8 }

     

      
在上面的实现中,很明显的问题是,当一个读者执行 read() 的同时,不可能有另外的读者进入临界区并执行 read() 函数了。因此,必须确保在执行 read() 函数之前对readerLock解锁。代码类似于:

 
  1. 1 while(1)  
    2 {  
    3     pthread_mutex_lock(&readerLock);  
    4     pthread_mutex_unlock(&readerLock);  
    5     read();  
    6 }  

     

于是乎,现在我们可以注意到,假如写者率先锁定了 readerLock , 那么后续的读者被阻塞在 pthread_mutex_lock(&readerLock); 了,这点很正确。在写者最终释放 readerLock 之前,可能有成千上万的读者都被阻塞在 readerLock 上, readerLock 释放之后,这些读者会竞争这 readerLock,嗯,这没什么问题。问题在于,此后,将可能有新的写者有写需求并希望获得这把锁(参照一下写者代码吧),那么,此时,写者不得不和成千上万的读者一起竞争 readerLock,由于将占有 readerLock 的人使随机的,写者在数量上不占优势,将进入饥饿状态。因此,这种实现无法满足“写者优先”的需求。
通过添加另一个互斥量,我们可以把写者的这些成千上万的竞争者不再竞争 readerLock ,下面的方案里,当读者释放锁时,假如写者想获得这把锁,那么它能立即得到。


 
  1. 1 while(1)  
    2 {  
    3     pthread_mutex_lock(&outerLock);//成千上万的读者被锁在这里  
    4     pthread_mutex_lock(&readerLock);//只被一个读者占有  
    5       
    6     pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock  
    7     pthread_mutex_unlock(&outerLock);  
    8     read();  
    9 }  

     


为了防止执行 read() 的同时,写者正在执行 write(),所以,有必要在读者的代码中,等待 write() 函数执行完,注意到写者的代码中,使用writeLock 互斥量来保护数据:

 
  1. 1 pthread_mutex_lock(&writeLock);  
    2 {//临界区,限制只有一个写者修改数据  
    3     write();  
    4 }  
    5 pthread_mutex_unlock(&writeLock);  

     


因而,读者也可以通过对 writeLock 加锁以保证读的时候没有同时写,但是下面这种做法又导致读者无法并发:

 
  1.  1 while(1)  
     2 {  
     3     pthread_mutex_lock(&outerLock);//成千上万的读者被锁在这里  
     4     pthread_mutex_lock(&readerLock);//只被一个读者占有  
     5       
     6     pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock  
     7     pthread_mutex_unlock(&outerLock);  
     8       
     9     pthread_mutex_lock(&writeLock);//每个执行到这里的读者都企图对 writeLock 加锁  
    10     {//临界区  
    11         read();//同一时刻只能有一个读者在这里,无法实现读并发  
    12     }  
    13     pthread_mutex_unlock(&writeLock);  
    14 }  

     




我们需要更合理地安排 lock(&writeLock) 的位置,并且需要避免每个读者都对 writeLock 加锁,即,只让某个读者对 writeLock 加锁,同时还保证读的时候,没有写者在写 。
注意到,假如有多个读者并发地执行 read() 函数,那么,只要在第一个读者开始读之前锁住 writeLock 在最后一个读者执行 read() 之后解锁 writeLock,就可以解决读者并发的问题。
我们需要为读者添加两段代码:
代码段1:

 
  1. 1 pthread_mutex_lock(&accessReaderCnt);  
    2 {//临界区  
    3     readerCnt++;  
    4     if(readerCnt == 1){//第一个并发读线程  
    5         pthread_mutex_lock(&writeLock);//在第一个并发读者这里开始禁止写者执行写操作  
    6     }  
    7 }  
    8 pthread_mutex_unlock(&accessReaderCnt);  

     




代码段2:

  1. 1 pthread_mutex_lock(&accessReaderCnt);  
    2 {//临界区  
    3     readerCnt--;  
    4     if(readerCnt == 0){//最后一个并发读线程  
    5         pthread_mutex_unlock(&writeLock);//在最后一个并发读者read()结束后写者可以执行写操作  
    6     }  
    7 }  
    8 pthread_mutex_unlock(&accessReaderCnt);  

     



代码段2 明显应该放在 read() 函数之后;

 
  1.  1 while(1)  
     2 {  
     3   
     4     //代码段1 可能的位置 0  
     5     pthread_mutex_lock(&outerLock);//假如写者锁定了readerLock,那么成千上万的读者被锁在这里  
     6     //代码段1 可能的位置 1  
     7     pthread_mutex_lock(&readerLock);//只被一个读者占有  
     8     //代码段1 可能的位置 2  
     9     pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock  
    10     //代码段1 可能的位置 3  
    11     pthread_mutex_unlock(&outerLock);  
    12     //代码段1 可能的位置 4  
    13   
    14     read();  
    15     pthread_mutex_lock(&accessReaderCnt);//代码段2  
    16     {//临界区  
    17         readerCnt--;  
    18         if(readerCnt == 0){  
    19             pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作  
    20         }  
    21     }  
    22     pthread_mutex_unlock(&accessReaderCnt);  
    23 }  

     

 
经典的做法是把代码段1放到位置2,如果是这样,那么代码如下,我故意用了很多块作用域(用花括号{}表示)来凸显临界区:
 

 
  1.  1 while(1)  
     2 {  
     3     pthread_mutex_lock(&outerLock);//假如写者锁定了readerLock,那么成千上万的读者被锁在这里  
     4     {//临界区  
     5         pthread_mutex_lock(&readerLock);//只被一个读者占有  
     6         {//临界区  
     7             pthread_mutex_lock(&accessReaderCnt);//代码段 1  
     8             {//临界区  
     9                 readerCnt++;  
    10                 if(readerCnt == 1){  
    11                     pthread_mutex_lock(&writeLock);  
    12                 }  
    13             }  
    14             pthread_mutex_unlock(&accessReaderCnt);  
    15         }  
    16         pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock  
    17     }  
    18     pthread_mutex_unlock(&outerLock);  
    19   
    20     read();  
    21       
    22     pthread_mutex_lock(&accessReaderCnt);//代码段2  
    23     {//临界区  
    24         readerCnt--;  
    25         if(readerCnt == 0){  
    26             pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作  
    27         }  
    28     }  
    29     pthread_mutex_unlock(&accessReaderCnt);  
    30 }  

     

如此布局的意义在于,必须放在 lock(&readerLock) 和 unlock(&readerLock) 之间锁定 writeLock 。否则,一旦读者释放了 readerLock ,后续的写进程(如果有)可能会率先获得 writeLock 锁,从而导致比读者后发生的写者先执行。
提示:读者在lock(&readerLock) 和 unlock(&readerLock) 之间锁定 writeLock 是绝对不会阻塞的。因为读者已经占据了 readerLock 互斥量,这意味着,即便有写者,它也不可能已经占据了 writeLock 锁。
将片段1放在其它位置 无法起到这种效果。但我觉得这么做没啥意义,因为会导致代码本身的矛盾:
1)按照老 写者、读者、新写者的出现顺序,假设新写者出现时,老写者还没完(此时读者是阻塞的),那么新写者就可以打破顺序,优先于读者执行。
2)按照 读者、写者的出现顺序,假设写者出现时读者还没完,那代码会要求必须等读者执行完。
 
与其做作地无意义,不如淫荡地统一。
 
 
 

5. 运行结果

频率调高时,眼花缭乱,我把输出重定向到文件里,然后使用 cat data.out | uniq 去除一些重复的输出,虽然不能说明代码正确,不过没有看到错误~
read 0
write 383
write 886
read 886
write 777
write 915
read 915
write 793
write 335
read 335

没出现什么乱子
 

嗯,该睡了,不喜熬夜。

附录. 

1. 读者优先代码


 
  1.  1 /* 
     2 *  多线程,读者优先 
     3 */  
     4   
     5 #include "stdio.h"  
     6 #include   
     7 #include   
     8   
     9   
    10 #define N_WRITER 3 //写者数目  
    11 #define N_READER 5 //读者数目  
    12 #define W_SLEEP  1 //控制写频率  
    13 #define R_SLEEP  1 //控制读频率  
    14   
    15   
    16 pthread_t wid[N_WRITER],rid[N_READER];  
    17 const int MAX_RAND = 1000;//产生的最大随机数  
    18 pthread_mutex_t writeLock = PTHREAD_MUTEX_INITIALIZER;//同一时间只能一个人写文件,互斥  
    19 pthread_mutex_t accessReaderCnt = PTHREAD_MUTEX_INITIALIZER;//同一时间只能有一个人访问 readerCnt  
    20 int data = 0;  
    21 int readerCnt = 0;  
    22 void write()  
    23 {  
    24     int rd = rand();  
    25     printf("write %d\n",rd);  
    26     data = rd;  
    27 }  
    28 void read()  
    29 {  
    30     printf("read %d\n",data);  
    31 }  
    32 void * writer(void * in)  
    33 {  
    34     while(1)  
    35     {  
    36         pthread_mutex_lock(&writeLock);  
    37         write();  
    38         pthread_mutex_unlock(&writeLock);  
    39         sleep(W_SLEEP);  
    40     }  
    41     pthread_exit((void *) 0);  
    42 }  
    43   
    44 void * reader (void * in)  
    45 {  
    46     while(1)  
    47     {  
    48         pthread_mutex_lock(&accessReaderCnt);  
    49         readerCnt++;  
    50         if(readerCnt == 1){  
    51             pthread_mutex_lock(&writeLock);  
    52         }  
    53         pthread_mutex_unlock(&accessReaderCnt);  
    54           
    55         read();  
    56           
    57         pthread_mutex_lock(&accessReaderCnt);  
    58         readerCnt--;  
    59         if(readerCnt == 0){  
    60             pthread_mutex_unlock(&writeLock);  
    61         }  
    62         pthread_mutex_unlock(&accessReaderCnt);  
    63         sleep(R_SLEEP);  
    64     }  
    65     pthread_exit((void *) 0);  
    66 }  
    67   
    68 int main()  
    69 {  
    70     int i = 0;  
    71     for(i = 0; i )  
    72     {  
    73         pthread_create(&wid[i],NULL,reader,NULL);  
    74     }  
    75     for(i = 0; i )  
    76     {  
    77         pthread_create(&rid[i],NULL,writer,NULL);  
    78     }  
    79     while(1){  
    80         sleep(10);  
    81     }  
    82     return 0;  
    83 }  

     



 

2. 写者优先代码


  1.   1 #include "stdio.h"  
      2 #include   
      3 #include   
      4   
      5   
      6 #define N_WRITER 2 //写者数目  
      7 #define N_READER 20 //读者数目  
      8 #define W_SLEEP 1 //控制写频率  
      9 #define R_SLEEP  0.5 //控制读频率  
     10   
     11   
     12 pthread_t wid[N_WRITER],rid[N_READER];  
     13 const int MAX_RAND = 1000;//产生的最大随机数  
     14 int data = 0;  
     15 int readerCnt = 0, writerCnt = 0;  
     16 pthread_mutex_t accessReaderCnt = PTHREAD_MUTEX_INITIALIZER;  
     17 pthread_mutex_t accessWriterCnt = PTHREAD_MUTEX_INITIALIZER;  
     18 pthread_mutex_t writeLock = PTHREAD_MUTEX_INITIALIZER;  
     19 pthread_mutex_t readerLock = PTHREAD_MUTEX_INITIALIZER;  
     20 pthread_mutex_t outerLock = PTHREAD_MUTEX_INITIALIZER;  
     21   
     22 void write()  
     23 {  
     24     int rd = rand()%MAX_RAND;  
     25     printf("write %d\n",rd);  
     26     data = rd;  
     27 }  
     28 void read()  
     29 {  
     30     printf("read %d\n",data);  
     31 }  
     32 void * writer(void * in)  
     33 {  
     34     while(1)  
     35     {  
     36         pthread_mutex_lock(&accessWriterCnt);  
     37         {//临界区,希望修改 writerCnt,独占 writerCnt  
     38             writerCnt++;  
     39             if(writerCnt == 1){  
     40                 //阻止后续的读者加入待读队列  
     41                 pthread_mutex_lock(&readerLock);  
     42             }  
     43         }  
     44         pthread_mutex_unlock(&accessWriterCnt);  
     45           
     46           
     47         pthread_mutex_lock(&writeLock);  
     48         {//临界区,限制只有一个写者修改数据  
     49             write();  
     50         }  
     51         pthread_mutex_unlock(&writeLock);  
     52           
     53         pthread_mutex_lock(&accessWriterCnt);  
     54         {//临界区,希望修改 writerCnt,独占 writerCnt  
     55             writerCnt--;  
     56             if(writerCnt == 0){  
     57                 //阻止后续的读者加入待读队列  
     58                 pthread_mutex_unlock(&readerLock);  
     59             }  
     60         }  
     61         pthread_mutex_unlock(&accessWriterCnt);  
     62         sleep(W_SLEEP);  
     63     }  
     64     pthread_exit((void *) 0);  
     65 }  
     66   
     67 void * reader (void * in)  
     68 {  
     69     while(1)  
     70     {  
     71         //假如写者锁定了readerLock,那么成千上万的读者被锁在这里  
     72         pthread_mutex_lock(&outerLock);  
     73         {//临界区  
     74             pthread_mutex_lock(&readerLock);//只被一个读者占有  
     75             {//临界区  
     76                 pthread_mutex_lock(&accessReaderCnt);//代码段 1  
     77                 {//临界区  
     78                     readerCnt++;  
     79                     if(readerCnt == 1){  
     80                         pthread_mutex_lock(&writeLock);  
     81                     }  
     82                 }  
     83                 pthread_mutex_unlock(&accessReaderCnt);  
     84             }  
     85             pthread_mutex_unlock(&readerLock);//释放时,写者将优先获得readerLock  
     86         }  
     87         pthread_mutex_unlock(&outerLock);  
     88   
     89         read();  
     90           
     91         pthread_mutex_lock(&accessReaderCnt);//代码段2  
     92         {//临界区  
     93             readerCnt--;  
     94             if(readerCnt == 0){  
     95                 pthread_mutex_unlock(&writeLock);//在最后一个并发读者读完这里开始禁止写者执行写操作  
     96             }  
     97         }  
     98         pthread_mutex_unlock(&accessReaderCnt);  
     99           
    100         sleep(R_SLEEP);  
    101     }  
    102     pthread_exit((void *) 0);  
    103 }  
    104   
    105 int main()  
    106 {  
    107     int i = 0;  
    108     for(i = 0; i )  
    109     {  
    110         pthread_create(&rid[i],NULL,reader,NULL);  
    111     }  
    112     for(i = 0; i )  
    113     {  
    114         pthread_create(&wid[i],NULL,writer,NULL);  
    115     }  
    116     while(1){  
    117         sleep(10);  
    118     }  
    119     return 0;  
    120 }  

     

 
转自:http://blog.csdn.net/yaozhiyi/article/details/7563869

推荐阅读
  • Skywalking系列博客1安装单机版 Skywalking的快速安装方法
    本文介绍了如何快速安装单机版的Skywalking,包括下载、环境需求和端口检查等步骤。同时提供了百度盘下载地址和查询端口是否被占用的命令。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
author-avatar
手机用户2602883655
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有