青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Linux程序設計入門--消息管理


前言:Linux下的進程通信(IPC)
Linux下的進程通信(IPC)
POSIX無名信號量
System V信號量
System V消息隊列
System V共享內存

1。POSIX無名信號量 如果你學習過操作系統,那么肯定熟悉PV操作了.PV操作是原子
操作.也就是操作是不可以中斷的,在一定的時間內,只能夠有一個進程的代碼在CPU上面
執行.在系統當中,有時候為了順利的使用和保護共享資源,大家提出了信號的概念. 假設
我們要使用一臺打印機,如果在同一時刻有兩個進程在向打印機輸出,那么最終的結果會
是什么呢.為了處理這種情況,POSIX標準提出了有名信號量和無名信號量的概念,由于Li
nux只實現了無名信號量,我們在這里就只是介紹無名信號量了. 信號量的使用主要是用
來保護共享資源,使的資源在一個時刻只有一個進程所擁有.為此我們可以使用一個信號
燈.當信號燈的值為某個值的時候,就表明此時資源不可以使用.否則就表>示可以使用.
為了提供效率,系統提供了下面幾個函數
POSIX的無名信號量的函數有以下幾個:
  1. #include <semaphore.h>    
  2. int sem_init(sem_t *sem,int pshared,unsigned int value);    
  3. int sem_destroy(sem_t *sem);    
  4. int sem_wait(sem_t *sem);    
  5. int sem_trywait(sem_t *sem);    
  6. int sem_post(sem_t *sem);    
  7. int sem_getvalue(sem_t *sem);    

sem_init創建一個信號燈,并初始化其值為value.pshared決定了信號量能否在幾個進程
間共享.由于目前Linux還沒有實現進程間共享信號燈,所以這個值只能夠取0. sem_dest
roy是用來刪除信號燈的.sem_wait調用將阻塞進程,直到信號燈的值大于0.這個函數返回
的時候自動的將信號燈的值的件一.sem_post和sem_wait相反,是將信號燈的內容加一同
時發出信號喚醒等待的進程..sem_trywait和sem_wait相同,不過不阻塞的,當信號燈的值
為0的時候返回EAGAIN,表示以后重試.sem_getvalue得到信號燈的值.
由于Linux不支持,我們沒有辦法用源程序解釋了.
這幾個函數的使用相當簡單的.比如我們有一個程序要向一個系統打印機打印兩頁.我們
首先創建一個信號燈,并使其初始值為1,表示我們有一個資源可用.然后一個進程調用se
m_wait由于這個時候信號燈的值為1,所以這個函數返回,打印機開始打印了,同時信號燈
的值為0 了. 如果第二個進程要打印,調用sem_wait時候,由于信號燈的值為0,資源不可
用,于是被阻塞了.當第一個進程打印完成以后,調用sem_post信號燈的值為1了,這個時候
系統通知第二個進程,于是第二個進程的sem_wait返回.第二個進程開始打印了.
不過我們可以使用線程來解決這個問題的.我們會在后面解釋什么是線程的.編譯包含上
面這幾個函數的程序要加上 -lrt選賢,以連接librt.so庫
2。System V信號量 為了解決上面哪個問題,我們也可以使用System V信號量.很幸運的
是Linux實現了System V信號量.這樣我們就可以用實例來解釋了. System V信號量的函
數主要有下面幾個.
 
  1. #include <sys/types.h>    
  2. #include <sys/ipc.h>    
  3. #include <sys/sem.h>    
  4. key_t ftok(char *pathname,char proj);    
  5. int semget(key_t key,int nsems,int semflg);    
  6. int semctl(int semid,int semnum,int cmd,union semun arg);    
  7. int semop(int semid,struct sembuf *spos,int nspos);    
  8. struct sembuf {    
  9. short sem_num; /* 使用那一個信號 */    
  10. short sem_op; /* 進行什么操作 */    
  11. short sem_flg; /* 操作的標志 */    
  12. };    

