Linux多任務多線程編程-_第1頁
Linux多任務多線程編程-_第2頁
Linux多任務多線程編程-_第3頁
Linux多任務多線程編程-_第4頁
Linux多任務多線程編程-_第5頁
已閱讀5頁,還剩50頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1多進程、多線程編程--Linux下多任務2主要內容1.什么是多任務(1)進程級多任務(2)線程級多任務(3)多任務處理的特點2.進程(1)進程的概念(2)文件描述符共享(3)Vfork()函數(4)exec()函數族(5)執(zhí)行新程序(6)進程的終止(7)進程的退出狀態(tài)(8)進程的通信31.什么是多任務當操作系統使用某種策略允許兩個或更多進程并發(fā)共享一個CPU時,它稱做多任務運行,或多道程序運行。在規(guī)定的時間片周期或某些事情發(fā)生前,一直執(zhí)行某個進程。然后,操作系統切換到另一個進程。這種切換十分迅速,給人一種這些進程是同時執(zhí)行的錯覺。而事實上,同一時刻在一個CPU上只能激活一個進程。這種進程間的切換在所有進程完成前一直進行。并發(fā)共享策略決定何時切換進程。改策略由操作系統或其他進程強制執(zhí)行。多任務可以分為三個級別:對話級、進程級、線程級。4(1)進程級多任務在對話中可以并發(fā)激活多個進程,這些進程相互合作來完成一個最終目標。所有的進程共享CPU運行,一個進程運行一段時間,然后另一個進程再運行一段時間。操作系統控制進程之間的轉換,直到所有的進程運行完成。對于這樣一種多個進程并發(fā)執(zhí)行的多任務實現方式,稱作進程級多任務。5進程是運行著的程序,是操作系統執(zhí)行任務的基本單位。進程具備文本、數據和堆棧片段,以及它自己的資源。資源可以是文件、對象句柄、設備、信號量、互斥量、管道,等等。操作系統管理進程運行出錯不會影響到別的進程運行。兩個進程之間可以通過管道等方式通信,或者通過信號量的工具同步運行。因此,進程是實現多任務處理的核心單元。什么是進程?6(2)線程級多任務進程完成單獨的任務,每個任務又可能有自己的控制流程。這些流程由輕量級的進程構成,稱作線程。進程的線程并發(fā)執(zhí)行稱作線程級多任務。7在窗口系統中每時每刻都在進行著上下文切換,而進程級的上下文切換代價十分昂貴,頻繁地切換不但不能體現多任務系統的優(yōu)勢,反而降低了系統的整體反應速度。線程是輕量級的進程,它由進程創(chuàng)建,并與創(chuàng)建它的進程工作在同一內存空間中,不但可以與同一進程中的其他線程共享數據和文件描述符,而且線程間的切換過程也是十分快捷和低成本的。因此越來越多的多任務處理在底層都采取線程來實現。你的程序選擇進程?還是線程?8(3)多任務處理的特點

