Linux設備驅動之IIO子系統——IIO框架及IIO數據結構

Linux設備驅動之IIO子系統——IIO框架及IIO數據結構
由于需要對ADC進行驅動設計,因此學習了一下Linux驅動的IIO子系統。本文翻譯自《Linux Device Drivers Development 》--John Madieu,本人水平有限,若有錯誤請大家指出。

IIO Framework

  工業I / O(IIO)是專用于模數轉換器(ADC)和數模轉換器(DAC)的內核子系統。隨著越來越多的具有不同代碼實現的傳感器(具有模擬到數字或數字到模擬,功能的測量設備)分散在內核源上,收集它們變得必要。這就是IIO框架以通用的方式所做的事情。自2009年以來,Jonathan Cameron和Linux-IIO社區一直在開發它。

  加速度計,陀螺儀,電流/電壓測量芯片,光傳感器,壓力傳感器等都屬于IIO系列器件。

  IIO模型基于設備和通道架構:

    l 設備代表芯片本身。它是層次結構的頂級。

    l 通道代表設備的單個采集線。設備可以具有一個或多個通道。例如,加速度計是具有 三個通道的裝置,每個通道對應一個軸(X,Y和Z)。

  IIO芯片是物理和硬件傳感器/轉換器。它作為字符設備(當支持觸發緩沖時)暴露給用戶空間,以及包含一組文件的sysfs目錄條目,其中一些文件代表通道。單個通道用單個sysfs文件條目表示。

  下面是從用戶空間與IIO驅動程序交互的兩種方式:

    l /sys/bus/iio/iio:deviceX/:表示傳感器及其通道

    l /dev/iio:deviceX: 表示導出設備事件和數據緩沖區的字符設備

IIO框架架構和布局

  上圖顯示了如何在內核和用戶空間之間組織IIO框架。 驅動程序使用IIO核心公開的一組工具和API來管理硬件并向IIO核心報告處理。 然后,IIO子系統通過sysfs接口和字符設備將整個底層機制抽象到用戶空間,用戶可以在其上執行系統調用。

  IIO API分布在多個頭文件中,如下所示:

#include / mandatory /

include / mandatory since sysfs is used /

include / For advanced users, to manage iio events /

include / mandatory to use triggered buffers /

include / Only if you implement trigger in your driver (rarely used)/

  在以下文章中,我們將描述和處理IIO框架的每個概念,例如

    遍歷其數據結構(設備,通道等)

    觸發緩沖支持和連續捕獲,以及其sysfs接口

    探索現有的IIO觸發器

    以單次模式或連續模式捕獲數據

    列出可用于幫助開發人員測試其設備的可用工具

(一):IIO data structures:IIO數據結構

  IIO設備在內核中表示為struct iio_dev結構體的一個實例,并由struct iio_info結構體描述。 所有重要的IIO結構都在include/linux/iio/iio.h中定義。

  iio_dev structure(iio_dev結構)

  該結構代表IIO設備,描述設備和驅動程序。 它告訴我們:

  l 設備上有多少個通道?

  l 設備可以在哪些模式下運行:單次,觸發緩沖?

  l 這個驅動程序可以使用哪些hooks鉤子?

復制代碼
struct iio_dev {
[...]
int modes;
int currentmode;
struct device dev;
struct iio_buffer *buffer;
int scan_bytes;
const unsigned long *available_scan_masks;
const unsigned long *active_scan_mask;
bool scan_timestamp;
struct iio_trigger *trig;
struct iio_poll_func *pollfunc;
struct iio_chan_spec const *channels;
int num_channels;
const char *name;
const struct iio_info *info;
const struct iio_buffer_setup_ops *setup_ops;
struct cdev chrdev;

};
復制代碼
完整的結構在IIO頭文件中定義。 我們將不感興趣的字段在此處刪除。

   modes: 這表示設備支持的不同模式。 支持的模式有:

     INDIO_DIRECT_MODE表示設備提供的sysfs接口。

     INDIO_BUFFER_TRIGGERED表示設備支持硬件觸發器。使用iio_triggered_buffer_setup()函數設置觸發緩沖區時,此模式會自動添加到設備中。

    INDIO_BUFFER_HARDWARE表示設備具有硬件緩沖區。

    INDIO_ALL_BUFFER_MODES是上述兩者的聯合。

  l currentmode: 這表示設備實際使用的模式。

  l dev: 這表示IIO設備所依賴的struct設備(根據Linux設備型號)。

  l buffer: 這是您的數據緩沖區,在使用觸發緩沖區模式時會推送到用戶空間。 使用iio_triggered_buffer_setup函數啟用觸發緩沖區支持時,它會自動分配并與您的設備關聯。

  l scan_bytes: 這是捕獲并饋送到緩沖區的字節數。 當從用戶空間使用觸發緩沖區時,緩沖區應至少為indio-> scan_bytes字節大。

  l available_scan_masks: 這是允許的位掩碼的可選數組。 使用觸發緩沖器時,可以啟用通道捕獲并將其饋入IIO緩沖區。 如果您不想允許某些通道啟用,則應僅使用允許的通道填充此數組。 以下是為加速度計(帶有X,Y和Z通道)提供掃描掩碼的示例:

復制代碼
/*

  • Bitmasks 0x7 (0b111) and 0 (0b000) are allowed.
  • It means one can enable none or all of them.
  • one can't for example enable only channel X and Y
    */

static const unsigned long my_scan_masks[] = {0x7, 0};
indio_dev->available_scan_masks = my_scan_masks;
復制代碼
l active_scan_mask: 這是啟用通道的位掩碼。 只有來自這些通道的數據能被推入緩沖區。 例如,對于8通道ADC轉換器,如果只啟用第一個(0),第三個(2)和最后一個(7)通道,則位掩碼將為0b10000101(0x85)。 active_scan_mask將設置為0x85。 然后,驅動程序可以使用for_each_set_bit宏遍歷每個設置位,根據通道獲取數據,并填充緩沖區。

l scan_timestamp: 這告訴我們是否將捕獲時間戳推入緩沖區。 如果為true,則將時間戳作為緩沖區的最后一個元素。 時間戳大8字節(64位)。

l trig: 這是當前設備觸發器(支持緩沖模式時)。

l pollfunc:這是在接收的觸發器上運行的函數。

l channels: 這表示通道規范結構,用于描述設備具有的每個通道。

l num_channels: 這表示通道中指定的通道數。

l name: 這表示設備名稱。

l info: 來自驅動程序的回調和持續信息。

l setup_ops: 啟用/禁用緩沖區之前和之后調用的回調函數集。 這個結構在include / linux / iio / iio.h中定義,如下所示:

復制代碼
struct iio_buffer_setup_ops {

int (* preenable) (struct iio_dev *);
int (* postenable) (struct iio_dev *);
int (* predisable) (struct iio_dev *);
int (* postdisable) (struct iio_dev *);
bool (* validate_scan_mask) (struct iio_dev *indio_dev,const unsigned long *scan_mask);

};
復制代碼
l setup_ops: 如果未指定,則IIO內核使用drivers / iio / buffer / industrialio-triggered-buffer.c中定義的缺省iio_triggered_buffer_setup_ops。

l chrdev: 這是由IIO核心創建的關聯字符設備。

用于為IIO設備分配內存的函數是iio_device_alloc():

struct iio_dev * iio_device_alloc(int sizeof_priv)
///struct iio_dev devm_iio_device_alloc(struct device dev, int sizeof_priv)
/ Resource-managed iio_device_alloc()/
/*Managed iio_device_alloc. iio_dev allocated with this function is automatically freed on driver detach.
If an iio_dev allocated with this function needs to be freed separately, devm_iio_device_free() must be used. */
  dev是為其分配iio_dev的設備,sizeof_priv是用于為任何私有結構分配的內存空間。 這樣,傳遞每個設備(私有)數據結構非常簡單。 如果分配失敗,該函數返回NULL:

復制代碼
struct iio_dev *indio_dev;
struct my_private_data *data;
indio_dev = iio_device_alloc(sizeof(*data));
if (!indio_dev)

      return -ENOMEM;

/data is given the address of reserved momory for private data /
data = iio_priv(indio_dev);
復制代碼

  在分配IIO設備存儲器之后,下一步是填充不同的字段。 完成后,必須使用iio_device_register函數向IIO子系統注冊設備:

int iio_device_register(struct iio_dev *indio_dev)

   //devm_iio_device_register(dev, indio_dev)

/ Resource-managed iio_device_register() /

  在執行此功能后,設備將準備好接受來自用戶空間的請求。 反向操作(通常在釋放函數中完成)是iio_device_unregister():

void iio_device_unregister(struct iio_dev *indio_dev)
// void devm_iio_device_unregister(struct device dev, struct iio_dev indio_dev)
  一旦取消注冊,iio_device_alloc分配的內存可以用iio_device_free釋放:

void iio_device_free(struct iio_dev *iio_dev)
// void devm_iio_device_free(struct device dev, struct iio_dev iio_dev)
  給定IIO設備作為參數,可以通過以下方式檢索私有數據:

 

 struct my_private_data *the_data = iio_priv(indio_dev);

iio_info structure:iio_info結構體

  struct iio_info結構用于聲明IIO內核使用的鉤子,以讀取/寫入通道/屬性值:

復制代碼
struct iio_info {

       struct module *driver_module;const struct attribute_group *attrs;int (*read_raw)(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int *val, int *val2, long mask);int (*write_raw)(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int val, int val2, long mask);[...]

};
復制代碼
l driver_module: 這是用于確保chrdev正確擁有的模塊結構,通常設置為THIS_MODULE。

l attrs: 這表示設備屬性。

l read_raw: 這是用戶讀取設備sysfs文件屬性時的回調運行。 mask參數是一個位掩碼,它允許我們知道請求了哪種類型的值。 channel參數讓我們知道相關的通道。 它可以是采樣頻率,用于將原始值轉換為可用值的比例,或原始值本身。

l write_raw: 這是用于將值寫入設備的回調。 例如,可以使用它來設置采樣頻率。

  以下代碼顯示了如何設置struct iio_info結構:

復制代碼
static const struct iio_info iio_dummy_info = {

.driver_module = THIS_MODULE,
.read_raw = &iio_dummy_read_raw,
.write_raw = &iio_dummy_write_raw,

[...]

/*

  • Provide device type specific interface functions and
  • constant data. 提供設備類型特定的接口功能和常量數據。
    */

indio_dev->info = &iio_dummy_info;
復制代碼

IIO channels:IIO通道

通道代表單條采集線。 例如加速度計具有3個通道(X,Y,Z),因為每個軸代表單個采集線。 struct iio_chan_spec是表示和描述內核中單個通道的結構:

復制代碼
struct iio_chan_spec {

    enum iio_chan_type type;int channel;int channel2;unsigned long address;int scan_index;struct {charsign;u8 realbits;u8 storagebits;u8 shift;u8 repeat;enum iio_endian endianness;} scan_type;long info_mask_separate;long info_mask_shared_by_type;long info_mask_shared_by_dir;long info_mask_shared_by_all;const struct iio_event_spec *event_spec;unsigned int num_event_specs;const struct iio_chan_spec_ext_info *ext_info;const char *extend_name;const char *datasheet_name;unsigned modified:1;unsigned indexed:1;unsigned output:1;unsigned differential:1;};

復制代碼
  各個參數意義:

l type: 這指定了通道的測量類型。 在電壓測量的情況下,它應該是IIO_VOLTAGE。 對于光傳感器,它是IIO_LIGHT。 對于加速度計,使用IIO_ACCEL。 所有可用類型都在include / uapi / linux / iio / types.h中定義,如enum iio_chan_type。 要為給定轉換器編寫驅動程序,請查看該文件以查看每個通道所屬的類型。

l channel: 這指定.indexed設置為1時的通道索引。

l channel2: 這指定.modified設置為1時的通道修飾。

l modified: 這指定是否將修飾符應用于此通道屬性名稱。 在這種情況下,修飾符設置在.channel2中。 (例如,IIO_MOD_X,IIO_MOD_Y,IIO_MOD_Z是關于xyz軸的軸向傳感器的修改器)。 可用修飾符列表在內核IIO頭中定義為枚舉iio_modifier。 修飾符只會破壞sysfs中的通道屬性名稱,而不是值。

l indexed: 這指定通道屬性名稱是否具有索引。 如果是,則在.channel字段中指定索引。

l scan_index and scan_type: 當使用緩沖區觸發器時,這些字段用于標識緩沖區中的元素。 scan_index設置緩沖區內捕獲的通道的位置。 具有較低scan_index的通道將放置在具有較高索引的通道之前。 將.scan_index設置為-1將阻止通道進行緩沖捕獲(scan_elements目錄中沒有條目)。

  暴露給用戶空間的通道sysfs屬性以位掩碼的形式指定。 根據共享信息,可以將屬性設置為以下掩碼之一:

l info_mask_separate 將屬性標記為特定于此通

l info_mask_shared_by_type 將該屬性標記為由相同類型的所有通道共享。 導出的信息由相同類型的所有通道共享。

l info_mask_shared_by_dir 將屬性標記為由同一方向的所有通道共享。 導出的信息由同一方向的所有通道共享。

l info_mask_shared_by_all 將屬性標記為所有通道共享,無論其類型或方向如何。 導出的信息由所有渠道共享。 用于枚舉這些屬性的位掩碼都在include / linux / iio / iio.h中定義:

復制代碼
enum iio_chan_info_enum {

IIO_CHAN_INFO_RAW = 0,
IIO_CHAN_INFO_PROCESSED,
IIO_CHAN_INFO_SCALE,
IIO_CHAN_INFO_OFFSET,
IIO_CHAN_INFO_CALIBSCALE,
[...]
IIO_CHAN_INFO_SAMP_FREQ,
IIO_CHAN_INFO_FREQUENCY,
IIO_CHAN_INFO_PHASE,
IIO_CHAN_INFO_HARDWAREGAIN,
IIO_CHAN_INFO_HYSTERESIS,
[...]

};
復制代碼
字節序字段應為以下之一:

enum iio_endian {

                     IIO_CPU,IIO_BE,IIO_LE,

};

Channel attribute naming conventions:通道屬性命名約定

  屬性的名稱由IIO核心自動生成,具有以下模式:{direction} {type} {index} {modifier} {info_mask}:

  l direction方向對應于屬性方向,根據drivers / iio / industrialio-core.c中的struct iio_direction結構:

static const char * const iio_direction[] = {
[0] = "in",
[1] = "out",
};
  l type對應于通道類型,根據char數組const iio_chan_type_name_spec:

復制代碼
static const char * const iio_chan_type_name_spec[] = {
[IIO_VOLTAGE] = "voltage",
[IIO_CURRENT] = "current",
[IIO_POWER] = "power",
[IIO_ACCEL] = "accel",
[...]
[IIO_UVINDEX] = "uvindex",
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
[IIO_COUNT] = "count",
[IIO_INDEX] = "index",
[IIO_GRAVITY] = "gravity",
};
復制代碼
l index 索引模式取決于是否設置了通道.indexed字段。 如果設置,索引將從.channel字段中獲取,以替換{index}模式。

l modifier 模式取決于通道所設置的.modified字段。 如果設置,修飾符將從.channel2字段中獲取,{modifier}模式將根據char數組struct iio_modifier_names結構替換:

復制代碼
static const char * const iio_modifier_names[] = {
[IIO_MOD_X] = "x",
[IIO_MOD_Y] = "y",
[IIO_MOD_Z] = "z",
[IIO_MOD_X_AND_Y] = "x&y",
[IIO_MOD_X_AND_Z] = "x&z",
[IIO_MOD_Y_AND_Z] = "y&z",
[...]
[IIO_MOD_CO2] = "co2",
[IIO_MOD_VOC] = "voc",
};
復制代碼
l info_mask取決于char數組iio_chan_info_postfix中的通道信息掩碼,私有或共享索引值:

復制代碼
/ relies on pairs of these shared then separate依賴于這些共享的對,然后分離/
static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_RAW] = "raw",
[IIO_CHAN_INFO_PROCESSED] = "input",
[IIO_CHAN_INFO_SCALE] = "scale",
[IIO_CHAN_INFO_CALIBBIAS] = "calibbias",
[...]
[IIO_CHAN_INFO_SAMP_FREQ] = "sampling_frequency",
[IIO_CHAN_INFO_FREQUENCY] = "frequency",
[...]
};
復制代碼

Distinguishing channels通道區分

當每種通道類型有多個數據通道時,您可能會遇到麻煩。 困境將是:如何識別它們。 有兩種解決方案:索引和修飾符。