ftok函數是根據pathname和proj來創建一個關鍵字.semget創建一個信號量.成功時返回
信號的ID,key是一個關鍵字,可以是用ftok創建的也可以是IPC_PRIVATE表明由系統選用
一個關鍵字. nsems表明我們創建的信號個數.semflg是創建的權限標志,和我們創建一個
文件的標志相同.
semctl對信號量進行一系列的控制.semid是要操作的信號標志,semnum是信號的個數,cm
d是操作的命令.經常用的兩個值是:SETVAL(設置信號量的值)和IPC_RMID(刪除信號燈).
arg是一個給cmd的參數.
semop是對信號進行操作的函數.semid是信號標志,spos是一個操作數組表明要進行什么
操作,nspos表明數組的個數. 如果sem_op大于0,那么操作將sem_op加入到信號量的值中
,并喚醒等待信號增加的進程. 如果為0,當信號量的值是0的時候,函數返回,否則阻塞直
到信號量的值為0. 如果小于0,函數判斷信號量的值加上這個負值.如果結果為0喚醒等待
信號量為0的進程,如果小與0函數阻塞.如果大于0,那么從信號量里面減去這個值并返回
..
下面我們一以一個實例來說明這幾個函數的使用方法.這個程序用標準錯誤輸出來代替我
們用的打印機.
 
  1. #include <stdio.h>    
  2. #include <unistd.h>    
  3. #include <limits.h>    
  4. #include <errno.h>    
  5. #include <string.h>    
  6. #include <stdlib.h>    
  7. #include <sys/stat.h>    
  8. #include <sys/wait.h>    
  9. #include <sys/ipc.h>    
  10. #include <sys/sem.h>    
  11. #define PERMS S_IRUSR|S_IWUSR    
  12. void init_semaphore_struct(struct sembuf *sem,int semnum,    
  13. int semop,int semflg)    
  14. {    
  15. /* 初始話信號燈結構 */    
  16. sem->sem_num=semnum;    
  17. sem->sem_op=semop;    
  18. sem->sem_flg=semflg;    
  19. }    
  20. int del_semaphore(int semid)    
  21. {    
  22. /* 信號燈并不隨程序的結束而被刪除,如果我們沒刪除的話(將1改為0)   
  23. 可以用ipcs命令查看到信號燈,用ipcrm可以刪除信號燈的   
  24. */    
  25. #if 1    
  26. return semctl(semid,0,IPC_RMID);    
  27. #endif    
  28. }    
  29. int main(int argc,char **argv)    
  30. {    
  31. char buffer[MAX_CANON],*c;    
  32. int i,n;    
  33. int semid,semop_ret,status;    
  34. pid_t childpid;    
  35. struct sembuf semwait,semsignal;    
  36. if((argc!=2)||((n=atoi(argv[1]))<1))    
  37. {    
  38. fprintf(stderr,"Usage:%s number\n\a",argv[0]);    
  39. exit(1);    
  40. }    
  41. /* 使用IPC_PRIVATE 表示由系統選擇一個關鍵字來創建 */    
  42. /* 創建以后信號燈的初始值為0 */    
  43. if((semid=semget(IPC_PRIVATE,1,PERMS))==-1)    
  44. {    
  45. fprintf(stderr,"[%d]:Acess Semaphore Error:%s\n\a",    
  46. getpid(),strerror(errno));    
  47. exit(1);    
  48. }    
  49. /* semwait是要求資源的操作(-1) */    
  50. init_semaphore_struct(&semwait,0,-1,0);    
  51. /* semsignal是釋放資源的操作(+1) */    
  52. init_semaphore_struct(&semsignal,0,1,0);    
  53. /* 開始的時候有一個系統資源(一個標準錯誤輸出) */    
  54. if(semop(semid,&semsignal,1)==-1)    
  55. {    
  56. fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",    
  57. getpid(),strerror(errno));    
  58. if(del_semaphore(semid)==-1)    
  59. fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",    
  60. getpid(),strerror(errno));    
  61. exit(1);    
  62. }    
  63. /* 創建一個進程鏈 */    
  64. for(i=0;i<n;i++)    
  65. if(childpid=fork()) break;    
  66. sprintf(buffer,"[i=%d]-->[Process=%d]-->[Parent=%d]-->[Child=%d]\n",    
  67. i,getpid(),getppid(),childpid);    
  68. c=buffer;    
  69. /* 這里要求資源,進入原子操作 */    
  70. while(((semop_ret=semop(semid,&semwait,1))==-1)&&(errno==EINTR));    
  71. if(semop_ret==-1)    
  72. {    
  73. fprintf(stderr,"[%d]:Decrement Semaphore Error:%s\n\a",    
  74. getpid(),strerror(errno));    
  75. }    
  76. else    
  77. {    
  78. while(*c!='\0')fputc(*c++,stderr);    
  79. /* 原子操作完成,趕快釋放資源 */    
  80. while(((semop_ret=semop(semid,&semsignal,1))==-1)&&(errno==EINTR));    
  81. if(semop_ret==-1)    
  82. fprintf(stderr,"[%d]:Increment Semaphore Error:%s\n\a",    
  83. getpid(),strerror(errno));    
  84. }    
  85. /* 不能夠在其他進程反問信號燈的時候,我們刪除了信號燈 */    
  86. while((wait(&status)==-1)&&(errno==EINTR));    
  87. /* 信號燈只能夠被刪除一次的 */    
  88. if(i==1)    
  89. if(del_semaphore(semid)==-1)    
  90. fprintf(stderr,"[%d]:Destroy Semaphore Error:%s\n\a",    
  91. getpid(),strerror(errno));    
  92. exit(0);    
  93. }    

