Linux系统构建完成后,就可以基于该环境方便地进行开发了,相关的开发流程与MCU类似,但是引入了设备树的概念,编写应用代码要相对复杂一点。但是省去了很多配置工作。

学习视频地址:【正点原子】STM32MP157开发板

字符设备驱动开发

驱动流程

在 Linux 中一切皆为文件,驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过对这个名为“/dev/xxx”(xxx 是具体的驱动文件名字)的文件进行相应的操作即可实现对硬件的操作。
比如现在有个叫做/dev/led 的驱动文件,此文件是 led 灯的驱动文件。

  1. open 函数来打开文件/dev/led,使用完成以后使用 close 函数关闭/dev/led 这个文件。 open和 close 就是打开和关闭 led 驱动的函数
  2. 点亮或关闭 led,那么就使用 write 函数来操作,也就是向此驱动写入数据,这个数据就是要关闭还是要打开 led 的控制参数
  3. 如果要获取led 灯的状态,就用 read 函数从驱动中读取相应的状态。

内核驱动函数及变量
owner:拥有该结构体的模块的指针变量, 一般设置为 THIS_MODULE
llseek:修改文件当前的读写位置
read:读取设备文件
write:向设备文件写入数据
poll:轮询函数,用于查询设备是否可以进行非阻塞的读写。
unlocked_ioctl:提供对于设备的控制功能,与应用程序中的 ioctl 函数对应
compat_ioctl:函数与 unlocked_ioctl 函数功能一样,区别在于在 64 位系统上,
32 位的应用程序调用将会使用此函数。在 32 位的系统上运行 32 位的应用程序调用的是unlocked_ioctl。
mmap:用于将将设备的内存映射到进程空间中(也就是用户空间),一般帧
缓冲设备会使用此函数,比如 LCD 驱动的显存,将帧缓冲(LCD 显存)映射到用户空间中以后应用程序就可以直接操作显存了,这样就不用在用户空间和内核空间之间来回复制。
open:打开设备文件
release:用于释放(关闭)设备文件,与应用程序中的 close 函数对应
fasync:用于刷新待处理的数据,用于将缓冲区中的数据刷新到磁盘中

驱动开发步骤

1. 驱动模块的加载和卸载

  1. 将驱动编译进Linux内核中,内核启动时就会自动运行驱动程序
  2. 将驱动编译为模块(Linux 下模块扩展名为.ko),Linux内核启动后采用modprobe指令加载模块。
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数

2. 字符设备的注册与注销

字符设备的注册与注销放在模块的加载和卸载函数中。

// 只指定主设备号,会造成资源浪费
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major,const char *name)// 用多少申请多少,比较合理
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
int register_chrdev_region(dev_t from, unsigned count, const char *name)
void unregister_chrdev_region(dev_t from, unsigned count)

3. 类和设备的创建

这部分是为了设备可以自动创建和删除文件节点

