文档章节

Android深入浅出之Binder机制(一)

ifindbug
 ifindbug
发布于 2014/09/25 23:23
字数 3247
阅读 70
收藏 1
点赞 0
评论 0

一、 说明

Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的。所以搞明白Binder的话,在很大程度上就能理解程序运行的流程。

我们这里将以MediaService的例子来分析Binder的使用:

  • ServiceManager,这是Android OS的整个服务的管理程序

  • MediaService,这个程序里边注册了提供媒体播放的服务程序MediaPlayerService,我们最后只分析这个

  • MediaPlayerClient,这个是与MediaPlayerService交互的客户端程序

下面先讲讲MediaService应用程序。

二、 MediaService的诞生

MediaService是一个应用程序,虽然Android搞了七七八八的JAVA之类的东西,但是在本质上,它还是一个完整的Linux操作系统,也还没有牛到什么应用程序都是JAVA写。所以,MS(MediaService)就是一个和普通的C++应用程序一样的东西。

MediaService的源码文件在:framework\base\Media\MediaServer\Main_mediaserver.cpp中。让我们看看到底是个什么玩意儿!

<!-- lang: cpp -->
int main( int argc, char** argv )

{
/* FT,就这么简单?? */

/* 获得一个ProcessState实例 */

	sp<ProcessState> proc( ProcessState::self() );


/* 得到一个ServiceManager对象 */

	sp<IServiceManager> sm = defaultServiceManager();

	MediaPlayerService::instantiate();              /* 初始化MediaPlayerService服务 */

	ProcessState::self()->startThreadPool();        /*看名字,启动Process的线程池? */

	IPCThreadState::self()->joinThreadPool();       /* 将自己加入到刚才的线程池? */
}

其中,我们只分析MediaPlayerService。

这么多疑问,看来我们只有一个个函数深入分析了。不过,这里先简单介绍下sp这个东西。

sp,究竟是smart pointer还是strong pointer呢?其实我后来发现不用太关注这个,就把它当做一个普通的指针看待,即sp<IServiceManager>======》IServiceManager*吧。sp是google搞出来的为了方便C/C++程序员管理指针的分配和释放的一套方法,类似JAVA的什么WeakReference之类的。我个人觉得,要是自己写程序的话,不用这个东西也成。

好了,以后的分析中,sp<XXX>就看成是XXX*就可以了。

2.1 ProcessState

第一个调用的函数是ProcessState::self(),然后赋值给了proc变量,程序运行完,proc会自动delete内部的内容,所以就自动释放了先前分配的资源。

ProcessState位置在framework\base\libs\binder\ProcessState.cpp

<!-- lang: cpp -->
sp<ProcessState> ProcessState::self()

{
	if ( gProcess != NULL )
		return(gProcess);  ---- >第 一 次进来肯定 走这 儿

	AutoMutex _l( gProcessMutex );  --->锁 保护

	if ( gProcess == NULL )
		gProcess = new ProcessState; --->创 建 一 个 ProcessState对象

	return(gProcess); --->看见没,这里返回的是指针, 但是函数返回的是sp<xxx>,所以把sp<xxx>看成是XXX*是可以的 
}

再来看ProcessState构造函数

<!-- lang: cpp -->
/* 这个构造函数看来很重要 */

ProcessState::ProcessState()
: mDriverFD( open_driver() )----->Android很多代码都是这么写的,稍不留神就没看见这里调用了一个很重要的函数

, mVMStart( MAP_FAILED ) /*映射内存的起始地址 */

 , mManagesContexts( false )

, mBinderContextCheckFunc( NULL )

, mBinderContextUserData( NULL )

, mThreadPoolStarted( false )

, mThreadPoolSeq( 1 )
{
	if ( mDriverFD >= 0 )
	{
/* BIDNER_VM_SIZE定义为(1*1024*1024) - (4096 *2) 1M-8K */

		mVMStart = mmap( 0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,

				 mDriverFD, 0 ); /* 这个需要你自己去man mmap的用法了,不过大概意思就是 */

/* 将fd映射为内存,这样内存的memcpy等操作就相当于write/read(fd)了 */
	}

	...
}

<font color="red">最讨厌这种在构造list中添加函数的写法了,常常疏忽某个变量的初始化是一个函数调用的结果。</font>

open_driver,就是打开/dev/binder这个设备,这个是android在内核中搞的一个专门用于完成进程间通讯而设置的一个虚拟的设备。BTW,说白了就是内核提供的一个机制,这个和我们用socket加NET_LINK方式和内核通讯是一个道理。

