主頁 > 作業系統 > android6.0系統Healthd深入分析

android6.0系統Healthd深入分析

2020-09-13 12:00:43 作業系統

概述

Healthd是android4.4之后提出來的一種中介模型,該模型向下監聽來自底層的電池事件,向上傳遞電池資料資訊給Framework層的BatteryService用以計算電池電量相關狀態資訊,BatteryServcie通過傳遞來的資料來計算電池電量顯示,剩余電量,電量級別等資訊,如果收到過溫報警或者嚴重低電報警等資訊,系統會直接關機,保護硬體,

主模塊處理流程

Healthd模塊代碼是在system/core/healthd/,其模塊入口在healthd的main函式,函式代碼如下:

int main(int argc, char **argv) {
 
int ch;
 
int ret;
 
klog_set_level(KLOG_LEVEL);
 
healthd_mode_ops = &android_ops;
 
 
 
if (!strcmp(basename(argv[0]), "charger")) {
 
        healthd_mode_ops = &charger_ops;
 
} else {
 
        while ((ch = getopt(argc, argv, "cr")) != -1) {
 
            switch (ch) {
 
            case 'c':
 
                healthd_mode_ops = &charger_ops;
 
                break;
 
            case 'r':
 
                healthd_mode_ops = &recovery_ops;
 
                break;
 
            case '?':
 
            default:
 
                KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n",
 
                           optopt);
 
                exit(1);
 
            }
 
        }
 
    }
 
ret = healthd_init();
 
    if (ret) {
 
        KLOG_ERROR("Initialization failed, exiting\n");
 
        exit(2);
 
    }
 
 
 
    healthd_mainloop();
 
    KLOG_ERROR("Main loop terminated, exiting\n");
 
    return 3;
 
}

可以看出Main函式并不長,但是其作用確實巨大的,main函式起著一個統籌兼顧的作用,其他各個模塊函式去做一些具體相應的作業,最后匯總到main函式中被呼叫,

代碼中開始便是決議引數,healthd_mode_ops是一個關于充電狀態結構體變數,結構體變數里的引數是函式指標,在初始化時指向各個不同的操作函式,當開機充電時變數賦值為&android_ops,關機充電時候變數賦值為&charger_ops,

在ret = healthd_init();中進行一些初始化作業,

static int healthd_init() {
 
epollfd = epoll_create(MAX_EPOLL_EVENTS);
 
    if (epollfd == -1) {
 
       KLOG_ERROR(LOG_TAG,
 
                   "epoll_create failed; errno=%d\n",
 
                   errno);
 
       return -1;
 
   }
 
 
 
    healthd_board_init(&healthd_config);
 
    healthd_mode_ops->init(&healthd_config);
 
    wakealarm_init();
 
    uevent_init();
 
    gBatteryMonitor = new BatteryMonitor();
 
    gBatteryMonitor->init(&healthd_config);
 
    return 0;
 
}

創建一個epoll的變數將其賦值給epollfd,在healthd_board_init中未作任何事便回傳了,

healthd_mode_ops->init呼叫有兩種情況:關機情況下呼叫charger_ops的init函式;開機情況下呼叫android_ops的init函式,這里就開機情況來分析,android_ops的init函式指標指向healthd_mode_android_init函式

代碼如下:

void healthd_mode_android_init(struct healthd_config* /*config*/) {
    ProcessState::self()->setThreadPoolMaxThreadCount(0);//執行緒池里最大執行緒數
    IPCThreadState::self()->disableBackgroundScheduling(true);//禁用后臺調度
    IPCThreadState::self()->setupPolling(&gBinderFd);//
 
    if (gBinderFd >= 0) {
        if (healthd_register_event(gBinderFd, binder_event))
            KLOG_ERROR(LOG_TAG,
                       "Register for binder events failed\n");
    }
 
    gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
    gBatteryPropertiesRegistrar->publish();
}

再來看看wakealarm_init函式:

static void wakealarm_init(void) {
    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
    if (wakealarm_fd == -1) {
        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
        return;
    }
 
    if (healthd_register_event(wakealarm_fd, wakealarm_event))
        KLOG_ERROR(LOG_TAG,
                   "Registration of wakealarm event failed\n");
 
    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
}

首先創建一個wakealarm_fd的定時器與之對應的檔案描述符,healthd_register_event將wakealarm事件注冊到wakealarm_fd檔案節點用以監聽wakealarm事件,wakealarm_set_interval設定alarm喚醒的間隔

再看看uevent_init函式:

static void uevent_init(void) {
    uevent_fd = uevent_open_socket(64*1024, true);
 
    if (uevent_fd < 0) {
        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
        return;
    }
 
    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
    if (healthd_register_event(uevent_fd, uevent_event))
        KLOG_ERROR(LOG_TAG,
                   "register for uevent events failed\n");
}

創建并打開一個64k的socket檔案描述符uevent_fd,設定檔案狀態標志為非阻塞模,將uevent事件注冊到uevent_fd檔案節點用以監聽uevent事件,

我們可以看到android利用epoll監聽了三個檔案節點的改變事件,分別是:通過gBinderfd監聽執行緒Binder通信事件;通過wakealarm_fd監聽wakealarm事件;通過uevent_fd監聽wakealarm事件,至于如何監聽后面做詳細分析

在healthd_init中最后創建BatteryMonitor的物件,并將其初始化,BatteryMonitor主要接受healthd傳來的資料,做電池狀態的計算并更新,

我們可以看到在BatterMonitor中的init函式中有以下陳述句:

DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
 
struct dirent* entry;
 
,,,,,,,
 
    while ((entry = readdir(dir))) {
 
        const char* name = entry->d_name;
 
,,,,,,
 
}

POWER_SUPPLY_SYSFS_PATH定義為"/sys/class/power_supply",在init函式中打開系統該檔案夾,然后一一讀取該檔案夾下的檔案內容,在while回圈中判斷該檔案夾下各個檔案節點的內容,并將其初始化給相關的引數.

至此,healthd_init函式就分析完了,其主要作業就是:創建了三個檔案節點用來監聽相應的三種事件改變;創建BatteryMonitor物件,并通過讀取/sys/class/power_supply將其初始化,

Healthd_init走完之后,接著就是呼叫healthd_mainloop函式,該函式維持了一個死回圈,代碼如下:

static void healthd_mainloop(void) {
 
while (1) {
 
        struct epoll_event events[eventct];
 
        int nevents;
 
        int timeout = awake_poll_interval;
 
        int mode_timeout;
 
        mode_timeout = healthd_mode_ops->preparetowait();
 
        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
 
            timeout = mode_timeout;
 
        nevents = epoll_wait(epollfd, events, eventct, timeout);
 
        if (nevents == -1) {
 
            if (errno == EINTR)
 
                continue;
 
            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
 
            break;
 
        }
 
        for (int n = 0; n < nevents; ++n) {
 
            if (events[n].data.ptr)
 
                (*(void (*)(int))events[n].data.ptr)(events[n].events);
 
        }
 
        if (!nevents)
 
            periodic_chores();
 
        healthd_mode_ops->heartbeat();
 
    }
 
    return;
 
}

Healthd_mainloop中維持了一個死回圈,死回圈中變數nevents 表示從epollfd中輪循中監聽得到的事件數目,這里介紹一下輪詢機制中重要函式epoll_waite().

epoll_wait運行的道理是:等侍注冊在epfd上的socket fd的事務的產生,若是產生則將產生的sokct fd和事務型別放入到events陣列中,且timeout如果為-1則為阻塞式,timeowout為0則表示非阻塞式,可以看到代碼中timeout為-1,故為阻塞式輪詢,當epollfd上有事件發生,則會走到下面的處理邏輯,事件處理主要在for回圈中:

在periodic_chores()中呼叫到healthd_battery_update()更新電池狀態,