信號燈的主要用途是保護臨界資源(在一個時刻只被一個進程所擁有).
3。SystemV消息隊列 為了便于進程之間通信,我們可以使用管道通信 SystemV也提供了
一些函數來實現進程的通信.這就是消息隊列.
 
  1. #include <sys/types.h>    
  2. #include <sys/ipc.h>    
  3. #include <sys/msg.h>    
  4. int msgget(key_t key,int msgflg);    
  5. int msgsnd(int msgid,struct msgbuf *msgp,int msgsz,int msgflg);    
  6. int msgrcv(int msgid,struct msgbuf *msgp,int msgsz,    
  7. long msgtype,int msgflg);    
  8. int msgctl(Int msgid,int cmd,struct msqid_ds *buf);    
  9.   
  10. struct msgbuf {    
  11. long msgtype; /* 消息類型 */    
  12. ....... /* 其他數據類型 */    
  13. }    

msgget函數和semget一樣,返回一個消息隊列的標志.msgctl和semctl是對消息進行控制
.. msgsnd和msgrcv函數是用來進行消息通訊的.msgid是接受或者發送的消息隊列標志.
msgp是接受或者發送的內容.msgsz是消息的大小. 結構msgbuf包含的內容是至少有一個
為msgtype.其他的成分是用戶定義的.對于發送函數msgflg指出緩沖區用完時候的操作.
接受函數指出無消息時候的處理.一般為0. 接收函數msgtype指出接收消息時候的操作.

如果msgtype=0,接收消息隊列的第一個消息.大于0接收隊列中消息類型等于這個值的第
一個消息.小于0接收消息隊列中小于或者等于msgtype絕對值的所有消息中的最小一個消
息. 我們以一個實例來解釋進程通信.下面這個程序有server和client組成.先運行服務
端后運行客戶端.
服務端 server.c
 
  1. #include <stdio.h>    
  2. #include <string.h>    
  3. #include <stdlib.h>    
  4. #include <errno.h>    
  5. #include <unistd.h>    
  6. #include <sys/types.h>    
  7. #include <sys/ipc.h>    
  8. #include <sys/stat.h>    
  9. #include <sys/msg.h>    
  10. #define MSG_FILE "server.c"    
  11. #define BUFFER 255    
  12. #define PERM S_IRUSR|S_IWUSR    
  13. struct msgtype {    
  14. long mtype;    
  15. char buffer[BUFFER+1];    
  16. };    
  17. int main()    
  18. {    
  19. struct msgtype msg;    
  20. key_t key;    
  21. int msgid;    
  22. if((key=ftok(MSG_FILE,'a'))==-1)    
  23. {    
  24. fprintf(stderr,"Creat Key Error:%s\a\n",strerror(errno));    
  25. exit(1);    
  26. }    
  27. if((msgid=msgget(key,PERM|IPC_CREAT|IPC_EXCL))==-1)    
  28. {    
  29. fprintf(stderr,"Creat Message Error:%s\a\n",strerror(errno));    
  30. exit(1);    
  31. }    
  32. while(1)    
  33. {    
  34. msgrcv(msgid,&msg,sizeof(struct msgtype),1,0);    
  35. fprintf(stderr,"Server Receive:%s\n",msg.buffer);    
  36. msg.mtype=2;    
  37. msgsnd(msgid,&msg,sizeof(struct msgtype),0);    
  38. }    
  39. exit(0);    
  40. }    
  41. ----------------------------------------------------------------------------    

