欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Linux動態鏈接庫.so文件的創建與使用

系統 1985 0

From: http://blog.csdn.net/sunboy_2050/article/details/7346146

1. 介紹

??????? 使用GNU的工具我們如何在Linux下創建自己的程序函數庫?一個“程序函數庫”簡單的說就是一個文件包含了一些編譯好的代碼和數據,這些編譯好的代碼和數據可以在事后供其他的程序使用。程序函數庫可以使整個程序更加模塊化,更容易重新編譯,而且更方便升級。 ?

程序函數庫可分為3種類型:靜態函數庫(static libraries)、共享函數庫(shared libraries)、動態加載函數庫(dynamically loaded?libraries):?

1、 靜態函數庫 ,是在程序執行前就加入到目標程序中去了;

2、 共享函數庫 ,則是在程序啟動的時候加載到程序中,它可以被不同的程序共享;動態加載函數庫則可以在程序運行的任何時候動態的加載。

3、 動態函數庫 ,并非另外一種庫函數格式,區別是動態加載函數庫是如何被程序員使用的。

?

2. 靜態函數庫

??????? 靜態函數庫實際上就是簡單的一個普通的目標文件的集合,一般來說習慣用“.a”作為文件的后綴。可以用ar這個程序來產生靜態函數庫文件。Ar是archiver的縮寫。靜態函數庫現在已經不在像以前用得那么多了,主要是共享函數庫與之相比較有很多的優勢的原因。慢慢地,大家都喜歡使用共享函數庫了。不過,在一些場所靜態函數庫仍然在使用,一來是保持一些與以前某些程序的兼容,二來它描述起來也比較簡單。?

??????? 靜態庫函數允許程序員把程序link起來而不用重新編譯代碼,節省了重新編譯代碼的時間。不過,在今天這么快速的計算機面前,一般的程序的重新編譯也花費不了多少時間,所以這個優勢已經不是像它以前那么明顯了。靜態函數庫對開發者來說還是很有用的,例如你想把自己提供的函數給別人使用,但是又想對函數的源代碼進行保密,你就可以給別人提供一個靜態函數庫文件。理論上說,使用ELF格式的靜態庫函數生成的代碼可以比使用共享函數庫(或者動態函數庫)的程序運行速度上快一些,大概1-5%。?

創建一個靜態函數庫文件,或者往一個已經存在地靜態函數庫文件添加新的目標代碼,可以用下面的命令:?

????????ar rcs my_library.a file1.o file2.o?

這個例子中是把目標代碼file1.o和file2.o加入到my_library.a這個函數庫文件中,如果my_library.a不存在則創建一個新的文件。在用ar命令創建靜態庫函數的時候,還有其他一些可以選擇的參數,可以參加ar的使用幫助。這里不再贅述。

一旦你創建了一個靜態函數庫,你可以使用它了。你可以把它作為你編譯和連接過程中的一部分用來生成你的可執行代碼。如果你用gcc來編譯產生可執行代碼的話,你可以用“-l”參數來指定這個庫函數。你也可以用ld來做,使用它的“-l”和“-L”參數選項。具體用法可以參考info:gcc。?

?

3. 共享函數庫

共享函數庫中的函數是在當一個可執行程序在啟動的時候被加載。如果一個共享函數庫正常安裝,所有的程序在重新運行的時候都可以自動加載最新的函數庫中的函數。對于Linux系統還有更多可以實現的功能:?
??????? 1、升級了函數庫但是仍然允許程序使用老版本的函數庫。
??????? 2、當執行某個特定程序的時候可以覆蓋某個特定的庫或者庫中指定的函數。
??????? 3、可以在庫函數被使用的過程中修改這些函數庫。

3.1. 一些約定
如果你要編寫的共享函數庫支持所有有用的特性,你在編寫的過程中必須遵循一系列約定。你必須理解庫的不同的名字間的區別,例如它的“soname”和“real name”之間的區別和它們是如何相互作用的。你同樣還要知道你應該把這些庫函數放在你文件系統的什么位置等等。下面我們具體看看這些問題。?

3.1.1. 共享庫的命名