對話間的多任務是一個高級別的多任務,它受用戶控制。進程間的多任務以及多線程在低級別上實現,由設計它的程序員控制。程序員創(chuàng)建進程,并決定每個進程的線程數,任務的優(yōu)先權,以及什么時候掛起、什么時候終止。92、開發(fā)多進程程序(1)進程的概念:一般把進程定義成正在運行的程序的實例,簡單的說,進程就是一個正在運行的程序。10(2)進程環(huán)境和屬性在Linux系統,C程序總是從main()函數開始的,當用戶編寫好的程序在運行的時候,操作系統會使用exec()函數運行程序,在調用main()函數之前,exec()系統調用會先調用一個特殊的啟動例程,負責從操作系統內核讀取程序的命令行參數,為main()函數準備好工作環(huán)境。在bash下,可以執(zhí)行export查看本機支持的環(huán)境變量名稱和內容。11(3)Linux內核的進程管理創(chuàng)建新進程fork&vfork執(zhí)行程序exec…進程終止exit…12進程描述符每個進程都有一個非負整型的唯一進程ID進程ID號為0表示調度進程,常常被稱為交換進程(swapper)。該進程并不執(zhí)行任何磁盤上的程序—它是內核的一部分,因此也被稱為系統進程。進程ID號為1通常是init進程,在自舉過程結束時由內核調用。init通常讀與系統有關的初始化文件(/etc/rc*文件),并將系統引導到一個狀態(tài)(例如多用戶)。init進程決不會終止。它是一個普通的用戶進程(與交換進程不同,它不是內核中的系統進程),但是它以超級用戶特權運行。13與進程ID相關的API一個進程除了能獲得操作系統提供的環(huán)境變量外,還具備自身的基本屬性,主要包括:進程號(PID:ProcessID):操作系統通過進程號標識一個用戶進程父進程號(PPID:ParentProcessID):Linux系統中,除了init進程外,所有進程都是通過init進程創(chuàng)建的,同時,進程又可以創(chuàng)建其他進程,最終形成了一個倒樹形結構,每個進程都會有自己的父進程,通過父進程號標識。進程組號(PGID:ProcessGroupID):操作系統允許對進程分組,不同的進程通過進程組號標識。真實用戶號(UID:UserID):用戶唯一標識號,用于標識一個用戶。真實組號(GID:GroupID):用戶的唯一標識號,用于標識一個用戶組。有效用戶號(EUID:EffectiveUserID):以其他用戶身份訪問文件使用。有效組號(EGID:EffectiveGroupID):以其他用戶組身份訪問文件使用14與進程ID相關的API#include<sys/types.h>#include<unistd.h>pid_tgetpid(void);返回:調用進程的進程IDpid_tgetppid(void);返回:調用進程的父進程IDuid_tgetuid(void);返回:調用進程的實際用戶IDuid_tgeteuid(void);返回:調用進程的有效用戶IDgid_tgetgid(void);返回:調用進程的實際組IDgid_tgetegid(void);返回:調用進程的有效組ID15創(chuàng)建進程Linux系統通過fork()系統調用創(chuàng)建一個進程,fork()函數定義如下: #include<sys/types.h> #include<unistd.h> pid_tfork(void);返回:子進程中為0,父進程中為子進程ID,出錯為-1該函數被調用一次,但返回兩次。兩次返回的區(qū)別是子進程的返回值是0,而父進程的返回值則是新子進程的進程ID。為什么將子進程ID返回給父進程?16fork創(chuàng)建進程過程17fork在內核究竟干了那些事情子進程和父進程繼續(xù)執(zhí)行fork之后的指令。子進程是父進程的復制品。子進程獲得父進程數據空間、堆和棧的復制品。子進程所擁有這些數據的拷貝。18fork的執(zhí)行流程檢查可用的內核資源取一個空閑的進程表項和唯一的PID號檢查用戶有沒有過多的運行進程將子進程的狀態(tài)設為“創(chuàng)建”狀態(tài)將父進程的進程表項中的數據拷貝到子進程表項中當前目錄的索引節(jié)點和改變的根目錄的引用計數加1文件表中的打開文件的引用數加119fork的執(zhí)行流程(續(xù))在內存中作父進程上下文的拷貝在子進程的系統級上下文中壓入虛擬系統級上下文層;If(正在執(zhí)行的進程是父進程)將子進程狀態(tài)設為“就緒”狀態(tài)return子進程的PIDelse初始化計時域return020創(chuàng)建進程例子#include<sys/types.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>intmain(){pid_tpid;pid=fork(); //創(chuàng)建進程if(-1==pid){ //創(chuàng)建進程失敗printf("Errortocreatenewprocess!\n");return0;}elseif(pid==0){ //子進程printf("Childprocess!\n");}else{ //父進程printf("Parentprocess!ChildprocessID:%d\n",pid);}return0;}21fork出錯的原因系統中已經有了太多的進程該實際用戶ID的進程總數超過了系統限制CHILD_MAX規(guī)定了每個實際用戶ID在任一時刻可具有的最大進程使用ulimit–a系統對進程和文件描述符個數的限制。22vforkvfork函數的調用序列和返回值與fork相同,但兩者的語義不同。vfork用于創(chuàng)建一個新進程,但是它并不將父進程的地址空間完全復制到子進程中,而是讓新進程exec一個新程序vfork保證子進程先運行,在它調用exec或exit之后父進程才可能被調度運行。23(3)等待進程結束等待進程結束是指需要一種方法讓父進程知道子進程在什么時候結束。由于父進程創(chuàng)建子進程后,兩個進程是無序運行的,如果父進程先于子進程結束,那么子進程就會因為找不到父進程的進程號而無法通知父進程,導致資源無法釋放,因此需要一種方法讓父進程知道子進程在什么時候結束。24等待進程結束當一個進程正?;虍惓=K止時,內核就向其父進程發(fā)送SIGCHLD信號。Linux系統提供了waitid()函數,他們的作用是等待另外一個進程的結束。函數定義如下:#include<sys/types.h>#include<sys/wait.h>pid_twaitpid(pid_tpid,int*statloc,intoptions);兩個函數返回:若成功則為進程ID,若出錯則為-125等待進程結束#include<sys/types.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>intmain(){pid_tpid,pid_wait;intstatus;pid=fork(); //創(chuàng)建子進程if(-1==pid){ //檢查是否創(chuàng)建成功printf("Errortocreatenewprocess!\n");return0;}elseif(pid==0){ //子進程printf("Childprocess!\n");}else{ //父進程printf("Parentprocess!ChildprocessID:%d\n",pid);pid_wait=waitpid(pid,&status,0); //參數0表示,如果沒有子進程則立即返回,否則等待指定進程號的子進程printf("Childprocess%dreturned!\n",pid_wait);}return0;}26退出進程Linux提供了幾個退出進程相關的函數exit()、_exit()、atexit()和on_exit()。exit()函數的作用是退出當前進程,并且盡可能釋放當前進程占用的資源。_exit()函數作用也是退出當前進程,但是并不試圖釋放進程占用的資源。atexit()函數和on_exit()函數作用都是為程序退出時指定調用用戶的代碼,區(qū)別在于on_exit()函數可以為設定的用戶函數設定參數。這幾個函數的定義:#include<stdlib.h>intatexit(void(*function)(void));inton_exit(void(*function)(int,void*),void*arg);voidexit(intstatus);#include<unistd.h>void_exit(intstatus);27退出進程include<stdio.h>#include<stdlib.h>#include<unistd.h>voidbye(void)//退出時回調的函數{printf("Thatwasall,folks\n");}voidbye1(void)//退出時回調的函數{printf("Thisshouldcalledfirst!\n");}intmain(){longa;inti;i=atexit(bye);//設置退出回調函數并檢查返回結果if(i!=0){fprintf(stderr,"cannotsetexitfunctionbye\n");returnEXIT_FAILURE;}i=atexit(bye1);//設置退出回調函數并檢查返回結果if(i!=0){fprintf(stderr,"cannotsetexitfunctionbye1\n");returnEXIT_FAILURE;}returnEXIT_SUCCESS;}28常用進程間通信的方法Linux提供了多種進程間通信的方法,常見的包括管道、FIFO、消息隊列、信號量、共享存儲以及通過socket也可以實現不同進程間的通信。本節(jié)簡述管道和共享內存兩種進程間通信方法。1.管道(及有名管道:FIFO)2.共享內存29a.管道管道是UNIXIPC(進程間通信)的最基本形式,并且所有UNIX系統都提供此種通信機制#include<unistd.h>intpipe(intfiledes[2]);返回:若成功則為0,若出錯則為-1管道有兩種限制它們是半雙工的。數據只能在一個方向上流動。它們只能在具有公共祖先的進程之間使用。30fork后的半雙工管道31從父進程到子進程的管道32對管道的操作如果要直接存取管道,可以使用和低級的文件I/O同樣的系統調用,因為在系統內核中管道實際上是由一個有效的索引節(jié)點表示的。如果希望向管道中發(fā)送數據,可以使用系統調用write(),反之,如果希望從管道中讀取數據,可以使用系統調用read()。33創(chuàng)建管道pipe如果要使用C語言創(chuàng)建一個簡單的管道,可以使用系統調用pipe()。#include<unistd.h>原型:intpipe(intfd[2]);其中fd[0]為讀打開,fd[1]為寫打開;返回值:如果系統調用成功,返回0 如果系統調用失敗返回-134管道的通信實現#include<sys/types.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(){intfd[2];