// 类的创建和删除
struct class *class_create (struct module *owner, const char *name)
void class_destroy(struct class *cls)// 设备的创建和删除
struct device *device_create(struct class *cls,struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
void device_destroy(struct class *cls, dev_t devt)

4. 实例代码

字符设备驱动

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>#define CHRDEVBASE_MAJOR 100             /* 主设备号 */
#define CHRDEVBASE_NAME     "devbase"         /* 设备名   */static char readbuf[100];        /* 读缓冲区 */
static char writebuf[100];      /* 写缓冲区 */
static char kerneldata[] = {"kernel data!"};/** @description        : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int chrdevbase_open(struct inode *inode, struct file *filp)
{printk("devbase open! \r\n");return 0;
}/** @description      : 从设备读取数据 * @param - filp  : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int retvalue = 0;/* 向用户空间发送数据 */memcpy(readbuf, kerneldata, sizeof(kerneldata));retvalue = copy_to_user(buf, readbuf, cnt);if(retvalue == 0){printk("kernel senddata ok!\r\n");}else{printk("kernel senddata failed!\r\n");}return 0;
}/** @description      : 向设备写数据 * @param - filp   : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue = 0;/* 接收用户空间传递给内核的数据并且打印出来 */retvalue = copy_from_user(writebuf, buf, cnt);if(retvalue == 0){printk("kernel recevdata:%s\r\n", writebuf);}else{printk("kernel recevdata failed!\r\n");}return 0;
}/** @description      : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/
static int chrdevbase_release(struct inode *inode, struct file *filp)
{printk("devbase release! \r\n");return 0;
}/** 设备操作函数结构体*/
static struct file_operations devbase_fops = {.owner = THIS_MODULE,   .open = chrdevbase_open,.read = chrdevbase_read,.write = chrdevbase_write,.release = chrdevbase_release,
};/** @description : 驱动入口函数 * @param      : 无* @return       : 0 成功;其他 失败*/
static int __init chrdevbase_init(void)
{int retvalue = 0;/* 注册字符设备驱动 */retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &devbase_fops);if(retvalue < 0){printk("chrdevbase driver register failed\r\n");}printk("chrdevbase init!\r\n");return 0;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit chrdevbase_exit(void)
{/* 注销字符设备驱动 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase exit!\r\n");
}/* * 将上面两个函数指定为驱动的入口和出口函数 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);/* * LICENSE和作者信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JozenLee");

新字符驱动代码(以LED为例)

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define NEWCHRLED_CNT          1           /* 设备号个数 */
#define NEWCHRLED_NAME          "newchrled"   /* 名字 */
#define LEDOFF                  0           /* 关灯 */
#define LEDON                   1           /* 开灯 *//* 寄存器物理地址 */
#define PERIPH_BASE                     (0x40000000)
#define MPU_AHB4_PERIPH_BASE            (PERIPH_BASE + 0x10000000)
#define RCC_BASE                        (MPU_AHB4_PERIPH_BASE + 0x0000)
#define RCC_MP_AHB4ENSETR               (RCC_BASE + 0XA28)
#define GPIOI_BASE                      (MPU_AHB4_PERIPH_BASE + 0xA000)
#define GPIOI_MODER                     (GPIOI_BASE + 0x0000)
#define GPIOI_OTYPER                    (GPIOI_BASE + 0x0004)
#define GPIOI_OSPEEDR                   (GPIOI_BASE + 0x0008)
#define GPIOI_PUPDR                     (GPIOI_BASE + 0x000C)
#define GPIOI_BSRR                      (GPIOI_BASE + 0x0018)/* 映射后的寄存器虚拟地址指针 */
static void __iomem *MPU_AHB4_PERIPH_RCC_PI;
static void __iomem *GPIOI_MODER_PI;
static void __iomem *GPIOI_OTYPER_PI;
static void __iomem *GPIOI_OSPEEDR_PI;
static void __iomem *GPIOI_PUPDR_PI;
static void __iomem *GPIOI_BSRR_PI;/* newchrled设备结构体 */
struct newchrled_dev{dev_t devid;           /* 设备号   */struct cdev cdev;        /* cdev     */struct class *class;      /* 类        */struct device *device;    /* 设备    */int major;               /* 主设备号   */int minor;              /* 次设备号   */
};struct newchrled_dev newchrled;   /* led设备 *//** @description        : LED打开/关闭* @param - sta   : LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return           : 无*/
void led_switch(u8 sta)
{u32 val = 0;if(sta == LEDON) {val = readl(GPIOI_BSRR_PI);val |= (1 << 16);  writel(val, GPIOI_BSRR_PI);}else if(sta == LEDOFF) {val = readl(GPIOI_BSRR_PI);val|= (1 << 0);    writel(val, GPIOI_BSRR_PI);}
}/** @description      : 取消映射* @return            : 无*/
void led_unmap(void)
{/* 取消映射 */iounmap(MPU_AHB4_PERIPH_RCC_PI);iounmap(GPIOI_MODER_PI);iounmap(GPIOI_OTYPER_PI);iounmap(GPIOI_OSPEEDR_PI);iounmap(GPIOI_PUPDR_PI);iounmap(GPIOI_BSRR_PI);
}/** @description      : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &newchrled; /* 设置私有数据 */return 0;
}/** @description      : 从设备读取数据 * @param - filp  : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** @description      : 向设备写数据 * @param - filp   : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];     /* 获取状态值 */if(ledstat == LEDON) { led_switch(LEDON);      /* 打开LED灯 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}return 0;
}/** @description      : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/
static int led_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations newchrled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release =  led_release,
};/** @description : 驱动出口函数* @param       : 无* @return       : 无*/
static int __init led_init(void)
{u32 val = 0;int ret;/* 初始化LED *//* 1、寄存器地址映射 */MPU_AHB4_PERIPH_RCC_PI = ioremap(RCC_MP_AHB4ENSETR, 4);GPIOI_MODER_PI = ioremap(GPIOI_MODER, 4);GPIOI_OTYPER_PI = ioremap(GPIOI_OTYPER, 4);GPIOI_OSPEEDR_PI = ioremap(GPIOI_OSPEEDR, 4);GPIOI_PUPDR_PI = ioremap(GPIOI_PUPDR, 4);GPIOI_BSRR_PI = ioremap(GPIOI_BSRR, 4);/* 2、使能PI时钟 */val = readl(MPU_AHB4_PERIPH_RCC_PI);val &= ~(0X1 << 8); /* 清除以前的设置 */val |= (0X1 << 8);  /* 设置新值 */writel(val, MPU_AHB4_PERIPH_RCC_PI);/* 3、设置PI0通用的输出模式。*/val = readl(GPIOI_MODER_PI);val &= ~(0X3 << 0); /* bit0:1清零 */val |= (0X1 << 0);  /* bit0:1设置01 */writel(val, GPIOI_MODER_PI);/* 3、设置PI0为推挽模式。*/val = readl(GPIOI_OTYPER_PI);val &= ~(0X1 << 0); /* bit0清零,设置为上拉*/writel(val, GPIOI_OTYPER_PI);/* 4、设置PI0为高速。*/val = readl(GPIOI_OSPEEDR_PI);val &= ~(0X3 << 0); /* bit0:1 清零 */val |= (0x2 << 0); /* bit0:1 设置为10*/writel(val, GPIOI_OSPEEDR_PI);/* 5、设置PI0为上拉。*/val = readl(GPIOI_PUPDR_PI);val &= ~(0X3 << 0); /* bit0:1 清零*/val |= (0x1 << 0); /*bit0:1 设置为01*/writel(val,GPIOI_PUPDR_PI);/* 6、默认关闭LED */val = readl(GPIOI_BSRR_PI);val |= (0x1 << 0);writel(val, GPIOI_BSRR_PI);/* 注册字符设备驱动 *//* 1、创建设备号 */if (newchrled.major) {       /*  定义了设备号 */newchrled.devid = MKDEV(newchrled.major, 0);ret = register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);if(ret < 0) {pr_err("cannot register %s char driver [ret=%d]\n",NEWCHRLED_NAME, NEWCHRLED_CNT);goto fail_map;}} else {    /* 没有定义设备号 */ret = alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME);    /* 申请设备号 */if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", NEWCHRLED_NAME, ret);goto fail_map;}newchrled.major = MAJOR(newchrled.devid); /* 获取分配号的主设备号 */newchrled.minor = MINOR(newchrled.devid);  /* 获取分配号的次设备号 */}printk("newcheled major=%d,minor=%d\r\n",newchrled.major, newchrled.minor);    /* 2、初始化cdev */newchrled.cdev.owner = THIS_MODULE;cdev_init(&newchrled.cdev, &newchrled_fops);/* 3、添加一个cdev */ret = cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);if(ret < 0)goto del_unregister;/* 4、创建类 */newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);if (IS_ERR(newchrled.class)) {goto del_cdev;}/* 5、创建设备 */newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);if (IS_ERR(newchrled.device)) {goto destroy_class;}return 0;destroy_class:class_destroy(newchrled.class);
del_cdev:cdev_del(&newchrled.cdev);
del_unregister:unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);
fail_map:led_unmap();return -EIO;}/** @description : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit led_exit(void)
{/* 取消映射 */led_unmap();/* 注销字符设备驱动 */cdev_del(&newchrled.cdev);/*  删除cdev */unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */device_destroy(newchrled.class, newchrled.devid);class_destroy(newchrled.class);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