<!-- lang: cpp -->
static int open_driver()

{
	int fd = open( "/dev/binder", O_RDWR ); /* 打开/dev/binder */

	if ( fd >= 0 )
	{
		....

		size_t maxThreads = 15;

		/* 通过ioctl方式告诉内核,这个fd支持最大线程数是15个。 */

		result = ioctl( fd, BINDER_SET_MAX_THREADS, &maxThreads );
	}

	return(fd);
}

好了,到这里Process::self就分析完了,到底干什么了呢?

  • 打开/dev/binder设备,这样的话就相当于和内核binder机制有了交互的通道

  • 映射fd到内存,设备的fd传进去后,估计这块内存是和binder设备共享的

接下来,就到调用defaultServiceManager()地方了。

2.2 defaultServiceManager

defaultServiceManager位置在framework\base\libs\binder\IServiceManager.cpp中

<!-- lang: cpp -->
sp<IServiceManager> defaultServiceManager()

{
	if ( gDefaultServiceManager != NULL )
		return(gDefaultServiceManager);

	/* 又是一个单例,设计模式中叫 singleton。 */

	{
		AutoMutex _l( gDefaultServiceManagerLock );


		if ( gDefaultServiceManager == NULL )
		{
                /* 真正的gDefaultServiceManager是在这里创建的喔 */

			gDefaultServiceManager = interface_cast<IServiceManager>(

				ProcessState::self()->getContextObject( NULL ) );
		}
	}

	return(gDefaultServiceManager);
}

gDefaultServiceManager = interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL));

ProcessState::self肯定返回的是刚才创建的gProcess,然后调用它的getContextObject,注意,传进去的是NULL,即0。

<!-- lang: cpp -->
/* 回到ProcessState类, */

sp<IBinder> ProcessState::getContextObject( const sp<IBinder> & caller )

{
	if ( supportsProcesses() )  /* 该函数根据打开设备是否成功来判断是否支持process */

	{ /* 在真机上肯定走这个 */
		return(getStrongProxyForHandle( 0 ) );  /* 注意,这里传入0 */
	}
}

进入到getStrongProxyForHandle,函数名字怪怪的,经常严重阻碍大脑运转。

注意这个参数的命名,handle。搞过windows的应该比较熟悉这个名字,这是对资源的一种标示,其实说白了就是某个数据结构,保存在数组中,然后handle是它在这个数组中的索引。--->就是这么一个玩意儿

<!-- lang: cpp -->
sp<IBinder> ProcessState::getStrongProxyForHandle( int32_t handle )

{
	sp<IBinder> result;

	AutoMutex _l( mLock );


	handle_entry* e = lookupHandleLocked( handle ); --哈哈,果然,从数组中查找对应索引的资源 。      

    //lookupHandleLocked这个就不说了,内部会返回一个handle_entry

/*
 *
 * struct handle_entry {
 *
 *              IBinder* binder;--->Binder
 *
 *              RefBase::weakref_type* refs;-->不知道是什么,不影响.
 *
 *          };
 *
 */

	if ( e != NULL )
	{
		IBinder* b = e->binder; -- > 第一次进来,肯定为空

		if ( b == NULL || !e->refs->attemptIncWeak( this ) )
		{
			b = new BpBinder( handle ); --->看见了吧,创建了一个新的BpBinder

			e->binder = b;

			result = b;
		}
		....
	}

	return(result); --->返回刚才创建的BpBinder
}

到这里,是不是有点乱了?对,当人脑分析的函数调用太深的时候,就容易忘记。

我们是从gDefaultServiceManager = interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL));开始搞的,现在,这个函数调用将变成gDefaultServiceManager = interface_cast<IServiceManager>(new BpBinder(0));

BpBinder又是个什么玩意儿?Android名字起得太眼花缭乱了。

因为还没介绍Binder机制的大架构,所以这里介绍BpBinder不合适,但是又讲到BpBinder了,不介绍Binder架构似乎又说不清楚....,sigh!

恩,还是继续把层层深入的函数调用栈化繁为简吧,至少大脑还可以工作。先看看BpBinder的构造函数吧。

2.3 BpBinder

BpBinder位置在framework\base\libs\binder\BpBinder.cpp中。

<!-- lang: cpp -->
BpBinder::BpBinder( int32_t handle )

: mHandle( handle ) /* 注意,接上述内容,这里调用的时候传入的是0 */