pid_tpid;charbuf[64]="I'mparentprocess!\n";//父進程要寫入管道的信息charline[64];if(0!=pipe(fd)){ //創(chuàng)建管道并檢查結果fprintf(stderr,"Failtocreatepipe!\n");return0;}}pid=fork(); //創(chuàng)建進程if(pid<0){fprintf(stderr,"Failtocreateprocess!\n");return0;}elseif(0==pid){ //父進程

close(fd[0]);

//關閉讀管道,使得父進程只能向管道寫入數據write(fd[1],buf,strlen(buf));//寫數據到管道

close(fd[1]); //關閉寫管道}else{ //子進程

close(fd[1]);

//關閉寫管道,使得子進程只能從管道讀取數據read(fd[0],line,64); //從管道讀取數據printf("DATAFromParent:%s",line);close(fd[0]); //關閉讀管道}return0;父進程關閉讀管道,子進程關閉寫管道,管道變成了一個從父進程到子進程單向傳遞的數據的通道。35b.共享內存方法共享內存:在內存開辟一段空間,供不同的進程訪問。與管道相比,共享內存不能在多個不同進程間共享數據,而且可以比管道傳送更大量的數據。36共享存儲共享存儲允許兩個或多個進程共享一給定的存儲區(qū)。因為數據不需要在客戶機和服務器之間復制,所以這是最快的一種IPC。使用共享存儲的唯一竅門是多個進程之間對一給定存儲區(qū)的同步存取。通常,信號量被用來實現對共享存儲存取的同步。37shmgetshmget,它獲得一個共享存儲標識符#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>intshmget(key_tkey,intsize,intflag);Key:關鍵字是系統唯一關鍵字,由ftok()生成,亦可指定;Size:需要的共享內存的字節(jié)數;Flag:存儲操作方式,讀,寫,創(chuàng)建通信..返回:若成功則為共享內存ID,若出錯則為-138shmat一旦創(chuàng)建了一個共享存儲段,進程就可調用shmat將其連接到它的地址空間中.Shmat()是獲得共享內存的起始地址.#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>void*shmat(intshmid,void*addr,intflag);Shmid:共享內存的ID,由shmget產生,addr:指定共享內存地址,若為0,表示由系統指定共享內存地址,若獲取內存成功,則返回相應共享內存的地址.返回:若成功則為指向共享存儲段的指針,若出錯則為-139shmdt當對共享存儲段的操作已經結束時,則調用shmdt脫接該段。#include<sys/types.h>#include<sys/ipc.h>#include<sys/shm.h>intshmdt(void*addr);addr:要分離共享內存的地址返回:若成功則為0,若出錯則為-140寫共享內存操作//shm_write.c-->gcc-owshm_write.c#include<sys/ipc.h>#include<sys/shm.h>#include<sys/types.h>#include<unistd.h>intmain(){intshmid; //定義共享內存idchar*ptr;char*shm_str="stringinasharememory";shmid=shmget(0x90,1024,SHM_W|SHM_R|IPC_CREAT|IPC_EXCL);//創(chuàng)建共享內存if(-1==shmid)perror("createsharememory");ptr=(char*)shmat(shmid,0,0); //通過共享內存id獲得共享內存地址if((void*)-1==ptr)perror("getsharememory");strcpy(ptr,shm_str); //把字符串寫入共享內存shmdt(ptr);return0;}41讀共享內存操作//shm_read.c-->gcc-orshm_read.c#include<sys/ipc.h>#include<sys/shm.h>#include<sys/types.h>#include<unistd.h>intmain(){intshmid; //定義共享內存idchar*ptr;shmid=shmget(0x90,1024,SHM_W|SHM_R|IPC_EXCL);//根據key獲得共享內存idif(-1==shmid){perror("createsharememory");Return(0)}ptr=shmat(shmid,0,0); //通過共享內存id獲得共享內存地址if((void*)-1==ptr){perror("getsharememory");Return(0);}printf("stringinsharememory:%s\n",ptr); //打印共享內存中的內容shmdt(ptr);return0;}42進程編程綜合實例//process_demo.c#include<sys/types.h>#include<sys/stat.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>intmain(){pid_tpid,pid_wait;intfd[2];charbuff[64],*cmd="exit";if(pipe(fd)){ //創(chuàng)建管道perror("Createpipefail!");return0;}pid=fork();if(-1==pid){perror("Createprocessfail!");return0;}elseif(0==pid){ //子進程close(fd[1]);//關閉寫操作

