Android的Surface的创建
ViewRootImpl管理著整個(gè)view tree。 對(duì)于ViewRootImpl.setView(),我們可以簡(jiǎn)單的把它當(dāng)做一個(gè)UI渲染操作的入口。
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/WindowManagerImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
//mWindowSession是一個(gè)aidl,ViewRootImpl利用它來(lái)和WindowManagerService交互
//mWindow是一個(gè)aidl,WindowManagerService可以利用這個(gè)對(duì)象與服務(wù)端交互
//mAttachInfo可以理解為是一個(gè)data bean,可以跨進(jìn)程傳遞
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
...
}
ViewRootImpl.setView()方法會(huì)向WindowManagerService請(qǐng)求添加一個(gè)Window,mWindowSession.addToDisplay()跨進(jìn)程最終調(diào)用到了WindowManagerService.addWindow():
http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client...) {
...
//WindowState用來(lái)描述一個(gè)Window
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
...
win.attach(); //會(huì)創(chuàng)建一個(gè)SurfaceSession
mWindowMap.put(client.asBinder(), win); //mWindowMap是WindowManagerService用來(lái)保存當(dāng)前所有Window新的的集合
...
win.mToken.addWindow(win); //一個(gè)token下會(huì)有多個(gè)win state。 其實(shí)token與PhoneWindow是一一對(duì)應(yīng)的。
...
}
http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void attach() {
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Attaching " + this + " token=" + mToken
+ ", list=" + mToken.windows);
mSession.windowAddedLocked();
}
WindowState是WindowManagerService用來(lái)描述應(yīng)用程序的一個(gè)Window的對(duì)象。上面注釋我標(biāo)注了win.attach(),這個(gè)方法可以說(shuō)是Window與SurfaceFlinger鏈接的起點(diǎn),它最終會(huì)調(diào)用到Session.windowAddedLocked():
http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/Session.java
void windowAddedLocked(String packageName) {
...
if (mSurfaceSession == null) {
...
mSurfaceSession = new SurfaceSession();
...
}
}
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/SurfaceSession.java
//SurfaceSession類的構(gòu)造方法
public final class SurfaceSession {
private long mNativeClient; // SurfaceComposerClient*
public SurfaceSession() {
mNativeClient = nativeCreate();
}
這里調(diào)用了native方法nativeCreate(),這個(gè)方法其實(shí)是返回了一個(gè)SurfaceComposerClient指針。那這個(gè)對(duì)象是怎么創(chuàng)建的呢?
SurfaceComposerClient的創(chuàng)建
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/jni/android_view_SurfaceSession.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient(); //構(gòu)造函數(shù)其實(shí)并沒有做什么
client->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(client);
}
即構(gòu)造了一個(gè)SurfaceComposerClient對(duì)象。并返回它的指針。這個(gè)對(duì)象一個(gè)應(yīng)用程序就有一個(gè),它是應(yīng)用程序與SurfaceFlinger溝通的橋梁,為什么這么說(shuō)呢?在SurfaceComposerClient指針第一次使用時(shí)會(huì)調(diào)用下面這個(gè)方法:
//這個(gè)方法在第一次使用SurfaceComposerClient的指針的時(shí)候會(huì)調(diào)用
void SurfaceComposerClient::onFirstRef() {
....
sp<ISurfaceComposerClient> conn;
//sf 就是SurfaceFlinger
conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) :
sf->createConnection();
...
}
即通過(guò)SurfaceFlinger(它本身具有跨進(jìn)程通信的能力)創(chuàng)建了一個(gè)ISurfaceComposerClient對(duì)象:
http://androidxref.com/6.0.1_r10/xref/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
return initClient(new Client(this)); //initClient這個(gè)方法其實(shí)并沒有做什么,
}
即構(gòu)造了一個(gè)Client對(duì)象,Client實(shí)現(xiàn)了ISurfaceComposerClient接口。是一個(gè)可以跨進(jìn)程通信的aidl對(duì)象。即SurfaceComposerClient可以通過(guò)它來(lái)和SurfaceFlinger通信。除此之外它還可以創(chuàng)建Surface,并且維護(hù)一個(gè)應(yīng)用程序的所有Layer(下文會(huì)分析到它是什么)。它是一個(gè)十分重要的對(duì)象,我們先來(lái)看一下它的組成,它所涉及的其他東西在下文分析中都會(huì)講到:
http://androidxref.com/6.0.1_r10/xref/frameworks/native/services/surfaceflinger/Client.h
class Client : public BnSurfaceComposerClient
{
public:
...
void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer);
void detachLayer(const Layer* layer);
...
private:
// ISurfaceComposerClient interface。 gbp很重要,它維護(hù)這一個(gè)應(yīng)用程序的渲染 Buffer隊(duì)列
virtual status_t createSurface(...sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp);
virtual status_t destroySurface(const sp<IBinder>& handle);
//跨進(jìn)程通信方法
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
...
// constant
sp<SurfaceFlinger> mFlinger;
// protected by mLock
DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers; // 一個(gè)應(yīng)用程序的所有Layer
...
};
經(jīng)過(guò)上面這一頓源碼分析,我們大概知道了ViewRootImpl.setView()所引發(fā)的主要操作:
WindowManagerService創(chuàng)建了一個(gè)WindowState。用來(lái)表示客戶端的一個(gè)Window
WindowManagerService創(chuàng)建了一個(gè)SurfaceSession,SurfaceSession會(huì)與SurfaceFlinger構(gòu)建鏈接,創(chuàng)建了一個(gè)SurfaceComposerClient對(duì)象,一個(gè)應(yīng)用程序只具有一個(gè)這個(gè)對(duì)象。
SurfaceComposerClient創(chuàng)建了一個(gè)Client, 這個(gè)對(duì)象十分重要,它維護(hù)這應(yīng)用程序的渲染核心數(shù)據(jù),并負(fù)責(zé)與SurfaceFlinger通信。
經(jīng)過(guò)上面的步驟,應(yīng)用程序的ViewRootImpl已經(jīng)被WindowManagerService識(shí)別,并且應(yīng)用程序已經(jīng)與SurfaceFlinger建立連接。即創(chuàng)建了SurfaceComposerClient和Client對(duì)象
文章開始就已經(jīng)說(shuō)了Surface是Window(ViewRootImpl)的UI載體,那Surface是在哪里創(chuàng)建的呢?
Surface的創(chuàng)建
其實(shí)一個(gè)ViewRootImpl就對(duì)應(yīng)一個(gè)Surface。
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/Surface.java
這點(diǎn)可以通過(guò)ViewRootImpl的源碼看出:
即ViewRootImpl在構(gòu)造的時(shí)候就new 了一個(gè) Surface。但其實(shí)這個(gè)新new的Surface并沒有什么邏輯,它的構(gòu)造函數(shù)是空的。
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
//...
public final Surface mSurface = new Surface();
//...
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout(); //susion 請(qǐng)求layout。先添加到待渲染隊(duì)列中
...
res = mWindowSession.addToDisplay(mWindow, ...); //WindowManagerService會(huì)創(chuàng)建mWindow對(duì)應(yīng)的WindowState
...
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
}
即在向WindowManagerService請(qǐng)求創(chuàng)建WindowState之前,調(diào)用了requestLayout(),這個(gè)方法會(huì)引起ViewRootImpl所管理的整個(gè)view tree的重新渲染。它最終會(huì)調(diào)用到scheduleTraversals():
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/ViewRootImpl.java
void scheduleTraversals() {
...
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
scheduleTraversals()會(huì)通過(guò)Choreographer來(lái)post一個(gè)mTraversalRunnable,Choreographer接收顯示系統(tǒng)的時(shí)間脈沖(垂直同步信號(hào)-VSync信號(hào),16ms發(fā)出一次),在下一個(gè)frame渲染時(shí)控制執(zhí)行這個(gè)mTraversalRunnable。
但是mTraversalRunnable的執(zhí)行至少要在應(yīng)用程序與SurfaceFlinger建立連接之后。這是因?yàn)殇秩静僮魇怯?code>SurfaceFlinger負(fù)責(zé)調(diào)度了,如果應(yīng)用程序還沒有與SurfaceFlinger創(chuàng)建連接,那SurfaceFlinger當(dāng)然不會(huì)渲染這個(gè)應(yīng)用程序。所以在執(zhí)行完mWindowSession.addToDisplay(mWindow, ...)之后,才會(huì)執(zhí)行mTraversalRunnable:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
doTraversal()會(huì)調(diào)用到ViewRootImpl.performTraversals(),大部分同學(xué)可能知道這個(gè)方法是一個(gè)view tree的measure/layout/draw的控制方法:
private void performTraversals() {
finalView host = mView; //mView是一個(gè)Window的根View,對(duì)于Activity來(lái)說(shuō)就是DecorView
...
relayoutWindow(params, viewVisibility, insetsPending);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
...
}
Surface的具體創(chuàng)建就由relayoutWindow(params, viewVisibility, insetsPending)這個(gè)方法來(lái)完成的。這個(gè)方法會(huì)通過(guò)IPC調(diào)用到WindowManagerService.relayoutWindow():
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/ViewRootImpl.java
private int relayoutWindow(WindowManager.LayoutParams params, ...) throws RemoteException {
...
int relayoutResult = mWindowSession.relayout(mWindow,..., mSurface);
...
}
mWindowSession.relayout()方法的很多參數(shù),不過(guò)有一個(gè)十分重要的參數(shù)我沒有省略,就是mSurface。前面已經(jīng)分析了它就是一個(gè)空的Surface對(duì)象。其實(shí):
真正的Surface創(chuàng)建是由SurfaceControl完成的,應(yīng)用程序ViewRootImpl的Surface只是一個(gè)指針,指向這個(gè)Surface
下面就來(lái)看一下SurfaceControl是如何創(chuàng)建Surface的:
mWindowSession.relayout()會(huì)調(diào)用到WindowManagerService.relayoutWindow():
http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
//這里的outSurface其實(shí)就是ViewRootImpl中的那個(gè)Surface
public int relayoutWindow(Session session, IWindow client....Surface outSurface){
...
result = createSurfaceControl(outSurface, result, win, winAnimator);
...
}
private int createSurfaceControl(Surface outSurface, int result, WindowState win,WindowStateAnimator winAnimator) {
...
surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
...
surfaceController.getSurface(outSurface);
}
winAnimator.createSurfaceLocked實(shí)際上是創(chuàng)建了一個(gè)SurfaceControl。即上面是先構(gòu)造SurfaceControl,然后在構(gòu)造Surface。
http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
SurfaceControl createSurfaceLocked() {
final WindowState w = mWin;
if (mSurfaceControl == null) {
//...
mSurfaceFormat = format;
if (DEBUG_SURFACE_TRACE) {
mSurfaceControl = new SurfaceTrace(
mSession.mSurfaceSession,
attrs.getTitle().toString(),
width, height, format, flags);
} else {
mSurfaceControl = new SurfaceControl(
mSession.mSurfaceSession,
attrs.getTitle().toString(),
width, height, format, flags);
}
//...
}
return mSurfaceControl;
}
SurfaceControl的創(chuàng)建
winAnimator.createSurfaceLocked其實(shí)是通過(guò)SurfaceControl的構(gòu)造函數(shù)創(chuàng)建了一個(gè)SurfaceControl對(duì)象,這個(gè)對(duì)象的作用其實(shí)就是負(fù)責(zé)維護(hù)Surface,Surface其實(shí)也是由這個(gè)對(duì)象負(fù)責(zé)創(chuàng)建的,我們看一下這個(gè)對(duì)象的構(gòu)造方法:
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/android/view/SurfaceControl.java
long mNativeObject; //成員指針變量,指向native創(chuàng)建的SurfaceControl
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
SurfaceControl parent, int windowType, int ownerUid){
...
mNativeObject = nativeCreate(session, name, w, h, format, flags,
parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
...
}
即調(diào)用了nativeCreate()并返回一個(gè)SurfaceControl指針:
http://androidxref.com/6.0.1_r10/xref/frameworks/native/libs/gui/SurfaceControl.cpp
static jlong nativeCreate(JNIEnv* env, ...) {
//這個(gè)client其實(shí)就是前面創(chuàng)建的SurfaceComposerClinent
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
sp<SurfaceControl> surface; //創(chuàng)建成功之后,這個(gè)指針會(huì)指向新創(chuàng)建的SurfaceControl
status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid);
...
return reinterpret_cast<jlong>(surface.get()); //返回這個(gè)SurfaceControl的地址
}
即調(diào)用到SurfaceComposerClient.createSurfaceChecked():
http://androidxref.com/6.0.1_r10/xref/frameworks/native/libs/gui/SurfaceComposerClient.cpp
//outSurface會(huì)指向新創(chuàng)建的SurfaceControl
status_t SurfaceComposerClient::createSurfaceChecked(...sp<SurfaceControl>* outSurface..)
{
sp<IGraphicBufferProducer> gbp; //這個(gè)對(duì)象很重要
...
err = mClient->createSurface(name, w, h, format, flags, parentHandle, windowType, ownerUid, &handle, &gbp);
if (err == NO_ERROR) {
//SurfaceControl創(chuàng)建成功, 指針賦值
*outSurface = new SurfaceControl(this, handle, gbp, true);
}
return err;
}
上面這個(gè)方法實(shí)際上是調(diào)用Client.createSurface()來(lái)創(chuàng)建一個(gè)Surface。在創(chuàng)建時(shí)有一個(gè)很重要的參數(shù)sp<IGraphicBufferProducer> gbp,在下面源碼分析中我們也要重點(diǎn)注意它。這是因?yàn)閼?yīng)用所渲染的每一幀,實(shí)際上都會(huì)添加到IGraphicBufferProducer中,來(lái)等待SurfaceFlinger的渲染。
http://androidxref.com/6.0.1_r10/xref/frameworks/native/services/surfaceflinger/Client.cpp
status_t Client::createSurface(...)
{
...
//gbp 直接透?jìng)鞯搅薙urfaceFlinger
return mFlinger->createLayer(name, this, w, h, format, flags, windowType, ownerUid, handle, gbp, &parent);
}
Surface在SurfaceFlinger中對(duì)應(yīng)的實(shí)體其實(shí)是Layer
我們繼續(xù)看一下mFlinger->createLayer()
http://androidxref.com/6.0.1_r10/xref/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::createLayer(const String8& name,const sp<Client>& client...)
{
status_t result = NO_ERROR;
sp<Layer> layer; //將要?jiǎng)?chuàng)建的layer
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceNormal:
result = createBufferLayer(client,
uniqueName, w, h, flags, format,
handle, gbp, &layer); // 注意gbp,這時(shí)候還沒有構(gòu)造呢!
break;
... //Layer 分為好幾種,這里不全部列出
}
...
result = addClientLayer(client, *handle, *gbp, layer, *parent); //這個(gè)layer和client相關(guān)聯(lián), 添加到Client的mLayers集合中。
...
return result;
}
從SurfaceFlinger.createLayer()方法可以看出Layer分為好幾種。我們這里只對(duì)普通的BufferLayer的創(chuàng)建做一下分析,看createBufferLayer():
http://androidxref.com/6.0.1_r10/xref/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client... sp<Layer>* outLayer)
{
...
sp<BufferLayer> layer = new BufferLayer(this, client, name, w, h, flags);
status_t err = layer->setBuffers(w, h, format, flags); //設(shè)置layer的寬高
if (err == NO_ERROR) {
*handle = layer->getHandle(); //創(chuàng)建handle
*gbp = layer->getProducer(); //創(chuàng)建 gbp IGraphicBufferProducer
*outLayer = layer; //把新建的layer的指針拷貝給outLayer,這樣outLayer就指向了新建的BufferLayer
}
return err;
}
前面我說(shuō)過(guò)IGraphicBufferProducer(gbp)是一個(gè)很重要的對(duì)象,它涉及到SurfaceFlinger的渲染邏輯,下面我們就看一下這個(gè)對(duì)象的創(chuàng)建邏輯:
IGraphicBufferProducer(gbp)的創(chuàng)建
sp<IGraphicBufferProducer> BufferLayer::getProducer() const {
return mProducer;
}
即mProducer其實(shí)是Layer的成員變量,它的創(chuàng)建時(shí)機(jī)是Layer第一次被使用時(shí):
void BufferLayer::onFirstRef() {
...
BufferQueue::createBufferQueue(&producer, &consumer, true);
mProducer = new MonitoredProducer(producer, mFlinger, this);
...
}
所以mProducer的實(shí)例是MonitoredProducer,但其實(shí)它只是一個(gè)裝飾類,它實(shí)際功能都委托給構(gòu)造它的參數(shù)producer:
BufferQueue.cpp
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
...
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); //注意這個(gè)consumer
...
*outProducer = producer;
*outConsumer = consumer;
}
所以實(shí)際實(shí)現(xiàn)mProducer的工作的queueProducer是BufferQueueProducer。
所以構(gòu)造一個(gè)SurfaceControl所做的工作就是創(chuàng)建了一個(gè)SurfaceControl,并讓SurfaceFlinger創(chuàng)建了一個(gè)對(duì)應(yīng)的Layer,Layer中有一個(gè)IGraphicBufferProducer,它的實(shí)例是BufferQueueProducer。
可以用下面這個(gè)圖來(lái)描述SurfaceControl的創(chuàng)建過(guò)程:
從SurfaceControl中獲取Surface
我們回看WindowManagerService.createSurfaceControl(), 來(lái)看一下java層的Surface對(duì)象到底是個(gè)什么:
http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private int createSurfaceControl(Surface outSurface, int result, WindowState win,WindowStateAnimator winAnimator) {
...
surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
...
surfaceController.getSurface(outSurface);
}
上面我們已經(jīng)了解了winAnimator.createSurfaceLocked的整個(gè)過(guò)程,我們看一下surfaceController.getSurface(outSurface), surfaceController是WindowSurfaceController的實(shí)例:
//WindowSurfaceController.java
void getSurface(Surface outSurface) {
outSurface.copyFrom(mSurfaceControl);
}
//Surface.java
public void copyFrom(SurfaceControl other) {
...
long surfaceControlPtr = other.mNativeObject;
...
long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);
...
mNativeObject = ptr; // mNativeObject指向native創(chuàng)建的Surface
}
即Surface.copyFrom()方法調(diào)用nativeGetFromSurfaceControl()來(lái)獲取一個(gè)指針,這個(gè)指針是根據(jù)前面創(chuàng)建的SurfaceControl的指針來(lái)尋找的,即傳入的參數(shù)surfaceControlPtr:
android_view_Surface.cpp
static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj)); //把java指針轉(zhuǎn)化內(nèi)native指針
sp<Surface> surface(ctrl->getSurface()); //直接構(gòu)造一個(gè)Surface,指向 ctrl->getSurface()
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner); //強(qiáng)引用
}
return reinterpret_cast<jlong>(surface.get());
}
這里的ctrl指向前面創(chuàng)建的SurfaceControl,繼續(xù)追溯ctrl->getSurface():
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
return generateSurfaceLocked();
}
return mSurfaceData;
}
sp<Surface> SurfaceControl::generateSurfaceLocked() const
{
//這個(gè)mGraphicBufferProducer其實(shí)就是上面分析的BufferQueueProducer
mSurfaceData = new Surface(mGraphicBufferProducer, false);
return mSurfaceData;
}
即直接new了一個(gè)nativie的Surface返回給java層,java層的Surface指向的就是native層的Surface。
所以Surface的實(shí)際創(chuàng)建可以用下圖表示:
經(jīng)過(guò)上面這個(gè)圖,也可以理解SurfaceControl為什么叫SurfaceControl了。
總結(jié)
以上是生活随笔為你收集整理的Android的Surface的创建的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: reg类型变量综合电路_2014年PLD
- 下一篇: Linux如何查看进程、杀死进程、启动进