每個共享函數庫都有個特殊的名字,稱作“soname”。soname名字命名必須以“lib”作為前綴,然后是函數庫的名字,然后是“.so”,最后是版本號信息。不過有個特例,就是非常底層的C庫函數都不是以lib開頭這樣命名的。
?? ?每個共享函數庫都有一個真正的名字(“real name”),它是包含真正庫函數代碼的文件。真名有一個主版本號,和一個發行版本號。最后一個發行版本號是可選的,可以沒有。主版本號和發行版本號使你可以知道你到底是安裝了什么版本的庫函數。另外,還有一個名字是編譯器編譯的時候需要的函數庫的名字,這個名字就是簡單的soname名字,而不包含任何版本號信息。

管理共享函數庫的關鍵是區分好這些名字。當可執行程序需要在自己的程序中列出這些他們需要的共享庫函數的時候,它只要用soname就可以了;反過來,當你要創建一個新的共享函數庫的時候,你要指定一個特定的文件名,其中包含很細節的版本信息。當你安裝一個新版本的函數庫的時候,你只要先將這些函數庫文件拷貝到一些特定的目錄中,運行ldconfig這個實用就可以。ldconfig檢查已經存在的庫文件,然后創建soname的符號鏈接到真正的函數庫,同時設置/etc/ld.so.cache這個緩沖文件。這個我們稍后再討論。

ldconfig并不設置鏈接的名字,通常的做法是在安裝過程中完成這個鏈接名字的建立,一般來說這個符號鏈接就簡單的指向最新的soname或者最新版本的函數庫文件。最好把這個符號鏈接指向soname,因為通常當你升級你的庫函數后,你就可以自動使用新版本的函數庫類。

我們來舉例看看:/usr/lib/libreadline.so.3 是一個完全的完整的soname,ldconfig可以設置一個符號鏈接到其他某個真正的函數庫文件,例如是/usr/lib/libreadline.so.3.0。同時還必須有一個鏈接名字,例如 /usr/lib/libreadline.so就是一個符號鏈接指向/usr/lib/libreadline.so.3。

3.1.2. 文件系統中函數庫文件的位置

共享函數庫文件必須放在一些特定的目錄里,這樣通過系統的環境變量設置,應用程序才能正確的使用這些函數庫。大部分的源碼開發的程序都遵循GNU的一些標準,我們可以看info幫助文件獲得相信的說明,info信息的位置是:info:standards#Directory_Variables。GNU標準建議所有的函數庫文件都放在/usr/local/lib目錄下,而且建議命令可執行程序都放在/usr/local/bin目錄下。這都是一些習慣問題,可以改變的。?