设备应用

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"/* 用户数据, 用于写入设备测试 */
static char usrdata[] = {"usr data!"};int main(int argc, char *argv[])
{int fd, retvalue;char *filename;char readbuf[100], writebuf[100];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开驱动文件 */fd  = open(filename, O_RDWR);if(fd < 0){printf("Can't open file %s\r\n", filename);return -1;}/* 从驱动文件读取数据测试 */if(atoi(argv[2]) == 1){ retvalue = read(fd, readbuf, 50);if(retvalue < 0){printf("read file %s failed!\r\n", filename);}else{printf("read data:%s\r\n",readbuf);}}/* 向设备驱动写数据测试 */if(atoi(argv[2]) == 2){memcpy(writebuf, usrdata, sizeof(usrdata));retvalue = write(fd, writebuf, 50);if(retvalue < 0){printf("write file %s failed!\r\n", filename);}}/* 关闭设备 */retvalue = close(fd);if(retvalue < 0){printf("Can't close file %s\r\n", filename);return -1;}return 0;
}

编译

将以下内容写入MakeFile

KERNELDIR := /home/zuozhongkai/linux/atk-mp1/linux/my_linux/linux-5.4.31 #Linux内核路径
CURRENT_PATH := $(shell pwd)obj-m := newchrled.o #要跟设备驱动代码文件名称一致build: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