客戶端(client.c)
 
  1. #include <stdio.h>    
  2. #include <string.h>    
  3. #include <stdlib.h>    
  4. #include <errno.h>    
  5. #include <sys/types.h>    
  6. #include <sys/ipc.h>    
  7. #include <sys/msg.h>    
  8. #include <sys/stat.h>    
  9. #define MSG_FILE "server.c"    
  10. #define BUFFER 255    
  11. #define PERM S_IRUSR|S_IWUSR    
  12. struct msgtype {    
  13. long mtype;    
  14. char buffer[BUFFER+1];    
  15. };    
  16. int main(int argc,char **argv)    
  17. {    
  18. struct msgtype msg;    
  19. key_t key;    
  20. int msgid;    
  21. if(argc!=2)    
  22. {    
  23. fprintf(stderr,"Usage:%s string\n\a",argv[0]);    
  24. exit(1);    
  25. }    
  26. if((key=ftok(MSG_FILE,'a'))==-1)    
  27. {    
  28. fprintf(stderr,"Creat Key Error:%s\a\n",strerror(errno));    
  29. exit(1);    
  30. }    
  31. if((msgid=msgget(key,PERM))==-1)    
  32. {    
  33. fprintf(stderr,"Creat Message Error:%s\a\n",strerror(errno));    
  34. exit(1);    
  35. }    
  36. msg.mtype=1;    
  37. strncpy(msg.buffer,argv[1],BUFFER);    
  38. msgsnd(msgid,&msg,sizeof(struct msgtype),0);    
  39. memset(&msg,'\0',sizeof(struct msgtype));    
  40. msgrcv(msgid,&msg,sizeof(struct msgtype),2,0);    
  41. fprintf(stderr,"Client receive:%s\n",msg.buffer);    
  42. exit(0);    
  43. }    

注意服務端創建的消息隊列最后沒有刪除,我們要使用ipcrm命令來刪除的.
4。SystemV共享內存 還有一個進程通信的方法是使用共享內存.SystemV提供了以下幾個
函數以實現共享內存.
 
  1. #include <sys/types.h>    
  2. #include <sys/ipc.h>    
  3. #include <sys/shm.h>    
  4. int shmget(key_t key,int size,int shmflg);    
  5. void *shmat(int shmid,const void *shmaddr,int shmflg);    
  6. int shmdt(const void *shmaddr);    
  7. int shmctl(int shmid,int cmd,struct shmid_ds *buf);    

shmget和shmctl沒有什么好解釋的.size是共享內存的大小. shmat是用來連接共享內存
的.shmdt是用來斷開共享內存的.不要被共享內存詞語嚇倒,共享內存其實很容易實現和
使用的.shmaddr,shmflg我們只要用0代替就可以了.在使用一個共享內存之前我們調用s
hmat得到共享內存的開始地址,使用結束以后我們使用shmdt斷開這個內存.
 
  1. #include <stdio.h>    
  2. #include <string.h>    
  3. #include <errno.h>    
  4. #include <unistd.h>    
  5. #include <sys/stat.h>    
  6. #include <sys/types.h>    
  7. #include <sys/ipc.h>    
  8. #include <sys/shm.h>    
  9. #define PERM S_IRUSR|S_IWUSR    
  10. int main(int argc,char **argv)    
  11. {    
  12. int shmid;    
  13. char *p_addr,*c_addr;    
  14. if(argc!=2)    
  15. {    
  16. fprintf(stderr,"Usage:%s\n\a",argv[0]);    
  17. exit(1);    
  18. }    
  19. if((shmid=shmget(IPC_PRIVATE,1024,PERM))==-1)    
  20. {    
  21. fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno));    
  22. exit(1);    
  23. }    
  24. if(fork())    
  25. {    
  26. p_addr=shmat(shmid,0,0);    
  27. memset(p_addr,'\0',1024);    
  28. strncpy(p_addr,argv[1],1024);    
  29. exit(0);    
  30. }    
  31. else    
  32. {    
  33. c_addr=shmat(shmid,0,0);    
  34. printf("Client get %s",c_addr);    
  35. exit(0);    
  36. }    
  37. }    