void healthd_battery_update(void) {
 
   int new_wake_interval = gBatteryMonitor->update() ?
 
       healthd_config.periodic_chores_interval_fast :
 
           healthd_config.periodic_chores_interval_slow;
 
 
 
    if (new_wake_interval != wakealarm_wake_interval)
 
            wakealarm_set_interval(new_wake_interval);
 
    if (healthd_config.periodic_chores_interval_fast == -1)
 
        awake_poll_interval = -1;
 
    Else
 
        awake_poll_interval = new_wake_interval == healthd_config.periodic_chores_interval_fast ?
 
                -1 : healthd_config.periodic_chores_interval_fast * 1000;
 
}

可以看出該函式并不長,new_wake_interval表示新的wakealarm喚醒間隔,通過呼叫BatteryMonitor的update函式(后面詳細分析如何更新),其回傳值為是否處于充電狀態,當處于充電狀態,則喚醒間隔為healthd_config.periodic_chores_interval_fast(短間隔),當不再充電狀態時喚醒間隔為healthd_config.periodic_chores_interval_slow(長間隔)

當新的間隔變數new_wake_interval與舊的變數wakealarm_wake_interval不一樣,則將新的喚醒間隔設定成wakealarm的喚醒間隔;

awake_poll_internal作為下一次epoll_waite的timeout引數,在這里將其更新,在充電狀態下awake_poll_internal為-1,沒有充電的狀態下awake_poll_internal為60000ms

healthd主流程都是在main函式中處理,至此main已經分析完成,其簡要流程圖如下

image

Healthd處理邏輯

初始化處理

前面將healthd模塊中main函式分析完了,其主要作業流程有個大概的了解,但是其詳細處理邏輯并未做分析,在此之后,對Healthd的初始化,事件處理,狀態更新將做一個詳細的分析,

前面已經說過在healthd_init中創建了三個檔案節點gBinderfd,uevent_fd,wakealarm_fd,并用以注冊監聽三種事件,注冊監聽都是通過healthd_register_event函式實作的,

healthd_register_event(gBinderFd, binder_event);

healthd_register_event(wakealarm_fd, wakealarm_event);

healthd_register_event(uevent_fd, uevent_event);

其healthd_register_event實作代碼如下:

int healthd_register_event(int fd, void (*handler)(uint32_t)) {
 
struct epoll_event ev;
 
    ev.events = EPOLLIN | EPOLLWAKEUP;
 
    ev.data.ptr = (void *)handler;
 
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
 
        KLOG_ERROR(LOG_TAG,
 
                   "epoll_ctl failed; errno=%d\n", errno);
 
        return -1;
 
    }
 
    eventct++;
 
    return 0;
 
}

函式將相應的檔案節點事件賦值為函式的第二個形參,也就是說相應的gBinderfd的事件處理函式為binder_event函式,同理wakealarm_fd,ueven_fd的事件事件處理分別為wakealarm_event,uevent_event函式,然后將其三個檔案節點加入到epollfd中,

事件獲取與處理

Healthd中維持了一個阻塞式的死回圈healthd_mainloop,在該函式中提供阻塞式的監聽已發送的事件函式epoll_wait(),healthd_mainloop中有如下代碼

nevents = epoll_wait(epollfd, events, eventct, timeout);
 
 
 
for (int n = 0; n < nevents; ++n) {
 
if (events[n].data.ptr)
 
        (*(void (*)(int))events[n].data.ptr)(events[n].events);
 
}

當epoll_waite接受到gBinderfd,wakealarm_fd,uevent_fd其中的事件,便會將監聽到的事件加入到event陣列中,在for回圈中做處理,for回圈中代碼看起來非常難懂,其實if判斷的便是event有沒有相應的處理函式,在前面注冊事件時候已經提到,三種句柄上的事件都有對應的處理函式,也就是當收到gBinderfd上的事件,便用binder_event函式處理,當收到uevent_fd上的事件便用uevent_event處理,當收到wakealarm_fd上的事件便用wakealarm_event處理,

這里以較為重要的uevent_event事件處理為例:

#define UEVENT_MSG_LEN 2048
 