文件系統層次化標準FHS(Filesystem Hierarchy Standard)( http://www.pathname.com/fhs )規定了在一個發行包中大部分的函數庫文件應該安裝到/usr/lib目錄下,但是如果某些庫是在系統啟動的時候要加載的,則放到/lib目錄下,而那些不是系統本身一部分的庫則放到/usr/local/lib下面。?

上面兩個路徑的不同并沒有本質的沖突。GNU提出的標準主要對于開發者開發源碼的,而FHS的建議則是針對發行版本的路徑的。具體的位置信息可以看/etc/ld.so.conf里面的配置信息。

3.2. 這些函數庫如何使用

在基于GNU glibc的系統里,包括所有的linux系統,啟動一個ELF格式的二進制可執行文件會自動啟動和運行一個program loader。對于Linux系統,這個loader的名字是/lib/ld-linux.so.X(X是版本號)。這個loader啟動后,反過來就會load所有的其他本程序要使用的共享函數庫。

到底在哪些目錄里查找共享函數庫呢?這些定義缺省的是放在/etc/ld.so.conf文件里面,我們可以修改這個文件,加入我們自己的一些特殊的路徑要求。大多數RedHat系列的發行包的/etc/ld.so.conf文件里面不包括/usr/local/lib這個目錄,如果沒有這個目錄的話,我們可以修改/etc/ld.so.conf,自己手動加上這個條目。

如果你想覆蓋某個庫中的一些函數,用自己的函數替換它們,同時保留該庫中其他的函數的話,你可以在 /etc/ld.so.preload中加入你想要替換的庫(.o結尾的文件),這些preloading的庫函數將有優先加載的權利。

當程序啟動的時候搜索所有的目錄顯然會效率很低,于是Linux系統實際上用的是一個高速緩沖的做法。ldconfig缺省情況下讀出/etc/ld.so.conf相關信息,然后設置適當地符號鏈接,然后寫一個cache到 /etc/ld.so.cache這個文件中,而這個/etc/ld.so.cache則可以被其他程序有效的使用了。這樣的做法可以大大提高訪問函數庫的速度。這就要求每次新增加一個動態加載的函數庫的時候,就要運行ldconfig來更新這個cache,如果要刪除某個函數庫,或者某個函數庫的路徑修改了,都要重新運行ldconfig來更新這個cache。通常的一些包管理器在安裝一個新的函數庫的時候就要運行ldconfig。?

另外,FreeBSD使用cache的文件不一樣。FreeBSD的ELF cache是/var/run/ld-elf.so.hints,而a.out的cache則是/var/run/ld.so.hints。它們同樣是通過ldconfig來更新。

3.3. 環境變量

各種各樣的環境變量控制著一些關鍵的過程。例如你可以臨時為你特定的程序的一次執行指定一個不同的函數庫。Linux系統中,通常變量LD_LIBRARY_PATH就是可以用來指定函數庫查找路徑的,而且這個路徑通常是在查找標準的路徑之前查找。這個是很有用的,特別是在調試一個新的函數庫的時候,或者在特殊的場合使用一個非標準的函數庫的時候。環境變量LD_PRELOAD列出了所有共享函數庫中需要優先加載的庫文件,功能和/etc/ld.so.preload類似。這些都是有/lib/ld-linux.so這個loader來實現的。值得一提的是,LD_LIBRARY_PATH可以在大部分的UNIX-linke系統下正常起作用,但是并非所有的系統下都可以使用,例如HP-UX系統下,就是用SHLIB_PATH這個變量,而在AIX下則使用LIBPATH這個變量。

LD_LIBRARY_PATH在開發和調試過程中經常大量使用,但是不應該被一個普通用戶在安裝過程中被安裝程序修改,大家可以去參考http:// www.visi.com/~barr/ldpath.html ,這里有一個文檔專門介紹為什么不使用LD_LIBRARY_PATH這個變量。

事實上還有更多的環境變量影響著程序的調入過程,它們的名字通常就是以LD_或者RTLD_打頭。大部分這些環境變量的使用的文檔都是不全,通常搞得人頭昏眼花的,如果要真正弄清楚它們的用法,最好去讀loader的源碼(也就是gcc的一部分)。

允許用戶控制動態鏈接函數庫將涉及到setuid/setgid這個函數,如果特殊的功能需要的話。因此,GNU loader通常限制或者忽略用戶對這些變量使用setuid和setgid。如果loader通過判斷程序的相關環境變量判斷程序的是否使用了setuid或者setgid,如果uid和euid不同,或者gid和egid部一樣,那么loader就假定程序已經使用了setuid或者setgid,然后就大大的限制器控制這個老鏈接的權限。如果閱讀GNU?glibc的庫函數源碼,就可以清楚地看到這一點。特別的我們可以看elf/rtld.c和sysdeps/generic/dl-sysdep.c這兩個文件。這就意味著如果你使得uid和gid與euid和egid分別相等,然后調用一個程序,那么這些變量就可以完全起效。

3.4. 創建一個共享函數庫

現在我們開始學習如何創建一個共享函數庫。其實創建一個共享函數庫非常容易。首先創建object文件,這個文件將加入通過gcc –fPIC參數命令加入到共享函數庫里面。PIC的意思是“位置無關代碼”(Position Independent Code)。下面是一個標準的格式:

????????gcc -shared -Wl,-soname,your_soname -o library_name file_list library_list

下面再給一個例子,它創建兩個object文件(a.o和b.o),然后創建一個包含a.o和b.o的共享函數庫。例子中”-g”和“-Wall”參數不是必須的。

????????gcc -fPIC -g -c -Wall a.c

????????gcc -fPIC -g -c -Wall b.c

????????gcc -shared -Wl,-soname,liblusterstuff.so.1 -o liblusterstuff.so.1.0.1 a.o b.o -lc

下面是一些需要注意的地方:

不用使用-fomit-frame-pointer這個編譯參數除非你不得不這樣。雖然使用了這個參數獲得的函數庫仍然可以使用,但是這使得調試程序幾乎沒有用,無法跟蹤調試。

使用-fPIC來產生代碼,而不是-fpic。

某些情況下,使用gcc 來生成object文件,需要使用“-Wl,-export-dynamic”這個選項參數。?

通常,動態函數庫的符號表里面包含了這些動態的對象的符號。這個選項在創建ELF格式的文件時候,會將所有的符號加入到動態符號表中。可以參考ld的幫助獲得更詳細的說明。

3.5. 安裝和使用共享函數庫

一旦你定義了一個共享函數庫,你還需要安裝它。其實簡單的方法就是拷貝你的庫文件到指定的標準的目錄(例如/usr/lib),然后運行ldconfig。

如果你沒有權限去做這件事情,例如你不能修改/usr/lib目錄,那么你就只好通過修改你的環境變量來實現這些函數庫的使用了。首先,你需要創建這些共享函數庫;然后,設置一些必須得符號鏈接,特別是從soname到真正的函數庫文件的符號鏈接,簡單的方法就是運行ldconfig:

????????ldconfig -n directory_with_shared_libraries?
然后你就可以設置你的LD_LIBRARY_PATH這個環境變量,它是一個以逗號分隔的路徑的集合,這個可以用來指明共享函數庫的搜索路徑。例如,使用bash,就可以這樣來啟動一個程序my_program:

????????LD_LIBRARY_PATH=$LD_LIBRARY_PATH my_program

如果你需要的是重載部分函數,則你就需要創建一個包含需要重載的函數的object文件,然后設置LD_PRELOAD環境變量。

通常你可以很方便的升級你的函數庫,如果某個API改變了,創建庫的程序會改變soname。然而,如果一個函數升級了某個函數庫而保持了原來的soname,你可以強行將老版本的函數庫拷貝到某個位置,然后重新命名這個文件(例如使用原來的名字,然后后面加.orig后綴),然后創建一個小的“wrapper”腳本來設置這個庫函數和相關的東西。例如下面的例子:

????????#!/bin/sh export LD_LIBRARY_PATH=/usr/local/my_lib,$LD_LIBRARY_PATH

????????exec /usr/bin/my_program.orig $*

我們可以通過運行ldd來看某個程序使用的共享函數庫。例如你可以看ls這個實用工具使用的函數庫:

????????ldd /bin/ls

????????libtermcap.so.2 => /lib/libtermcap.so.2 (0x4001c000)

????????libc.so.6 => /lib/libc.so.6 (0x40020000)

????????/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)??
通常我么可以看到一個soname的列表,包括路徑。在所有的情況下,你都至少可以看到兩個庫:

·?????????????????? /lib/ld-linux.so.N(N是1或者更大,一般至少2)。這是這個用于加載其他所有的共享庫的庫。

·?????????????????? ?libc.so.N(N應該大于或者等于6)。這是C語言函數庫。

值得一提的是,不要在對你不信任的程序運行ldd命令。在ldd的manual里面寫得很清楚,ldd是通過設置某些特殊的環境變量(例如,對于ELF對象,設置LD_TRACE_LOADED_OBJECTS),然后運行這個程序。這樣就有可能使得某地程序可能使得ldd來執行某些意想不到的代碼,而產生不安全的隱患。

3.6. 不兼容的函數庫

如果一個新版的函數庫要和老版本的二進制的庫不兼容,則soname需要改變。對于C語言,一共有4個基本的理由使得它們在二進制代碼上很難兼容:

一個函數的行文改變了,這樣它就可能與最開始的定義不相符合。

·????????? 輸出的數據項改變了。

·????????? 某些輸出的函數刪除了。

·????????? 某些輸出函數的接口改變了。
?? ?如果你能避免這些地方,你就可以保持你的函數庫在二進制代碼上的兼容,或者說,你可以使得你的程序的應用二進制接口(ABI:Application Binary Interface)上兼容。

?

4. 動態加載的函數庫Dynamically Loaded (DL) Libraries