, mAlive( 1 )

, mObitsSent( 0 )

, mObituaries( NULL )

{
	IPCThreadState::self()->incWeakHandle( handle ); /* FT,竟然到IPCThreadState::self() */
}

这里一块说说吧。IPCThreadState::self估计怎么着又是 一 个 singleton 吧 ?

<!-- lang: cpp -->
/* 该文件位置在framework\base\libs\binder\IPCThreadState.cpp */

IPCThreadState* IPCThreadState::self()

{
	if ( gHaveTLS ) /* 第一次进来为false */

	{
    restart:

		const pthread_key_t k = gTLS;

/* TLS是Thread Local Storage的意思,不懂得自己去google下它的作用吧。这里只需要 */

/* 知道这种空间每个线程有一个,而且线程间不共享这些空间,好处是?我就不用去搞什么 */

/*同步了。在这个线程,我就用这个线程的东西,反正别的线程获取不到其他线程TLS中的数据。===》这句话有漏洞,钻牛角尖的明白大概意思就可以了。 */

/* 从线程本地存储空间中获得保存在其中的IPCThreadState对象 */

/* 这段代码写法很晦涩,看见没,只有pthread_getspecific,那么肯定有地方调用 */

/* pthread_setspecific。 */

		IPCThreadState* st = (IPCThreadState *) pthread_getspecific( k );

		if ( st )
			return(st);

		return(new IPCThreadState); /* new一个对象, */
	}


	if ( gShutdown )
		return(NULL);


	pthread_mutex_lock( &gTLSMutex );

	if ( !gHaveTLS )
	{
		if ( pthread_key_create( &gTLS, threadDestructor ) != 0 )
		{
			pthread_mutex_unlock( &gTLSMutex );

			return(NULL);
		}

		gHaveTLS = true;
	}

	pthread_mutex_unlock( &gTLSMutex );

	goto restart; /* 我FT,其实goto没有我们说得那样卑鄙,汇编代码很多跳转语句的。 */

/* 关键是要用好。 */
}


/* 这里是构造函数,在构造函数里边pthread_setspecific */

IPCThreadState::IPCThreadState()

	: mProcess( ProcessState::self() ), mMyThreadId( androidGetTid() )

{
	pthread_setspecific( gTLS, this );

	clearCaller();

	mIn.setDataCapacity( 256 );

/* mIn,mOut是两个Parcel,干嘛用的啊?把它看成是命令的buffer吧。再深入解释,又会大脑停摆的。 */

	mOut.setDataCapacity( 256 );
}

出来了,终于出来了....,恩,回到BpBinder那。

<!-- lang: cpp -->
BpBinder::BpBinder(int32_t handle)

: mHandle(handle) //注意,接上述内容,这里调用的时候传入的是0

, mAlive(1)

, mObitsSent(0)

, mObituaries(NULL)

{

......

IPCThreadState::self()->incWeakHandle(handle);

/**什么incWeakHandle,不讲了..**/

}

喔,new BpBinder就算完了。到这里,我们创建了些什么呢?

  • ProcessState有了。

  • IPCThreadState有了,而且是主线程的。

  • BpBinder有了,内部handle值为0。

gDefaultServiceManager = interface_cast<IServiceManager>(new BpBinder(0));

终于回到原点了,大家是不是快疯掉了?

interface_cast,我第一次接触的时候,把它看做类似的static_cast一样的东西,然后死活也搞不明白 BpBinder指针怎么能强转为IServiceManager,花了n多时间查看BpBinder是否和IServiceManager继承还是咋的....。

终于,我用ctrl+鼠标(source insight)跟踪进入了interface_cast

IInterface.h位于framework/base/include/binder/IInterface.h

<!-- lang: cpp -->
template<typename INTERFACE>

inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)

{

    return INTERFACE::asInterface(obj);

}

所以,上面等价于:

<!-- lang: cpp -->

inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj)

{

    return IServiceManager::asInterface(obj);

}

看来,只能跟到IServiceManager了。

IServiceManager.h---》framework/base/include/binder/IServiceManager.h

看看它是如何定义的:

2.4 IServiceManager

<!-- lang: cpp -->
class IServiceManager : public IInterface

{

//ServiceManager,字面上理解就是Service管理类,管理什么?增加服务,查询服务等

//这里仅列出增加服务addService函数

public:

    DECLARE_META_INTERFACE(ServiceManager);