這個程序是父進程將參數寫入到共享內存,然后子進程把內容讀出來.最后我們要使用ip
crm釋放資源的.先用ipcs找出ID然后用ipcrm shm ID刪除.
后記:
進程通信(IPC)是網絡程序的基礎,在很多的網絡程序當中會大量的使用進程通信的概念
和知識.其實進程通信是一件非常復雜的事情,我在這里只是簡單的介紹了一下.如果你想
學習進程通信的詳細知識,最好的辦法是自己不斷的寫程序和看聯機手冊.現在網絡上有
了很多的知識可以去參考.可惜我看到的很多都是英文編寫的.如果你找到了有中文的版
本請盡快告訴我.謝謝!

posted on 2008-04-16 09:11 RedLight 閱讀(331) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

導航

統計

公告


Name: Galen
QQ: 88104725

常用鏈接

留言簿(3)

隨筆分類

隨筆檔案

相冊

My Friend

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美电影免费观看高清| 国产精品久久看| 一本一本a久久| 亚洲国产清纯| 久久免费视频观看| 欧美成年网站| 亚洲欧洲精品一区二区三区波多野1战4 | 亚洲午夜激情在线| 亚洲一区免费网站| 欧美一区免费视频| 欧美成人69av| 在线一区二区三区做爰视频网站| 一本一本久久| 久久大逼视频| 欧美精品在线观看播放| 国产日韩精品入口| 99re6热只有精品免费观看| 欧美亚洲综合久久| 亚洲第一福利视频| 亚洲欧美在线一区二区| 女同性一区二区三区人了人一| 欧美日韩情趣电影| 一区二区亚洲欧洲国产日韩| 99综合精品| 久久躁日日躁aaaaxxxx| 日韩一级免费观看| 久久综合九色99| 国产精品超碰97尤物18| 在线精品国产欧美| 香港成人在线视频| 亚洲国产精品一区制服丝袜| 欧美一区久久| 国产精品黄色| 日韩一二在线观看| 欧美α欧美αv大片| 性刺激综合网| 国产精品高清在线| 日韩亚洲欧美成人| 欧美成人69av| 久久久久高清| 国产亚洲精品成人av久久ww| 亚洲一二三区视频在线观看| 欧美黑人多人双交| 久久久久久一区| 国产女优一区| 午夜视频精品| 亚洲天堂视频在线观看| 欧美视频网站| 亚洲综合成人在线| 一区二区三区视频免费在线观看| 欧美国产精品v| 亚洲国产专区| 欧美成人免费在线观看| 久久久国产精品一区| 国产一区二区成人| 久久精品首页| 久久精品亚洲精品国产欧美kt∨| 国产网站欧美日韩免费精品在线观看 | 欧美成人精精品一区二区频| 极品裸体白嫩激情啪啪国产精品| 91久久精品久久国产性色也91| 亚洲视频电影图片偷拍一区| 欧美日韩国产bt| 99国内精品久久久久久久软件| 欧美黄色片免费观看| 欧美www视频| 亚洲毛片在线免费观看| 亚洲黄色小视频| 欧美国产大片| 亚洲欧美国产77777| 日韩一级精品视频在线观看| 国产精品卡一卡二卡三| 亚洲男人第一网站| 亚洲欧美日韩综合国产aⅴ| 国产女主播一区二区三区| 欧美专区日韩专区| 久久综合电影| 亚洲一级免费视频| 一区二区三区视频免费在线观看| 国产精品黄页免费高清在线观看| 欧美在线免费观看视频| 久久久噜噜噜久久中文字幕色伊伊| 亚洲国产另类精品专区| 亚洲美女区一区| 国产日韩在线亚洲字幕中文| 欧美成人精品福利| 欧美亚州韩日在线看免费版国语版| 欧美一区2区三区4区公司二百 | 99re66热这里只有精品3直播| 欧美性猛交视频| 美女成人午夜| 欧美视频在线观看 亚洲欧| 久久久99久久精品女同性| 免费高清在线视频一区·| 亚洲制服av| 美女诱惑一区| 欧美一区高清| 欧美激情中文字幕一区二区 | 亚洲伦理久久| 狠狠色狠狠色综合系列| 亚洲激情网站| 欧美日韩aaaaa| 久久人人精品| 国产精品theporn88| 女人天堂亚洲aⅴ在线观看| 欧美视频在线观看免费网址| 美日韩精品视频| 国产精品视频自拍| 亚洲国产裸拍裸体视频在线观看乱了中文| 亚洲经典三级| 欧美日韩在线三级| 久久久人成影片一区二区三区| 欧美激情国产精品| 久热精品视频在线观看一区| 国产精品第十页| 亚洲激情欧美| 亚洲视频 欧洲视频| 久久久亚洲精品一区二区三区| 亚洲欧美日韩在线高清直播| 欧美大片在线观看一区二区| 国产午夜精品理论片a级探花| 91久久夜色精品国产九色| 悠悠资源网久久精品| 欧美一区在线直播| 欧美一区1区三区3区公司| 国产精品成人av性教育| 亚洲美洲欧洲综合国产一区| 亚洲日本一区二区| 免费观看不卡av| 国内外成人免费激情在线视频网站| 99在线精品观看| 夜夜夜精品看看| 欧美激情在线观看| 亚洲美女av在线播放| 亚洲免费成人av电影| 欧美成人a视频| 亚洲欧洲视频| 亚洲欧美国产va在线影院| 欧美天天影院| 制服丝袜激情欧洲亚洲| 亚洲尤物在线| 国产精品久久久久久久免费软件| 一本大道久久a久久综合婷婷| 在线亚洲欧美专区二区| 欧美日韩另类一区| 亚洲校园激情| 久久久噜噜噜久久人人看| 激情成人在线视频| 噜噜噜久久亚洲精品国产品小说| 欧美国产一区二区在线观看 | 久久亚洲综合网| 国产日韩欧美91| 久久久精品午夜少妇| 欧美肥婆bbw| 亚洲图色在线| 国产精品免费在线 | 久久国产日韩欧美| 久久全国免费视频| 亚洲国产福利在线| 欧美精品免费在线观看| 日韩一区二区福利| 久久久精品国产免费观看同学| 激情国产一区二区| 欧美激情一区二区三区成人| 夜夜嗨av一区二区三区| 久久精品国产清自在天天线| 亚洲动漫精品| 国产精品成人一区二区艾草| 欧美在线国产| 日韩午夜在线| 另类av导航| 亚洲欧美电影在线观看| 亚洲高清网站| 亚洲欧美另类久久久精品2019| 久久久精品五月天| 99re热精品| 一区二区视频欧美| 国产精品久久久久久久久久久久久| 亚洲综合首页| 亚洲国产欧美精品| 久久国产日韩欧美| 国产亚洲精品aa午夜观看| 久久一区中文字幕| 亚洲一区亚洲二区| 欧美精品观看| 性亚洲最疯狂xxxx高清| 最新日韩av| 毛片一区二区| 久久99在线观看| 国产精品99久久久久久久女警| 国外视频精品毛片| 国产精品视频999| 欧美日韩精品中文字幕| 久久亚裔精品欧美| 欧美中文字幕第一页| 亚洲中无吗在线| 在线亚洲电影| 亚洲免费观看高清在线观看 | 免费高清在线一区| 国产精品久久九九|