?

動態加載的函數庫Dynamically loaded (DL) libraries是一類函數庫,它可以在程序運行過程中的任何時間加載。它們特別適合在函數中加載一些模塊和plugin擴展模塊的場合,因為它可以在當程序需要某個plugin模塊時才動態的加載。例如,Pluggable Authentication?Modules(PAM)系統就是用動態加載函數庫來使得管理員可以配置和重新配置身份驗證信息。

Linux系統下,DL函數庫與其他函數庫在格式上沒有特殊的區別,我們前面提到過,它們創建的時候是標準的object格式。主要的區別就是這些函數庫不是在程序鏈接的時候或者啟動的時候加載,而是通過一個API來打開一個函數庫,尋找符號表,處理錯誤和關閉函數庫。通常C語言環境下,需要包含這個頭文件。?
?? ???? Linux中使用的函數和Solaris中一樣,都是dlpoen() API。當然不是所有的平臺都使用同樣的接口,例如HP-UX使用shl_load()機制,而Windows平臺用另外的其他的調用接口。如果你的目的是使得你的代碼有很強的移植性,你應該使用一些wrapping函數庫,這樣的wrapping函數庫隱藏不同的平臺的接口區別。一種方法是使用glibc函數庫中的對動態加載模塊的支持,它使用一些潛在的動態加載函數庫界面使得它們可以夸平臺使用。具體可以參考 http://developer .gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html. 另外一個方法是使用libltdl,是GNU?libtool的一部分,可以進一步參考CORBA相關資料。??

4.1. dlopen()
dlopen函數打開一個函數庫然后為后面的使用做準備。C語言原形是:

????????void * dlopen(const char *filename, int flag);

如果文件名filename是以“/”開頭,也就是使用絕對路徑,那么dlopne就直接使用它,而不去查找某些環境變量或者系統設置的函數庫所在的目錄了。否則dlopen()就會按照下面的次序查找函數庫文件:
?????? 1. 環境變量LD_LIBRARY指明的路徑。

2. /etc/ld.so.cache中的函數庫列表。

3. /lib目錄,然后/usr/lib。不過一些很老的a.out的loader則是采用相反的次序,也就是先查 /usr/lib,然后是/lib。
?? ?dlopen()函數中,參數flag的值必須是RTLD_LAZY或者RTLD_NOW,RTLD_LAZY的意思是resolve undefined symbols as code from the dynamic library is executed,而RTLD_NOW的含義是resolve all undefined symbols before dlopen() returns and fail if this cannot be done'。
?? ?如果有好幾個函數庫,它們之間有一些依賴關系的話,例如X依賴Y,那么你就要先加載那些被依賴的函數。例如先加載Y,然后加載X。

?? ?dlopen()函數的返回值是一個句柄,然后后面的函數就通過使用這個句柄來做進一步的操作。如果打開失敗dlopen()就返回一個NULL。如果一個函數庫被多次打開,它會返回同樣的句柄。?
?? ?如果一個函數庫里面有一個輸出的函數名字為_init,那么_init就會在dlopen()這個函數返回前被執行。我們可以利用這個函數在我的函數庫里面做一些初始化的工作。我們后面會繼續討論這個問題的。??
4.2. dlerror()

通過調用dlerror()函數,我們可以獲得最后一次調用dlopen(),dlsym(),或者dlclose()的錯誤信息。?
4.3. dlsym()

如果你加載了一個DL函數庫而不去使用當然是不可能的了,使用一個DL函數庫的最主要的一個函數就是dlsym(),這個函數在一個已經打開的函數庫里面查找給定的符號。這個函數如下定義:

????????void * dlsym(void *handle, char *symbol);