static void uevent_event(uint32_t /*epevents*/) {
 
char msg[UEVENT_MSG_LEN+2];
 
    char *cp;
 
    int n;
 
 
 
    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
 
    if (n <= 0)
 
        return;
 
    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
 
        return;
 
 
 
    msg[n] = '\0';
 
    msg[n+1] = '\0';
 
    cp = msg;
 
 
 
    while (*cp) {
 
        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
 
            healthd_battery_update();
 
            break;
 
        }
 
 
 
        /* advance to after the next \0 */
 
        while (*cp++)
 
            ;
 
    }
 
}

處理函式首先從uevent_fd 獲取事件數目,然后回圈判斷是否是來自與power_supply目錄下的事件,如果是,則呼叫到healthd_battery_update中去更新電池狀態,

更新電池狀態

當收到事件,做一些判斷作業便需要更新電池狀態,其更新函式為healthd.cpp下的healthd_battery_update函式,但是主要更新并不在heathd中完成的,而是在BatteryMonitor中的update函式,其代碼較多,分段分析:

props.chargerAcOnline = false;
 
props.chargerUsbOnline = false;
 
props.chargerWirelessOnline = false;
 
props.batteryStatus = BATTERY_STATUS_UNKNOWN;
 
props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
 
 
 
if (!mHealthdConfig->batteryPresentPath.isEmpty())
 
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
 
else
 
    props.batteryPresent = mBatteryDevicePresent;
 
 
 
props.batteryLevel = mBatteryFixedCapacity ?
 
    mBatteryFixedCapacity :
 
    getIntField(mHealthdConfig->batteryCapacityPath);
 
props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
 
 
 
props.batteryTemperature = mBatteryFixedTemperature ?
 
    mBatteryFixedTemperature :
 
    getIntField(mHealthdConfig->batteryTemperaturePath);
 
const int SIZE = 128;
 
char buf[SIZE];
 
String8 btech;
 
 
 
if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
 
    props.batteryStatus = getBatteryStatus(buf);
 
 
 
if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
 
    props.batteryHealth = getBatteryHealth(buf);
 
 
 
if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)
 
    props.batteryTechnology = String8(buf);

在init函式中將healthd_config 物件傳入,并且將里面的成員的一些地址資訊去初始化保存起來,主要是保存一些地址資訊,以及充電方式,在BatteryMonitor初始化中,heathd_config傳入init函式中,賦值為mHealthdConfig,上面一段主要是讀取/sys/class/power_supply下的檔案節點資訊初更新電池資料屬性值,

path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
 
                  mChargerNames[i].string());
 
 
 
if (readFromFile(path, buf, SIZE) > 0) {
 
if (buf[0] != '0') {
 
        path.clear();
 
        path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
 
                          mChargerNames[i].string());
 
        switch(readPowerSupplyType(path)) {
 
        case ANDROID_POWER_SUPPLY_TYPE_AC:
 
            props.chargerAcOnline = true;
 
            break;
 
        case ANDROID_POWER_SUPPLY_TYPE_USB:
 
            props.chargerUsbOnline = true;
 
            break;
 
        case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
 
            props.chargerWirelessOnline = true;
 
            break;
 
        default:
 
            KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
 
                         mChargerNames[i].string());
 
        }
 
    }
 
}

將電池當前的電量級別,電壓,溫度,健康狀況,電池狀態以及充放電倍率存入dmesgline變數中,在后面會將電池充電型別,電池使用時間都以字串存入dmesgline變數中,然后:

KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);//向log記錄電池當前各種狀態資訊

}

healthd_mode_ops->battery_update(&props);//更新電池

return props.chargerAcOnline | props.chargerUsbOnline |

props.chargerWirelessOnline;//回傳是否在充電狀態

整個update函式做完更新資料,記錄資料到log之后,然后呼叫到BatteryPropertiesRegistrar的update函式繼續更新電池狀態,最后回傳值為是否處于充電狀態,

BatteryPropertiesRegistrar的update函式未作任何操作呼叫Healthd_mode_android.cpp中的healthd_mode_android_battery_update函式,我們可以看看該函式

void healthd_mode_android_battery_update(
struct android::BatteryProperties *props) {
 