使用索引:給定具有一個通道線的ADC器件,不需要索引。通道定義如下:

復制代碼
static const struct iio_chan_spec adc_channels[] = {

            {.type = IIO_VOLTAGE,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),},

}
復制代碼
由前面描述的通道產生的屬性名稱將是in_voltage_raw。

/sys/bus/iio/iio:deviceX/in_voltage_raw
現在讓我們看一下有4個甚至8個通道的轉換器。 我們如何識別它們? 解決方案是使用索引。 將.indexed字段設置為1將使用.channel值替換{index}模式來替換通道屬性名稱:

復制代碼
static const struct iio_chan_spec adc_channels[] = {

    {.type = IIO_VOLTAGE,.indexed = 1,.channel = 0,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),},{.type = IIO_VOLTAGE,.indexed = 1,.channel = 1,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),},{.type = IIO_VOLTAGE,.indexed = 1,.channel = 2,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),},{.type = IIO_VOLTAGE,.indexed = 1,.channel = 3,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),},

}
復制代碼
生成的通道屬性為:

/sys/bus/iio/iio:deviceX/in_voltage0_raw
/sys/bus/iio/iio:deviceX/in_voltage1_raw
/sys/bus/iio/iio:deviceX/in_voltage2_raw
/sys/bus/iio/iio:deviceX/in_voltage3_raw
  使用修飾符:給定一個帶有兩個通道的光傳感器 - 一個用于紅外光,一個用于紅外和可見光,沒有索引或修改器,屬性名稱將為in_intensity_raw。 在這里使用索引可能容易出錯,因為使用in_intensity0_ir_raw和in_intensity1_ir_raw是沒有意義的。 使用修飾符將有助于提供有意義的屬性名稱。 通道的定義如下:

復制代碼
static const struct iio_chan_spec mylight_channels[] = {

    {.type = IIO_INTENSITY,.modified = 1,.channel2 = IIO_MOD_LIGHT_IR,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),.info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),},{.type = IIO_INTENSITY,.modified = 1,.channel2 = IIO_MOD_LIGHT_BOTH,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),.info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ),},{.type = IIO_LIGHT,.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),.info_mask_shared =BIT(IIO_CHAN_INFO_SAMP_FREQ),},}

復制代碼
屬性結果:

l /sys/bus/iio/iio:deviceX/in_intensity_ir_raw 用于測量IR強度的通道

l /sys/bus/iio/iio:deviceX/in_intensity_both_raw用于測量紅外和可見光的通道

l /sys/bus/iio/iio:deviceX/in_illuminance_input用于處理后的數據

l /sys/bus/iio/iio:deviceX/sampling_frequency 用于采樣頻率,由所有人共享

  這也適用于加速度計,我們將在案例研究中進一步了解。 現在,讓我們總結一下我們到目前為止在虛擬IIO驅動程序中討論過的內容。

Putting it all together總結

  讓我們總結一下迄今為止我們在一個簡單的虛擬驅動器中看到的內容,它將暴露出四個電壓通道。 我們將忽略read()或write()函數:

復制代碼

include

include

include

include

include

include

include

include

include

include

define FAKE_VOLTAGE_CHANNEL(num) \

{ \

     .type = IIO_VOLTAGE,                      \.indexed = 1,                             \.channel = (num),                         \.address = (num),                         \.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \.info_mask_shared_by_type =BIT(IIO_CHAN_INFO_SCALE) \

}

struct my_private_data {

int foo;
int bar;
struct mutex lock;

};

static int fake_read_raw(struct iio_dev *indio_dev,

               struct iio_chan_spec const *channel, int *val,int *val2, long mask)

{

return 0;

}

static int fake_write_raw(struct iio_dev *indio_dev,

               struct iio_chan_spec const *chan,int val, int val2, long mask)

{

return 0;

}

static const struct iio_chan_spec fake_channels[] = {
FAKE_VOLTAGE_CHANNEL(0),
FAKE_VOLTAGE_CHANNEL(1),
FAKE_VOLTAGE_CHANNEL(2),
FAKE_VOLTAGE_CHANNEL(3),

};

static const struct of_device_id iio_dummy_ids[] = {

{ .compatible = "packt,iio-dummy-random", },
{ /* sentinel */ }

};