编译驱动

make -j16

编译应用文件

arm-none-linux-gnueabihf-gcc xxx.c -o xxx #xxx为应用代码文件名

将编译所得的库文件(如led.ko)和应用文件(如ledApp)放至arm的根目录下,即可在开发板中使用。

代码运行
进入到上述文件所在的目录

depmod                       # 第一次加载模块时需运行
modprobe led                # 加载驱动模块
./ledApp /dev/newchrled 1   # 执行应用代码

pinctrl和gpio子系统

pinctrl

作用:根据所提供的信息配置PIN功能

pinctrl: pin-controller@50002000 {#address-cells = <1>;#size-cells = <1>;compatible = "st,stm32mp157-pinctrl";ranges = <0 0x50002000 0xa400>;interrupt-parent = <&exti>;st,syscfg = <&exti 0x60 0xff>;hwlocks = <&hsem 0 1>;pins-are-numbered;......
};

pinctrl下需要有子节点,子节点中有pins节点,以配置特定的PIN属性

&pinctrl {......m_can1_pins_a: m-can1-0 {pins1 {pinmux = <STM32_PINMUX('H', 13, AF9)>; /* CAN1_TX */slew-rate = <1>;drive-push-pull;bias-disable;};pins2 {pinmux = <STM32_PINMUX('I', 9, AF9)>; /* CAN1_RX */bias-disable;};};
};

pins属性

  1. pinmux 属性
pinmux = <STM32_PINMUX(port, line, mode)>;
#例
pinmux = <STM32_PINMUX('H', 13, AF9)>;

port:表示用那一组 GPIO(例:H 表示为 GPIO 第 H 组,也就是 GPIOH)
line:表示这组 GPIO 的第几个引脚(例:13 表示为 GPIOH_13,也就是 PH13)
mode:表示当前引脚要做那种复用功能(例:AF9 表示为用第 9 个复用功能)

  1. 电气属性配置

gpio子系统

作用:用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。

创建节点

led {compatible = "atk,led";gpio = <&gpioi 0 GPIO_ACTIVE_LOW>;status = "okay";
};

API函数

int gpio_request(unsigned gpio, const char *label)   // 申请GPIO管脚
void gpio_free(unsigned gpio)                       // 释放GPIO
int gpio_direction_input(unsigned gpio)             // 设置GPIO为输入
int gpio_direction_output(unsigned gpio, int value) // 设置GPIO为输出
int __gpio_get_value(unsigned gpio)                 // 获取GPIO当前值
void __gpio_set_value(unsigned gpio, int value)     // 设定GPIO当前值

of_gpio_named_count:用于获取设备树某个属性里面定义了几个 GPIO 信息

int of_gpio_named_count(struct device_node *np, const char *propname)

of_gpio_count:和 of_gpio_named_count 函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的 GPIO 信息

int of_gpio_count(struct device_node *np)

of_get_named_gpio:获取 GPIO 编号

int of_get_named_gpio(struct device_node *np,const char *propname,int index)