    if (gBatteryPropertiesRegistrar != NULL)
 
        gBatteryPropertiesRegistrar->notifyListeners(*props);
 
 
 
    return;
 
}

這里這里直接呼叫到BatteryPropertiesRegistrar的notifyListeners去通知props改變了,props是什么呢?props是定義的一個BatteryProperties屬性集,里面的成員變數包含了所有的電池狀態資訊,在update開始便通過讀取各個檔案節點的實時資料更新電池屬性props,更新完成后通過BatteryPropertiesRegistrar通知其屬性監聽者去更新狀態,但是誰是監聽呢?

我們可以看到framework層中的BatteryService.java的onStart函式中有如下代碼:

public void onStart() {
 
IBinder b = ServiceManager.getService("batteryproperties");
 
    final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
 
            IBatteryPropertiesRegistrar.Stub.asInterface(b);
 
    try {
 
        batteryPropertiesRegistrar.registerListener(new BatteryListener());
 
    } catch (RemoteException e) {
 
        // Should never happen.
 
    }

我們在初始化的時候已經提到過,當healthd初始化時候會創建BatteryPropertiesRegistrar的物件并將其publish注冊到系統服務中,注冊服務的陳述句如下:

defaultServiceManager()->addService(String16("batteryproperties"), this);

所以BatteryService在這里獲取該服務,并以此注冊其監聽器為BatteryListener(),該監聽器監聽到BatteryProperties改變便會呼叫到BatteryService的update函式,去做電池電量相關計算以及顯示,

至此更新操作基本分析完成,其簡要流程如下圖所示
image

總結

Healthd是framework層傳遞來自底層電池事件資訊并呼叫相關模塊更新電池狀態的一個中間層,其向下監聽來自底層PMU驅動上報的uevent電池事件,向上呼叫BatteryService去計算電池,電量,使用等相關資訊,它通過一個阻塞式的死回圈不斷監聽底層三個檔案節點上的事件資訊,當監聽到事件便呼叫到BatteryMonitor執行更新操作,通過BatteryService.java中注冊監聽電池屬性改變的函式,當電池屬性資訊發生改變,即回呼到BatteryService中做更新操作,更新完成一次電池事件的上報到更新整個流程就完成;總之Healthd是連接Battery模塊framework中java層與HAL層互動的主要通道,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/24213.html

標籤:嵌入式

上一篇:Android Battery 架構【轉】

下一篇:以太網驅動的流程淺析(三)-ifconfig的-19錯誤最底層分析【原創】

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • CA和證書

    1、在 CentOS7 中使用 gpg 創建 RSA 非對稱密鑰對 gpg --gen-key #Centos上生成公鑰/密鑰對(存放在家目錄.gnupg/) 2、將 CentOS7 匯出的公鑰,拷貝到 CentOS8 中,在 CentOS8 中使用 CentOS7 的公鑰加密一個檔案 gpg -a ......

    uj5u.com 2020-09-10 00:09:53 more
  • Kubernetes K8S之資源控制器Job和CronJob詳解

    Kubernetes的資源控制器Job和CronJob詳解與示例 ......

    uj5u.com 2020-09-10 00:10:45 more
  • VMware下安裝CentOS

    VMware下安裝CentOS 一、軟硬體準備 1 Centos鏡像準備 1.1 CentOS鏡像下載地址 下載地址 1.2 CentOS鏡像下載程序 點擊下載地址進入如下圖的網站,選擇需要下載的版本,這里選擇的是Centos8,點擊如圖所示。 決定選擇Centos8后,選擇想要的鏡像源進行下載,此 ......

    uj5u.com 2020-09-10 00:12:10 more
  • 如何使用Grep命令查找多個字串

    如何使用Grep 命令查找多個字串 大家好,我是良許! 今天向大家介紹一個非常有用的技巧,那就是使用 grep 命令查找多個字串。 簡單介紹一下,grep 命令可以理解為是一個功能強大的命令列工具,可以用它在一個或多個輸入檔案中搜索與正則運算式相匹配的文本,然后再將每個匹配的文本用標準輸出的格式 ......

    uj5u.com 2020-09-10 00:12:28 more
  • git配置http代理

    git配置http代理 經常遇到克隆 github 慢的問題,這里記錄一下幾種配置 git 代理的方法,解決 clone github 過慢。 目錄 git配置代理 git單獨配置github代理 git配置全域代理 配置終端環境變數 git配置代理 主要使用 git config 命令 git單獨 ......

    uj5u.com 2020-09-10 00:12:33 more
  • Linux npm install 裝包時提示Error EACCES permission denied解

    npm install 裝包時提示Error EACCES permission denied解決辦法 ......

    uj5u.com 2020-09-10 00:12:53 more
  • Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包

    Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包。 18 (flaskApi) [root@67 flaskDemo]# yum -y install nginx 19 已加載插件:fastestmirror, langpacks 20 Loading ......

    uj5u.com 2020-09-10 00:13:13 more
  • Linux查看服務器暴力破解ssh IP

    在公網的服務器上經常遇到別人爆破你服務器的22埠,用來挖礦或者干其他嘿嘿嘿的事情~ 這種情況下正確的做法是: 修改默認ssh的22埠 使用設定密鑰登錄或者白名單ip登錄 建議服務器密碼為復雜密碼 創建普通用戶登錄服務器(root權限過大) 建立堡壘機,實作統一管理服務器 統計爆破IP [root ......

    uj5u.com 2020-09-10 00:13:17 more
  • CentOS 7系統常見快捷鍵操作方式

    Linux系統中一些常見的快捷方式,可有效提高操作效率,在某些時刻也能避免操作失誤帶來的問題。 ......

    uj5u.com 2020-09-10 00:13:31 more
  • CentOS 7作業系統目錄結構介紹

    作業系統存在著大量的資料檔案資訊,相應檔案資訊會存在于系統相應目錄中,為了更好的管理資料資訊,會將系統進行一些目錄規劃,不同目錄存放不同的資源。 ......

    uj5u.com 2020-09-10 00:13:35 more
最新发布
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:43:21 more
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:42:36 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:26:53 more
  • 設定Windows主機的瀏覽器為wls2的默認瀏覽器

    這里以Chrome為例。 1. 準備作業 wsl是可以使用Windows主機上安裝的exe程式,出于安全考慮,默認情況下改功能是無法使用。要使用的話,終端需要以管理員權限啟動。 我這里以Windows Terminal為例,介紹如何默認使用管理員權限打開終端,具體操作如下圖所示: 2. 操作 wsl ......

    uj5u.com 2023-04-19 09:25:49 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:19:04 more
  • Linux學習筆記

    IP地址和主機名 IP地址 ifconfig可以用來查詢本機的IP地址,如果不能使用,可以通過install net-tools安裝。 Centos系統下ens33表示主網卡;inet后表示IP地址;lo表示本地回環網卡; 127.0.0.1表示代指本機;0.0.0.0可以用于代指本機,同時在放行設 ......

    uj5u.com 2023-04-18 06:52:01 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:50 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:01 more
  • 你是不是暴露了?

    作者:袁首京 原創文章,轉載時請保留此宣告,并給出原文連接。 如果您是計算機相關從業人員,那么應該經歷不止一次網路安全專項檢查了,你肯定是收到過資訊系統技術檢測報告,要求你加強風險監測,確保你提供的系統服務堅實可靠了。 沒檢測到問題還好,檢測到問題的話,有些處理起來還是挺麻煩的,尤其是線上正在運行的 ......

    uj5u.com 2023-04-05 16:52:56 more
  • 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實作

    1. 前文回顧 在之前的幾篇記憶體管理系列文章中,筆者帶大家從宏觀角度完整地梳理了一遍 Linux 記憶體分配的整個鏈路,本文的主題依然是記憶體分配,這一次我們會從微觀的角度來探秘一下 Linux 內核中用于零散小記憶體塊分配的記憶體池 —— slab 分配器。 在本小節中,筆者還是按照以往的風格先帶大家簡單 ......

    uj5u.com 2023-04-05 16:44:11 more