static const struct iio_info fake_iio_info = {
.read_raw = fake_read_raw,
.write_raw = fake_write_raw,
.driver_module = THIS_MODULE,
};

static int my_pdrv_probe (struct platform_device *pdev)
{

struct iio_dev *indio_dev;
struct my_private_data *data;

indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));

if (!indio_dev) {

     dev_err(&pdev->dev, "iio allocation failed!\n");return -ENOMEM;

}

data = iio_priv(indio_dev);
mutex_init(&data->lock);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &fake_iio_info;
indio_dev->name = KBUILD_MODNAME;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = fake_channels;
indio_dev->num_channels = ARRAY_SIZE(fake_channels);
indio_dev->available_scan_masks = 0xF;

iio_device_register(indio_dev);
platform_set_drvdata(pdev, indio_dev);
return 0;

}
static void my_pdrv_remove(struct platform_device *pdev)
{

struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);

}
static struct platform_driver mypdrv = {

.probe      = my_pdrv_probe,
.remove     = my_pdrv_remove,
.driver     = {.name     = "iio-dummy-random",.of_match_table = of_match_ptr(iio_dummy_ids),  .owner    = THIS_MODULE,
},

};
module_platform_driver(mypdrv);
MODULE_AUTHOR("John Madieu john.madieu@gmail.com");
MODULE_LICENSE("GPL");
復制代碼
  加載上述模塊后, 我們將有以下輸出, 顯示我們的設備確實對應于我們已注冊的平臺設備:

~# ls -l /sys/bus/iio/devices/
lrwxrwxrwx 1 root root 0 Jul 31 20:26 iio:device0 -> ../../../devices/platform/iio-dummy-random.0/iio:device0
lrwxrwxrwx 1 root root 0 Jul 31 20:23 iio_sysfs_trigger -> ../../../devices/iio_sysfs_trigger
  下面的列表顯示了此設備的通道及其名稱, 這些通道與我們在驅動程序中描述的內容完全對應:

~# ls /sys/bus/iio/devices/iio:device0/
dev in_voltage2_raw name uevent
in_voltage0_raw in_voltage3_raw power
in_voltage1_raw in_voltage_scale subsystem
~# cat /sys/bus/iio/devices/iio:device0/name
iio_dummy_random
原文地址https://www.cnblogs.com/yongleili717/p/10744252.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275288.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275288.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275288.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

瀏覽器中的 ESM

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信 ruochuan12早期的web應用非常簡單,可以直接加載js的形式去實現。隨著需求的越來越多,應用越做越大,需要模塊化去管理項目中的js、css、圖片等資源。這里…

理解面向連接和無連接協議之間的區別

理解面向連接和無連接協議之間的區別 網絡編程中最基本的概念就是面向連接(connection-oriented)和無連接(connectionless)協議。 面向連接和無連接指的都是協議。也就是說,這些術語指的并不是無理介質本身&#xff0c…

標記圖標_標記您的圖標

標記圖標Not labeling your icons is the same as assuming that we are all fluent in ancient hieroglyphics. Are you? Can you just walk up to Cleopatras needle and read it like you could read a childrens book? Even emojis, our modern hieroglyphics dont mean …

找出無序數組中最小的k個數(top k問題)

2019獨角獸企業重金招聘Python工程師標準>>> 給定一個無序的整型數組arr,找到其中最小的k個數 該題是互聯網面試中十分高頻的一道題,如果用普通的排序算法,排序之后自然可以得到最小的k個數,但時間復雜度高達O(NlogN)&…

你應該知道的 Node 基礎知識

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信 ruochuan12 參與,已進行兩個多月,大家一起交流學習,共同進步。源碼共讀學的多數是 Node.js ,今天分享一篇 Node.js 基礎知識的文章。一. N…

C# 中數據緩存總結

在C#嘗試了5種方法進行數據緩存,具體如下:(如有遺漏,錯誤歡迎大家指正,歡迎提建議。)1:Session方法:此方法是針對于每個用戶來的,如果用戶量比較大,那么建議不要采用此方法&#xff…

react 引入 mobx @babel/core: 7.2.2