函數中的參數handle就是由dlopen打開后返回的句柄,symbol是一個以NIL結尾的字符串。如果dlsym()函數沒有找到需要查找的symbol,則返回NULL。如果你知道某個symbol的值不可能是NULL或者0,那么就很好,你就可以根據這個返回結果判斷查找的symbol是否存在了;不過,如果某個symbol的值就是NULL,那么這個判斷就有問題了。標準的判斷方法是先調用dlerror(),清除以前可能存在的錯誤,然后調用dlsym()來訪問一個symbol,然后再調用dlerror()來判斷是否出現了錯誤。一個典型的過程如下:

  1. dlerror();?????? /*clear?error?code?*/ ??
  2. s?=?(actual_type)dlsym(handle,?symbol_being_searched_for);??
  3. if ((error?=?dlerror())?!=?NULL){??
  4. ???? /*?handle?error,?the?symbol?wasn't?found?*/ ??
  5. }? else ?{??
  6. ???? /*?symbol?found,?its?value?is?in?s?*/ ??
  7. }??

4.4. dlclose()

dlopen()函數的反過程就是dlclose()函數,dlclose()函數用力關閉一個DL函數庫。Dl函數庫維持一個資源利用的計數器,當調用dlclose的時候,就把這個計數器的計數減一,如果計數器為0,則真正的釋放掉。真正釋放的時候,如果函數庫里面有_fini()這個函數,則自動調用_fini()這個函數,做一些必要的處理。Dlclose()返回0表示成功,其他非0值表示錯誤。

4.5. DL Library Example

下面是一個例子。例子中調入math函數庫,然后打印2.0的余弦函數值。例子中每次都檢查是否出錯。應該是個不錯的范例:

  1. int ?main( int ?argc,? char ?*argv){??
  2. ???????? void ?*handle;??
  3. ???????? char ?*error;??
  4. ??????????
  5. ???????? double ?(*cosine?)( double );??
  6. ????????handle?=?dlopen( "/lib/libm.so.6" ,?RTLD_LAZY);??
  7. ???????? if (!handle){??
  8. ????????????fputs(dlerror(),?stderr);??
  9. ?????????????exit(1);??
  10. ????????}??
  11. ??????????
  12. ????????cosine?=?dlsym(handle,? "cos" );??
  13. ???????? if ((error?=?dlerror())?!=?NULL){??
  14. ????????????fputs(error,?stderr);??
  15. ????????????exit(1);??
  16. ????????}??
  17. ??????????
  18. ????????printf( "%f" ,?(*cosine)(2,?0));??
  19. ??????????
  20. ????????dlclose(handle);??
  21. ??????????
  22. ???????? return ?0;??
  23. }??

如果這個程序名字叫foo.c,那么用下面的命令來編譯:

????????gcc -o foo foo.c –ldl

Linux動態鏈接庫.so文件的創建與使用


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 人人性人人性碰国产 | 91亚洲成人 | 精品一区二区三区网站 | 色综合亚洲精品激情狠狠 | 99亚洲精品高清一二区 | 黄色片免费在线 | 国产激情在线观看 | 国产++欧洲韩国野花视频 | 全免费午夜一级毛片一级毛 | 久久亚洲一级α片 | 精品久久久爽爽久久久AV | 国产一区二区黑人欧美xxxx | 99久久国产综合精品2020 | 国产成人高潮免费观看精品 | 国产精品91在线播放 | 久久狠狠| 国产一级大片在线观看 | 婷婷色基地 | 污版视频在线观看 | 日韩欧美中文在线 | 欧美在线a | 久久久免费视频观看 | 91久久精品国产一区二区 | 天天影视欧美 | 欧美日韩一区二区三区高清不卡 | av网址在线播放 | 日本在线观看视频网站 | 欧美成人在线免费观看 | 久久99精品亚洲热综合 | 午夜免费 | 91亚洲精品成人一区 | 欧美日韩一区二区在线视频 | 久草在线视频资源站 | aⅴ免费在线观看 | 亚洲日本在线天堂无码 | 精品一区二区国语对白 | 十六以下岁女子毛片免费 | 亚洲日韩中文字幕一区 | avtom影院入口永久在线观看 | 亚洲精品午夜一区二区 | 91tm视频|