【嵌入式Linux学习笔记】Linux驱动开发相关推荐

  1. [Linux学习笔记] Linux历史发展与应用

    2019独角兽企业重金招聘Python工程师标准>>> UNIX发展历史: (1) 1965年,美国麻省理工学院(MIT).通用电气公司(GE)及AT&T的贝尔实验室联合开发 ...

  2. Linux学习笔记——Linux、命令 01

    操作系统的发展史 Unix 1965年之前的时候,电脑并不像现在一样普遍,它可不是一般人能碰的起的,除非是军事或者学院的研究机构,而且当时大型主机至多能提供30台终端(30个键盘.显示器),连接一台电 ...

  3. Linux学习笔记-Linux下读写文件

    在Linux编程需要读写文件时,有两种方式: (1)ANSIC: 使用stdio.h里的函数.fopen, fclose, fwrite, fread (2)Linux API:Linux提供了另外一 ...

  4. 嵌入式linux学习笔记--linux下基于imx6ullpro 的 CP2102 /CH340 驱动 以及简单的测试

    今天再次编译了linux 的内核,想起来之前一直没实现的嵌入式linux 的串口驱动,故想实验一下. 本文章会分别介绍CP2102 以及CH340两者的驱动 以及他们的简单的测试,后续可能会更新一个 ...

  5. linux内核字符驱动设备,Linux学习笔记——linux内核字符设备驱动-Go语言中文社区...

    尝试在树莓派安装的raspbian系统上进行linux字符设备驱动 1.更新安装kernel header源码 sudo apt-get update sudo apt-get install ras ...

  6. 嵌入式linux学习笔记-- linux 开机总时间记录的一种方案

    产品的一个功能就是在某一个界面上显示产品的总开机时间,之前一直担心因为频繁写一个块导致的EMMC 写坏,今天放下了这个思想的负担 重新尝试简单但是方便的,定时更新写入的逻辑,嗯 真香,真简单!!!! ...

  7. Linux学习笔记-Linux下的设备文件

    在Linux下,有一种文件的类型叫设备文件. 在/dev目录下的文件基本上都是设备文件,文件属性以c或b打头(charactor, block) 设备文件用于代表一个物理设备 例如,声卡.显卡.键盘. ...

  8. linux学习笔记 linux内核6.0.2目录结构

    一.linux内核目录 arch 包含和硬件体系结构相关的代码,每种平台占一个相应的目录,如i386.arm.arm64.powerpc.mips等.Linux内核目前已经支持30种左右的体系结构.在 ...

  9. linux学习笔记-- linux的 shell和linux C 程序 获取命令行参数和环境变量

    常用的linux指令基本都支持命令行参数 例如 : ls -l rm -r rm -f cp -r 那么 这些参数都是如何去被应用程序获取的呢? 学习记录一下 1. shell 脚本 1.1 shel ...

  10. Linux学习笔记——Linux磁盘管理

    Linux中的分区管理工具主要有:fdisk, parted, sfdisk fdisk命令 对于一块硬盘最多只能管理15个分区 命令格式:fdisk -l [-u] [device...] fdis ...

最新文章

  1. 马斯克的星际飞船又炸了!回收时发动机重启失败,「横」着砸向地面
  2. 对Mapreduce代码进行单元测试
  3. java中public、protected、default、private权限问题
  4. URAL 1009. K-based Numbers
  5. oracle体系三大文件,oracle 体系_第三章控制文件
  6. 银监会周末突发4号文件,18年贷款比想象的要难
  7. Hadoop之Shell脚本自动启动
  8. Python基础学习笔记之(一)
  9. matlab keras,基于预训练的 Keras 层组合网络
  10. Jquery学习----选择器
  11. 一本通1628X-factor Chain
  12. 完美世界-游戏Java开发工程师-一面
  13. C语言编程>第二十七周 ① 请补充fun函数,该函数的功能是:寻找两个整数之间的所有素数(包括这两个整数),把结果保存在数组a中,函数返回素数的个数。
  14. 支付宝即时到账之给二维码设置超时失效时间
  15. HOOFR: 增强型仿生特征提取器
  16. Trello中的Scrum
  17. [设计模式]单例模式的几种不同写法比较
  18. arm的FIQ模式啥时候用?有啥好处?
  19. 2022-2028全球求职网站行业调研及趋势分析报告
  20. Can‘t use Subversion command line client:svn不能使用Subversion命令行客户端:svn

热门文章

  1. datagrip切换视觉主题
  2. b站m4s文件怎么转mp4
  3. 新绝代双骄3终极全攻略3
  4. 人工智能(14)安全
  5. heidisql修改mysql密码_读取HeidiSQL 配置文件中的密码
  6. 成为zabbix合作伙伴费用及意义
  7. (JAVA实现)平衡二叉树的判断
  8. 四川2020年5G规划出炉!新建3万个5G基站,实现21市州和所有县5G覆盖
  9. ie6 兼容整理《一》
  10. Allegro 绘图参数的设置-设置图纸尺寸