     virtual status_t   addService( const String16& name,

                                            const sp<IBinder>& service) = 0;

};

DECLARE_META_INTERFACE(ServiceManager)??

怎么和MFC这么类似?微软的影响很大啊!知道MFC的,有DELCARE肯定有IMPLEMENT。

果然,这两个宏DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE(INTERFACE, NAME)都在刚才的IInterface.h中定义。我们先看看DECLARE_META_INTERFACE这个宏往IServiceManager加了什么?

下面是DECLARE宏

<!-- lang: cpp -->
#define DECLARE_META_INTERFACE(INTERFACE)                               \

static const android::String16 descriptor;                          \

static android::sp<I##INTERFACE> asInterface(                       \

        const android::sp<android::IBinder>& obj);                  \

virtual const android::String16& getInterfaceDescriptor() const;    \

I##INTERFACE();                                                     \

virtual ~I##INTERFACE(); 

我们把它兑现到IServiceManager就是:

static const android::String16 descriptor; -->喔,增加一个描述字符串

`static android::sp< IServiceManager > asInterface(const android::spandroid::IBinder&

obj)` --->增加一个asInterface函数

virtual const android::String16& getInterfaceDescriptor() const;--->增加一个get函数

估计其返回值就是descriptor这个字符串

IServiceManager (); \

virtual ~IServiceManager(); \**增加构造和虚析购函数...**\

那IMPLEMENT宏在哪定义的呢?

见IServiceManager.cpp。位于framework/base/libs/binder/IServiceManager.cpp

IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");

下面是这个宏的定义

<!-- lang: cpp -->

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \

    const android::String16 I##INTERFACE::descriptor(NAME);             \

    const android::String16&                                            \

            I##INTERFACE::getInterfaceDescriptor() const {              \

        return I##INTERFACE::descriptor;                                \

    }                                                                   \

    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \

            const android::sp<android::IBinder>& obj)                   \

    {                                                                   \

        android::sp<I##INTERFACE> intr;                                 \

        if (obj != NULL) {                                              \

            intr = static_cast<I##INTERFACE*>(                          \

                obj->queryLocalInterface(                               \

                        I##INTERFACE::descriptor).get());               \

            if (intr == NULL) {                                         \

                intr = new Bp##INTERFACE(obj);                          \

            }                                                           \

        }                                                               \

        return intr;                                                    \

    }                                                                   \

    I##INTERFACE::I##INTERFACE() { }                                    \

I##INTERFACE::~I##INTERFACE() { }                                   \

很麻烦吧?尤其是宏看着头疼。赶紧兑现下吧。

<!-- lang: cpp -->
const

android::String16 IServiceManager::descriptor(“android.os.IServiceManager”);

const android::String16& IServiceManager::getInterfaceDescriptor() const

 {  return IServiceManager::descriptor;//返回上面那个android.os.IServiceManager

   }                                                                      android::sp<IServiceManager> IServiceManager::asInterface(

            const android::sp<android::IBinder>& obj)

    {

        android::sp<IServiceManager> intr;

        if (obj != NULL) {                                             

            intr = static_cast<IServiceManager *>(                         

                obj->queryLocalInterface(IServiceManager::descriptor).get());              

            if (intr == NULL) {                                         

                intr = new BpServiceManager(obj);                         

            }                                                          

        }                                                               

        return intr;                                                   

    }                                                                 

    IServiceManager::IServiceManager () { }                                   

    IServiceManager::~ IServiceManager() { }

哇塞,asInterface是这么搞的啊,赶紧分析下吧,还是不知道interface_cast怎么把BpBinder*转成了IServiceManager

我们刚才解析过的interface_cast<IServiceManager>(new BpBinder(0)),

原来就是调用asInterface(new BpBinder(0))

<!-- lang: cpp -->
android::sp<IServiceManager> IServiceManager::asInterface(

        const android::sp<android::IBinder>& obj)

{

    android::sp<IServiceManager> intr;

    if (obj != NULL) {                                             

        ....                                      

                intr = new BpServiceManager(obj);

//神呐,终于看到和IServiceManager相关的东西了,看来

//实际返回的是BpServiceManager(new BpBinder(0));                         

            }                                                          

        }                                                              

        return intr;                                                   

}                                

BpServiceManager是个什么玩意儿?p是什么个意思?

2.5 BpServiceManager

终于可以讲解点架构上的东西了。p是proxy即代理的意思,Bp就是BinderProxy,BpServiceManager,就是SM的Binder代理。既然是代理,那肯定希望对用户是透明的,那就是说头文件里边不会有这个Bp的定义。是吗?