printf("waitcommandfromparent!\n");while(1){read(fd[0],buff,64);if(0==strcmp(buff,cmd)){printf("recvcommandok!\n");close(fd[0]);exit(0);}}}else{ //父進程printf("Parentprocess!childprocessid:%d\n",pid);close(fd[0]); //關閉讀操作sleep(2);printf("Sendcommandtochildprocess.\n");write(fd[1],cmd,strlen(cmd)+1); //寫入命令close(fd[1]);}return0;}43開發(fā)多線程程序1.線程的定義:線程是一種輕量級的進程。 與進程最大的不同是,線程沒有系統資源。線程是操作系統調度的最小單位,可以理解為一個進程是由一個或者多個線程組成的。在操作系統內核中,是按照線程作為調度單位來調度資源的。

在一個進程內部,多個線程之間的資源是共享的。也就是說,一個進程內部所有線程擁有相同的代碼地址空間和數據空間,任意線程可以訪問其他所有線程的數據。442進程和線程對比進程和線程有許多相似之處,但是也有許多不同:資源分配不同。工作效率不同。執(zhí)行方式不同。452進程和線程對比463創(chuàng)建線程Linux系統開發(fā)多線程程序大多使用pthread庫,pthread庫是符合POSIX線程標準的一個應用庫,提供了線程的管理和操作方法。pthread庫對線程操作的函數基本都以pthread開頭,創(chuàng)建線程的函數定義如下:#include<pthread.h>intpthread_create(pthread_t*restrictthread,constpthread_attr_t*restrictattr, void*(*start_routine)(void*),void*restrictarg);restrict為關鍵字,限制指針,thread為創(chuàng)建線程成功后的句柄,attr:為pthread_attr_t類型結構,設置線程屬性,一般為NULL;star_routine:要執(zhí)行的線程函數指針;arg:傳遞的參數(無符號指針類型的參數)473創(chuàng)建線程#include<pthread.h>#include<stdio.h>#include<stdlib.h>void*thread_func(void*arg) //線程函數{int*val=arg;printf("Hi,I'mathread!\n");if(NULL!=arg) //如果參數不為空,打印參數內容printf("argumentset:%d\n",*val);while(1){//addyourcodehere

……..}}intmain(){

pthread_ttid; //線程IDintt_arg=100; //給線程傳入的參數值if(pthread_create(&tid,NULL,thread_func,&t_arg)) //創(chuàng)建線程perror("Failtocreatethread");

sleep(1); //睡眠1秒,等待線程執(zhí)行while(1){printf("Mainthread!\n");}return0;}48創(chuàng)建多個線程#include<pthread.h>#include<stdio.h>#include<stdlib.h>void*thread_func(void*arg) //線程函數{int*val=arg;//傳遞參數printf("Hi,I'mathread!\n");if(NULL!=arg) //如果參數不為空,打印參數內容while(1){//addyourcodehereif(NULL!=arg)printf("argumentset:%d\n",*val);}}intmain(){pthread_ttid; //線程IDpthread_ttid_2; //線程IDpthread_ttid_3; //線程IDintt_arg; //給線程傳入的參數值//創(chuàng)建第一個線程t_arg=100;if(pthread_create(&tid,NULL,thread_func,&t_arg)) //創(chuàng)建線程,每次創(chuàng)建時,非全局變量t_arg獨立不同;若t_arg是在main()外定義的全局變量,則所有線程的t_arg相同

perror("Failtocreatethread");sleep(2); //睡眠1秒,等待線程執(zhí)行//創(chuàng)建第二個線程t_arg=200;if(pthread_create(&tid_2,NULL,thread_func,&t_arg)) perror("Failtocreatethread");sleep(2);//創(chuàng)建第三個線程t_arg=300;if(pthread_create(&tid_3,NULL,thread_func,&t_arg))perror("Failtocreatethread");sleep(2);while(1){printf("Mainthread!\n");}return0;}49編譯過程要注意問題undefinedreferenceto'pthread_create'

undefinedreferenceto'pthread_join'

問題原因:

pthread庫不是Linux系統默認的庫,連接時需要使用靜態(tài)庫libpthread.a,所以在使用pthread_create()創(chuàng)建線程,以及調用pthread_atfork()函數建立fork處理程序時,需要鏈接該庫。

問題解決:

在編譯中要加-lpthread參數

gccthread.c-othread-lpthread

504取消線程線程的退出有幾種條件,當線程本身的代碼運行結束后,會自動退出;或者線程代碼中調用return也會導致線程退出;還有一種情況是通過其他的線程把一個線程退出,pthread庫提供pthread_cancel()函數用來取消一個線程的執(zhí)行。函數定義如下:

#include<pthread.h> intpthread_cancel(pthread_tthread);514取消線程#include<pthread.h>#include<stdio.h>#include<stdlib.h>void*thread_func(void*arg) //線程函數{int*val=arg;printf("Hi,I'mathread!\n");if(NULL!=arg){ //如果參數不為空,打印參數內容while(1)printf("argumentset:%d\n",*val);}}intmain(){pthread_ttid; //線程IDintt_arg=100; //給線程傳入的參數值if(pthread_create(&tid,NULL,thread_func,&t_arg)) //創(chuàng)建線程perror("Failtocreatethread");sleep(1); //睡眠1秒,等待線程執(zhí)行printf("Mainthread!\n");If(tid!=NULL)pthread_cancel(tid); //取消線程return0;}525等待線程在線程操作實例中,主線程使用sleep()函數暫停自己的運行,等待新創(chuàng)建的線程結束。使用延遲函數的方法在簡單的程序中還能對付,但是復雜一點的程序就不好用了。由于線程的運行時間不確定,導致程序的運行結果無法預測。pthread庫提供了一種等待其他線程結束的方法,使用pthread_join()函數等待一個線程結函數定義如下: #include<pthread.h> intpthread_join(pthread_tthread,void**value_ptr);value_ptr:退出進程的返回值,若被等待進程成功返回,函數返回0,一般取為NULL536使用pthread庫線程操作實例//pthread_demo.c#include<pthread.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>void*mid_thread(void*arg); //mid線程聲明void*term_thread(void*arg); //term線程聲明intmain(){pthread_tmid_tid,term_tid; //存放線程idif(pthread_create(&mid_tid,NULL,mid_thread,NULL)){ //創(chuàng)建mid線程perror("Createmidthreaderror!");return0;}if(pthread_create(&term_tid,NULL,term_thread,(void*)&mid_thread)){//創(chuàng)建term線程perror("Createtermthreadfail!\n");return0;}if(pthread_join(mid_tid,NULL)){ //等待mid線程結束perror("waitmidthreaderror!");return0;}if(pthread_join(term_tid,NULL)){ //等待term線程結束perror("waittermthreaderror!");return0;}return0;

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論