為什么80%的碼農都做不了架構師?>>> yarn add babel/plugin-proposal-class-propertiesyarn add babel/plugin-proposal-decorators"babel": {"plugins": [["babel/plugin-proposal-decorators", {"legacy": …

面試官問:怎么自動檢測你使用的組件庫有更新

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信 ruochuan12本文來自V同學投稿的源碼共讀第六期筆記,寫得很有趣。現在已經進行到第十期了。你或許經常看見 npm 更新的提示。npm 更新提示面試官可能也會問你,組件庫…

設計模式完整備忘錄

小言:這不是設計模式講解型博文,以下將設計模式的概述、類圖,代碼示例,總結分每篇博文單獨展示,現將其歸類,便于以后翻閱,設計模式也不是一兩個月學完了就能完全領悟,它只告訴我們幾…

使用Microsoft Web Application Stress Tool對web進行壓力測試

你的Web服務器和應用到底能夠支持多少并發用戶訪問?在出現大量并發請求的情況下,軟件會出現問題嗎?這些問題靠通常的測試手段是無法解答的。本文介紹 了Microsoft為這個目的而提供的免費工具WAS及其用法。另外,本文介紹了一種Web應…

2021前端高頻面試題整理,附答案

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信 ruochuan12若川視野原意是若川的前端視野。但太長了就留下了四個字,不知道的以為關注的不是技術公眾號。今天分享一篇慕課網精英講師河畔一角的好文章~廢話不多說,…

OO第二單元作業小結

總結性博客作業 第一次作業 (1)從多線程的協同和同步控制方面,分析和總結自己三次作業的設計策略。 第一次作業為單電梯傻瓜調度,可以采用生產者——消費者模型,是一個有一個生產者(標準輸入電梯請求),一個…

dribbble加速vpn_關于Dribbble設計的幾點思考

dribbble加速vpn重點 (Top highlight)I’d like to start with the following quote from Paul Adam’s “The Dribbbilisation of Design,” a powerful read that examines the superficiality of modern product design portfolios, often containing Dribbble posts that l…

JS Compress and Decompress

<html><head><title>JavaScript字符串之壓縮與還原</title><meta http-equiv"Content-Type"content"text/html; charsetutf-8"/><script type"text/javascript"><!--/** * 壓縮 */functionCompress(strN…

尤雨溪推薦神器 ni ,能替代 npm/yarn/pnpm ?簡單好用!源碼揭秘!

1. 前言大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12想學源碼&#xff0c;極力推薦之前我寫的《學習源碼整體架構系列》jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4、koa-compose、…

如何了解自己的認知偏差_了解吸引力偏差

如何了解自己的認知偏差Let me introduce you the attractiveness bias theory known as cognitive bias.讓我向您介紹稱為認知偏差的吸引力偏差理論。 Think about a person with outstanding fashion. It will draw our attention, and maybe encourage us to interact with…

隱馬爾可夫模型(HMM)及Viterbi算法

HMM簡介 對于算法愛好者來說&#xff0c;隱馬爾可夫模型的大名那是如雷貫耳。那么&#xff0c;這個模型到底長什么樣&#xff1f;具體的原理又是什么呢&#xff1f;有什么具體的應用場景呢&#xff1f;本文將會解答這些疑惑。  本文將通過具體形象的例子來引入該模型&#xf…

尤大直播分享:vue3生態進展和展望

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12前言10月23日&#xff0c;參加了前端早早聊組織的【vue生態專場】&#xff0c;準備寫一波分享方便大家學習。早上有4個話題&#xff1a;volar開發&#xff0c;搭建平臺組件開發…

利用Python查看微信共同好友

思路 首先通過itchat這個微信個人號接口掃碼登錄個人微信網頁版&#xff0c;獲取可以識別好友身份的數據。這里是需要分別登錄兩人微信的&#xff0c;拿到兩人各自的好友信息存到列表中。 這樣一來&#xff0c;查共同好友就轉化成了查兩個列表中相同元素的問題。獲取到共同好友…

女生適合學ux嗎_UX設計色彩心理學,理論與可訪問性

女生適合學ux嗎Colour is an interesting topic, which I feel is often overlooked and sometimes under-appreciated. One of the first things I was taught was the power of colour, how it can have an impact on human emotion, and that there should be purpose behin…