果然,BpServiceManager就在刚才的IServiceManager.cpp中定义。

<!-- lang: cpp -->
class BpServiceManager : public BpInterface<IServiceManager>

//这种继承方式,表示同时继承BpInterface和IServiceManager,这样IServiceManger的addService必然在这个类中实现

{

public:

//注意构造函数参数的命名 impl,难道这里使用了Bridge模式?真正完成操作的是impl对象?

//这里传入的impl就是new BpBinder(0)

    BpServiceManager(const sp<IBinder>& impl)

        : BpInterface<IServiceManager>(impl)

    {

    }

     virtual status_t addService(const String16& name, const sp<IBinder>& service)

    {

       //待会再说..

}

//基类BpInterface的构造函数(经过兑现后)

//这里的参数又叫remote,唉,真是害人不浅啊。

inline BpInterface< IServiceManager >::BpInterface(const sp<IBinder>& remote)

    : BpRefBase(remote)

{

}

BpRefBase::BpRefBase(const sp<IBinder>& o)

    : mRemote(o.get()), mRefs(NULL), mState(0)

//o.get(),这个是sp类的获取实际数据指针的一个方法,你只要知道

//它返回的是sp<xxxx>中xxx* 指针就行

{

//mRemote就是刚才的BpBinder(0)

   ...

}

好了,到这里,我们知道了:

sp<IServiceManager> sm = defaultServiceManager();返回的实际是BpServiceManager,它的remote对象是BpBinder,传入的那个handle参数是0。

现在重新回到MediaService。

<!-- lang: cpp -->
int main(int argc, char** argv)

{

    sp<ProcessState> proc(ProcessState::self());

sp<IServiceManager> sm = defaultServiceManager();

//上面的讲解已经完了

MediaPlayerService::instantiate();//实例化MediaPlayerservice

//看来这里有名堂!

 

    ProcessState::self()->startThreadPool();

    IPCThreadState::self()->joinThreadPool();

}

到这里,我们把binder设备打开了,得到一个BpServiceManager对象,这表明我们可以和SM打交道了,但是好像没干什么有意义的事情吧?

(未完待续...)

© 著作权归作者所有

共有 人打赏支持
ifindbug
粉丝 0
博文 9
码字总数 0
作品 0
广州
Android AIDL浅析及异步调用

AIDL:Android Interface Definition Language,即 Android 接口定义语言。 AIDL 是什么 Android 系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。 为了使其...

cspecialy ⋅ 05/20 ⋅ 0

Android应用开发以及设计思想深度剖析(3)

特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处。作者系LiAnLab.org资深Android技术顾问吴赫老师。 我们接下来从安全性,性能,功能,可移植性的角度分别分析Android系统为应用...

21cnbao ⋅ 2012/09/14 ⋅ 0

浅入浅出 Android 安全:第四章 Android 框架层安全

第四章 Android 框架层安全 来源:Yury Zhauniarovich | Publications 译者:飞龙 协议:CC BY-NC-SA 4.0 如我们在第1.2节中所描述的那样,应用程序框架级别上的安全性由 IPC 引用监视器实现...

apachecn_飞龙 ⋅ 2016/12/05 ⋅ 0

Android深入浅出

Handler 源码注释翻译 让你无缝了解 Handler~ Android:这是一份很详细的 Socket 使用攻略 所有关于 Android Socket 的使用都在这里了 由Message,Handler,MessageQueue和Looper引发的思考?...

掘金官方 ⋅ 01/04 ⋅ 0

2018届应届生android面试总结

写在前面 四月份结束了拥抱变化后的大规模面试后,2018届android校招面试总结:百度,大疆,乐视,知乎在五月份里,自己进行了最后俩次面试。算是对自己的大学生涯的一个交代。 面试过程 这俩...

MDove ⋅ 05/23 ⋅ 0

人人都会设计模式:05、代理模式--Proxy

教程简介 1、阅读对象 本篇教程适合新手阅读,老手直接略过 2、教程难度 初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢 正文 一、什么是代理模式 1、生活中的代理 ...

TigerChain ⋅ 06/15 ⋅ 0

云图信安(成都)科技有限公司招聘

  公司介绍   云图信安(成都)科技有限公司是一家以互联网平台运营、WEB应用开发、软件开发、大数据分析、移动互联网通讯技术、云计算、营销推广、技术培训等技术为核心的科技公司。公司...

FreeBuf ⋅ 06/13 ⋅ 0

Android启动优化之startActivity的底层实现

本文没啥用 不涉及任何优化 但是总感觉不总结一下说不过去 因为我们毕竟分析的是启动优化 这个不了解透彻感觉本末倒置了 所以就简单整理了一下 以launcher为例(毕竟做启动优化主要针对的是冷...

qq_36523667 ⋅ 04/25 ⋅ 0

BroadcastReceiver的源码分析

android提供了广播机制,通过BroadcastReceiver可以在不同的进程间传递消息。类似于观察者模式,A应用通过注册广播表示A对消息subject感兴趣,当B应用发出subject类型的消息的时候,A应用就能...

JasmineBen ⋅ 05/18 ⋅ 0

浅入浅出 Android 安全:第一章 Android

第一章 Android 来源:Yury Zhauniarovich | Publications 译者:飞龙 协议:CC BY-NC-SA 4.0 Android 安全架构的理解不仅帮助我了解 Android 的工作原理,而且为我开启了如何构建移动操作系...

apachecn_飞龙 ⋅ 2016/11/27 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

LVM

LVM: 硬盘划分分区成物理卷->物理卷组成卷组->卷组划分逻辑分区。 1.磁盘分区: fdisk /dev/sdb 划分几个主分区 输入t更改每个分区类型为8e(LVM) 使用partprobe生成分区的文件:如/dev/sd...

ZHENG-JY ⋅ 33分钟前 ⋅ 0

彻底删除Microsoft Office的方法

参照此链接彻底删除Office https://support.office.com/zh-cn/article/%e4%bb%8e-pc-%e5%8d%b8%e8%bd%bd-office-9dd49b83-264a-477a-8fcc-2fdf5dbf61d8?ui=zh-CN&rs=zh-CN&ad=CN......

Kampfer ⋅ 48分钟前 ⋅ 0

大盘与个股之间关系

大盘走多:积极出手 顺势加码 大盘走空: 少量出手 退场观望 大盘做头:逆势减码 少量操作 大盘做底 : 小量建仓 小量试单

guozenhua ⋅ 50分钟前 ⋅ 0

Day16 LVM(逻辑卷管理)与磁盘故障小案例

lvm详解 简述 LVM的产生是因为传统的分区一旦分区好后就无法在线扩充空间,也存在一些工具能实现在线扩充空间但是还是会面临数据损坏的风险;传统的分区当分区空间不足时,一般的解决办法是再...

杉下 ⋅ 56分钟前 ⋅ 0

rsync实现多台linux服务器的文件同步

一、首先安装rsync,怎样安装都行,rpm,yum,还是你用源码安装都可以。因为我用的是阿里云的ESC,yum install rsync就ok了。 二、配置rsync服务 1.先建立个同步数据的帐号 123 groupadd r...

在下头真的很硬 ⋅ 今天 ⋅ 0

前端基础(三):函数

字数:1685 阅读时间:5分钟 函数定义 在最新的ES规范中,声明函数有4中方法: -函数声明 -函数表达式 -构造函数Function -生成器函数 1.函数声明 语法: function name([param[, param2 [....

老司机带你撸代码 ⋅ 今天 ⋅ 0

Java虚拟机的Heap监狱

在Java虚拟机中,我是一个位高权重的大管家,他们都很怕我,尤其是那些Java 对象,我把他们圈到一个叫做Heap的“监狱”里,严格管理,生杀大权尽在掌握。 中国人把Stack翻译成“栈”,把Hea...

java高级架构牛人 ⋅ 今天 ⋅ 0

Spring MVC基本概念

只写Controller

颖伙虫 ⋅ 今天 ⋅ 0

微软重金收购GitHub的背后逻辑原来是这样的

全球最大的开发者社区GitHub网站花落谁家的问题已经敲定,微软最终以75亿美元迎娶了这位在外界看来无比“神秘”的小家碧玉。尽管此事已过去一些时日,但整个开发者世界,包括全球各地的开源社...

linux-tao ⋅ 今天 ⋅ 0

磁盘管理—逻辑卷lvm

4.10-4.12 lvm 操作流程: 磁盘分区-->创建物理卷-->划分为卷组-->划分成逻辑卷-->格式化、挂载-->扩容。 磁盘分区 注: 创建分区时需要更改其文件类型为lvm(代码8e) 分区 3 已设置为 Linu...

弓正 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部