ZYNQ之嵌入式驅動開發——字符設備驅動

文章目錄

  • Linux驅動程序分類
  • Linux應用程序和驅動程序的關系
  • 簡單的測試驅動程序
  • 在petalinux中添加LED驅動
  • 新字符設備驅動

Linux驅動程序分類

驅動程序分為字符設備驅動、塊設備驅動和網絡設備驅動。
字符設備是按字節訪問的設備,比如以一個字節收發數據的串口,字符設備在Linux外設中占比最大。
塊設備的特點是按一定格式存取的數據,具體的格式由文件系統決定。塊設備以存儲設備為主,存儲設備的特點是以存儲塊為基礎,因此得名塊設備。
網絡設備不同于上面兩種,應用程序和網絡設備驅動之間的通信由庫和內核提供的一套數據包傳輸函數替代了open()、read()、write()等函數。


Linux應用程序和驅動程序的關系

(1)應用程序調用庫函數提供的open()函數打開某個設備文件,該設備文件是在驅動加載成功之后在目錄/dev中生成的,是應用程序調用相應硬件的入口。
(2)庫根據open()函數的輸入參數引起CPU異常進入內核,系統調用處于內核空間,應用程序無法直接訪問,因此需要陷入到內核,方法就是軟中斷,陷入內核后還要指定系統調用號;
(3)內核的異常處理函數根據輸入參數找到相應的驅動程序,返回文件句柄給庫,庫函數再返回給應用程序;
(4)應用程序再使用得到的文件句柄調用write()、read()等函數發出控制指令;
(5)庫根據write()、read()等函數的輸入參數引起CPU異常,進入內核;
(6)內核的異常處理函數根據輸入參數調用相應的驅動程序執行相應的操作。
Linux應用程序調用驅動程序的步驟如下圖所示。
在這里插入圖片描述
應用程序中涉及到的open()、read()、write()等是由庫提供的系統調用,通過執行某條指令引發異常進入內核,是應用程序操作硬件的途徑。應用程序執行系統調用后進入內核,然后會使用驅動程序中對應的函數,驅動程序中的open()、read()、write()等函數是需要驅動開發人員實現的。應用程序運行于用戶空間,驅動程序運行于內核空間,Linux系統可以通過MMU限制應用程序運行在某個內存塊中,以避免這個應用程序出錯導致整個系統崩潰,運行于內核空間的驅動程序是系統的一部分,驅動程序出錯有可能牽連整個系統。


簡單的測試驅動程序

在進行LED驅動開發之前,先使用下面的代碼簡單測試一下。

#include <linux/module.h>static int __init chardev_init(void)
{printk("Hello!\n");return 0;
}static void __exit chardev_exit(void)
{printk("GoodBye!\n");
}module_init(chardev_init);
module_exit(chardev_exit);
MODULE_LICENSE("GPL");

如果使用Linux內核編譯上面的C文件,需要自己寫Makefile,然后編譯出驅動文件到7020開發板上驗證,驗證的結果如下圖所示。
在這里插入圖片描述
提示這個驅動是無效的模塊格式,可能ZYNQ開發板就需要使用petalinux這樣特定的工具進行開發,下面來看具體流程。
首先在petalinux的安裝路徑下設置環境變量。

source /opt/pkg/petalinux/settings.sh

進入到定制系統的根目錄下,使用下面的命令添加字符設備驅動。

petalinux-create -t modules -n chardev

在當前路徑下的/project-spec/meta-user/recipes-modules/下生成了一個名為chardev的文件夾,該文件夾下有以下三個文件,其中.c文件就是需要寫入驅動代碼的文件。
在這里插入圖片描述
Makefile在創建工程的時候已經創建好了,里面的內容如下圖所示,也不需要修改。
在這里插入圖片描述
在C文件中寫入驅動代碼后,返回到自定義的/zynq7020目錄下,使用下面的命令進行編譯。

petalinux-build -c chardev

編譯完成后的信息打印如下圖所示。
在這里插入圖片描述
由于編譯成的驅動文件存放路徑比較難找,因此直接在搜索欄中直接搜索驅動的名稱就會出現,但是文件夾必須打開到自定義的工程的這一層才能搜索到。
在這里插入圖片描述
可以右鍵該文件打開文件的具體存在位置為/opt/pkg/petalinux/zynq7020/build/tmp/sysroots-components/plnx_zynq7/chardev/lib /modules/4.14.0-xilinx-v2018.3/extra。
接下來就可以在開發板上驗證該驅動了,驗證的結果如下圖所示。
在這里插入圖片描述


在petalinux中添加LED驅動

同上面的示例,先在petalinux的安裝路徑下設置環境變量。

source /opt/pkg/petalinux/settings.sh

然后進入到定制系統的根目錄下,使用下面的命令添加驅動。

petalinux-create -t modules -n psled1-driver

需要注意的是,驅動文件命名不能使用下劃線,而要使用"-"代替。
在這里插入圖片描述
創建成功以后,打印的消息提示創建的模塊在當前路徑下的/project-spec/meta-user/recipes-modules/psled1-driver中,進到這個目錄下。在這里插入圖片描述
一步步進到最終的目錄下,在files文件夾下的.c文件就是要寫入驅動代碼的地方,在里面鍵入下面的代碼。

//該代碼來自ZYNQ教程,教程在文末給出
#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/init.h>  
#include <linux/ide.h>  
#include <linux/types.h>  /* 驅動名稱 */  
#define DEVICE_NAME       "ps_led1"  
/* 驅動主設備號 */  
#define GPIO_LED_MAJOR    200  /* gpio寄存器虛擬地址 */  
static unsigned int gpio_add_minor;  
/* gpio寄存器物理基地址 */  
#define GPIO_BASE         0xE000A000  
/* gpio寄存器所占空間大小 */  
#define GPIO_SIZE         0x1000  
/* gpio方向寄存器 */  
#define GPIO_DIRM_0       (unsigned int *)(0xE000A204 - GPIO_BASE + gpio_add_minor)  
/* gpio使能寄存器 */   
#define GPIO_OEN_0        (unsigned int *)(0xE000A208 - GPIO_BASE + gpio_add_minor)  
/* gpio控制寄存器 */  
#define GPIO_DATA_0       (unsigned int *)(0xE000A040 - GPIO_BASE + gpio_add_minor)  /* 時鐘使能寄存器虛擬地址 */  
static unsigned int clk_add_minor;  
/* 時鐘使能寄存器物理基地址 */  
#define CLK_BASE          0xF8000000  
/* 時鐘使能寄存器所占空間大小 */  
#define CLK_SIZE          0x1000  
/* AMBA外設時鐘使能寄存器 */  
#define APER_CLK_CTRL     (unsigned int *)(0xF800012C - CLK_BASE + clk_add_minor)        /* open函數實現, 對應到Linux系統調用函數的open函數 */  
static int gpio_leds_open(struct inode *inode_p, struct file *file_p)  
{  /* 把需要修改的物理地址映射到虛擬地址 */gpio_add_minor = (unsigned int)ioremap(GPIO_BASE, GPIO_SIZE);  clk_add_minor = (unsigned int)ioremap(CLK_BASE, CLK_SIZE);/* MIO_0時鐘使能 */  *APER_CLK_CTRL |= 0x00400000;  /* MIO_0設置成輸出 */  *GPIO_DIRM_0 |= 0x00000001;  /* MIO_0使能 */  *GPIO_OEN_0 |= 0x00000001;  printk("gpio_test module open\n");   return 0;  
}  /* write函數實現, 對應到Linux系統調用函數的write函數 */  
static ssize_t gpio_leds_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)  
{  int rst;  char writeBuf[5] = {0};  printk("gpio_test module write\n");  rst = copy_from_user(writeBuf, buf, len);  if(0 != rst)  {  return -1;    }  if(1 != len)  {  printk("gpio_test len err\n");  return -2;  }  if(1 == writeBuf[0])  {  *GPIO_DATA_0 &= 0xFFFFFFFE;  printk("gpio_test ON\n");  }  else if(0 == writeBuf[0])  {  *GPIO_DATA_0 |= 0x00000001;  printk("gpio_test OFF\n");  }  else  {  printk("gpio_test para err\n");  return -3;  }    return 0;  
}  /* release函數實現, 對應到Linux系統調用函數的close函數 */  
static int gpio_leds_release(struct inode *inode_p, struct file *file_p)  
{     	printk("gpio_test module release\n");  return 0;  
}  /* file_operations結構體聲明, 是上面open、write實現函數與系統調用函數對應的關鍵 */  
static struct file_operations gpio_leds_fops = {  .owner   = THIS_MODULE,  .open    = gpio_leds_open,  .write   = gpio_leds_write,     .release = gpio_leds_release,   
};  /* 模塊加載時會調用的函數 */  
static int __init gpio_led_init(void)  
{  int ret;  /* 通過模塊主設備號、名稱、模塊帶有的功能函數(及file_operations結構體)來注冊模塊 */  ret = register_chrdev(GPIO_LED_MAJOR, DEVICE_NAME, &gpio_leds_fops);  if (ret < 0)   {  printk("gpio_led_dev_init_error\n");  return ret;  }  else  {  /* 注冊成功 */ printk("gpio_led_dev_init_ok\n");  }  return 0;  
}  /* 卸載模塊 */  
static void __exit gpio_led_exit(void)  
{  /* 釋放對虛擬地址的占用 */  iounmap((unsigned int *)gpio_add_minor);  iounmap((unsigned int *)clk_add_minor); /* 注銷模塊, 釋放模塊對這個設備號和名稱的占用 */  unregister_chrdev(GPIO_LED_MAJOR, DEVICE_NAME);printk("gpio_led_dev_exit_ok\n");  
}  /* 標記加載、卸載函數 */  
module_init(gpio_led_init);  
module_exit(gpio_led_exit);  /* 驅動描述信息 */  
MODULE_AUTHOR("Alinx");  
MODULE_ALIAS("gpio_led");  
MODULE_DESCRIPTION("GPIO LED driver");  
MODULE_VERSION("v1.0");  
MODULE_LICENSE("GPL");  

上面介紹的是直接編譯驅動,也可以以圖形化的形式進行編譯,返回到自定義的/zynq7020目錄下,輸入下面的命令配置根文件系統。

petalinux-config -c rootfs

在彈出的圖形化配置窗口中選擇modules進入子菜單中。
在這里插入圖片描述
按Y鍵將該驅動包括進來,然后保存退出。
在這里插入圖片描述
根文件系統就配置成功了,然后使用petalinux-build命令編譯該工程。
在這里插入圖片描述
編譯成功后打印下面的信息。
在這里插入圖片描述
在/zynq7020目錄下搜索驅動文件,如下圖所示。
在這里插入圖片描述
右鍵該文件選擇打開文件存放位置,其存放在/zynq7020/build/tmp/sysroots-components/plnx_zynq7/psled1-driver/lib/modules/4.14.0-xilinx-v2018.3/extra,還是比較難找的,所以以后直接在工程目錄下搜索即可。
在開發板上加載驅動,可以看到相應的設備號已經出現了。
在這里插入圖片描述
使用下面的命令創建字符設備文件,指定主設備號和次設備號,設備文件名稱為psled1,之后寫應用程序的時候要使用該名稱來操作字符設備。

mknod /dev/psled1 c 200 0

創建設備文件成功之后在/dev目錄下就可以看到新添加的設備,如下圖所示。
在這里插入圖片描述
接下來寫一個應用端的測試程序,用來傳入數據點亮或者熄滅LED,程序如下。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char *argv[])
{int fd;int status;fd = open("/dev/psled1", O_RDWR);if(fd < 0){perror("open /dev/psled1 error!\n"); return fd; }status = atoi(argv[1]);write(fd, &status, 1);close(fd);  return 0;
}

將上面的程序通過交叉編譯工具編譯出適合在ARM平臺運行的文件,將其發送到開發板驗證,結果如下圖所示。
在這里插入圖片描述
如果采用下面的應用程序進行測試,LED將每隔一秒改變一下狀態。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char *argv[])
{int fd;int status;fd = open("/dev/psled1", O_RDWR);if(fd < 0){perror("open /dev/psled1 error!\n"); return fd; }while(1){status = 1;write(fd, &status, 1);sleep(1);status = 0;write(fd, &status, 1);sleep(1);}close(fd);  return 0;
}

在開發板上執行后的結果如下圖所示。
在這里插入圖片描述
開發板上PS LED1的狀態開始循環亮滅,如下動圖所示。
請添加圖片描述
終端里也是每隔一秒打印一次LED關閉或打開的狀態。請添加圖片描述
卸載驅動程序后打印下面的信息。
在這里插入圖片描述

新字符設備驅動

上面驅動代碼中將設備號寫死了,這樣做有很多不便之處,因為編譯驅動代碼前需要查看目標系統中設備號的占用情況,驅動注冊函數中僅有主設備號沒有次設備號,這意味著一個設備會占用所有的次設備號,十分浪費資源。針對這些問題,Linux內核提出了新的字符設備注冊方法,并由內核來管理設備號。
注冊字符設備號的函數原型如下。

int register_chrdev_region(dev_t from,unsigned count,const char* name);

from :需要申請的起始設備號,取代了原有的主設備號和次設備號,在需要指定主次設備號的情況下,可以通過方法from = MKDEV(major,minor); 來實現。
count :需要申請的設備號個數。
name :設備名稱。
在不需要指定主次設備號的情況下,設備號由內核來分配,傳入指針來獲取設備號,注冊、注銷設備號的函數原型如下。

int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char* name);
void unregister_chrdev_region(dev_t from,unsigned count);

dev :設備號指針,注冊成功之后,主次設備號可以通過 major = MAJOR(dev); minor = MINOR(dev); 來獲取。
baseminor :次設備號的起始地址。
新的注冊方法使用cdev結構體來定義一個字符設備,cdev結構體如下。

struct cdev
{struct kobject kobj;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;unsigned int count;
};

cdev結構體的初始化函數原型如下。

void cdev_init(struct cdev *cdev,const struct file_operations *fops);

注冊、注銷字符設備的函數原型如下。

int cdev_add(struct cdev *cdev,dev_t dev,unsigned count);
void cdev_del(struct cdev *cdev);

類的創建和刪除函數原型如下。

struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key);
void class_destroy(struct class *cls);

owner指定為THIS_MODULE,name是類的名稱,第三個參數可以省略。
設備節點的創建和刪除函數原型如下。

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void * drvdata, const char *fmt, ...);
void device_destroy(struct class *class, dev_t devt);

class是通過class_create創建的類,parent是父設備,無則填NULL,devt是設備號,drvdata是設備可能用到的數據,沒有則填NULL,fmt是設備名,創建成功后在/dev下生成。
下面代碼使用的是新字符設備方法。

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/init.h>  
#include <linux/ide.h>  
#include <linux/types.h>  
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>#define DEVICE_NAME       "psled1"
#define DEVID_COUNT       1   //設備號個數 
#define DRIVE_COUNT       1   //驅動個數/* gpio寄存器虛擬地址 */  
static unsigned int gpio_add_minor;  
/* gpio寄存器物理基地址 */  
#define GPIO_BASE         0xE000A000  
/* gpio寄存器所占空間大小 */  
#define GPIO_SIZE         0x1000  
/* gpio方向寄存器 */  
#define GPIO_DIRM_0       (unsigned int *)(0xE000A204 - GPIO_BASE + gpio_add_minor)  
/* gpio使能寄存器 */   
#define GPIO_OEN_0        (unsigned int *)(0xE000A208 - GPIO_BASE + gpio_add_minor)  
/* gpio控制寄存器 */  
#define GPIO_DATA_0       (unsigned int *)(0xE000A040 - GPIO_BASE + gpio_add_minor)  /* 時鐘使能寄存器虛擬地址 */  
static unsigned int clk_add_minor;  
/* 時鐘使能寄存器物理基地址 */  
#define CLK_BASE          0xF8000000  
/* 時鐘使能寄存器所占空間大小 */  
#define CLK_SIZE          0x1000  
/* AMBA外設時鐘使能寄存器 */  
#define APER_CLK_CTRL     (unsigned int *)(0xF800012C - CLK_BASE + clk_add_minor)        #if 0
struct chardev
{dev_t            devid;      //設備號struct cdev      cdev;       //字符設備struct class     *class;     //類struct device    *device;    //設備節點
};static struct chardev alinx_char = {.cdev = {.owner = THIS_MODULE,},
};
#endifdev_t            devid;      //設備號
struct cdev      cdev;       //字符設備
struct class     *class;     //類
struct device    *device;    //設備節點static int gpio_leds_open(struct inode *inode_p, struct file *file_p)  
{  gpio_add_minor = (unsigned int)ioremap(GPIO_BASE, GPIO_SIZE);  clk_add_minor = (unsigned int)ioremap(CLK_BASE, CLK_SIZE); /* MIO_0時鐘使能 */  *APER_CLK_CTRL |= 0x00400000;  /* MIO_0設置成輸出 */  *GPIO_DIRM_0 |= 0x00000001;  /* MIO_0使能 */  *GPIO_OEN_0 |= 0x00000001;    printk("gpio_test module open\n");   return 0;  
}  static ssize_t gpio_leds_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)  
{  int rst;  char writeBuf[5] = {0};   printk("gpio_test module write\n");  rst = copy_from_user(writeBuf, buf, len);  if(0 != rst)  {  return -1;    }  if(1 != len)  {  printk("gpio_test len err\n");  return -2;  }  if(1 == writeBuf[0])  {  *GPIO_DATA_0 &= 0xFFFFFFFE;  printk("gpio_test ON\n");  }  else if(0 == writeBuf[0])  {  *GPIO_DATA_0 |= 0x00000001;  printk("gpio_test OFF\n");  }  else  {  printk("gpio_test para err\n");  return -3;  }  return 0;  
}  static int gpio_leds_release(struct inode *inode_p, struct file *file_p)  
{  printk("gpio_test module release\n");  return 0;  
}  static struct file_operations chardev_fops = {  .owner   = THIS_MODULE,  .open    = gpio_leds_open,  .write   = gpio_leds_write,     .release = gpio_leds_release,   
};  static int __init gpio_led_init(void)  
{  #if 0alloc_chrdev_region(&alinx_char.devid, 0, DEVID_COUNT, DEVICE_NAME);  //注冊設備號cdev_init(&alinx_char.cdev, &chardev_fops);  //初始化字符設備結構體cdev_add(&alinx_char.cdev, alinx_char.devid, DRIVE_COUNT);  //注冊字符設備 alinx_char.class = class_create(THIS_MODULE, DEVICE_NAME);  //創建類if(IS_ERR(alinx_char.class)) {return PTR_ERR(alinx_char.class);}alinx_char.device = device_create(alinx_char.class, NULL, alinx_char.devid, NULL, DEVICE_NAME);  //創建設備節點printk("alloc success, major = %d minor = %d\n",MAJOR(alinx_char.devid),MINOR(alinx_char.devid));if (IS_ERR(alinx_char.device)) {return PTR_ERR(alinx_char.device);}#endifalloc_chrdev_region(&devid, 0, DEVID_COUNT, DEVICE_NAME);  //注冊設備號cdev.owner = THIS_MODULE;cdev_init(&cdev, &chardev_fops);  //初始化字符設備結構體cdev_add(&cdev, devid, DRIVE_COUNT);  //注冊字符設備 class = class_create(THIS_MODULE, DEVICE_NAME);  //創建類if(IS_ERR(class)) {return PTR_ERR(class);}device = device_create(class, NULL, devid, NULL, DEVICE_NAME);  //創建設備節點printk("alloc success, major = %d minor = %d\n",MAJOR(devid),MINOR(devid));if (IS_ERR(device)) {return PTR_ERR(device);}return 0;  
}static void __exit gpio_led_exit(void)  
{  iounmap((unsigned int *)gpio_add_minor);  iounmap((unsigned int *)clk_add_minor); #if 0cdev_del(&alinx_char.cdev);   //注銷字符設備unregister_chrdev_region(alinx_char.devid, DEVID_COUNT);  //注銷設備號device_destroy(alinx_char.class, alinx_char.devid);  //刪除設備節點class_destroy(alinx_char.class);  //刪除類#endifcdev_del(&cdev);   //注銷字符設備unregister_chrdev_region(devid, DEVID_COUNT);  //注銷設備號device_destroy(class, devid);  //刪除設備節點class_destroy(class);  //刪除類printk("gpio_led_dev_exit_ok\n");  
}  module_init(gpio_led_init);  
module_exit(gpio_led_exit);  
MODULE_LICENSE("GPL");  

加載驅動之后,內核就會為設備指定主設備號和次設備號,不用再使用命中自己指定了。
在這里插入圖片描述


參考文檔:course_s6_ZYNQ那些事兒-Linux驅動篇V1.05

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

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

相關文章

軟信天成:業務流程管理驅動企業數字化轉型

近日&#xff0c;在國家發展改革委辦公廳、國家數據局綜合司聯合印發的《數字經濟2024年工作要點》中&#xff0c;明確強調了本年度大力推進重點領域數字化轉型&#xff0c;營造數字化轉型生態的戰略舉措&#xff0c;標志著國家對于企業數字化轉型的高度重視與積極倡導。 企業…

dubbo復習:(3) 服務超時時間配置

在dubbo admin中 可以進行類似如下配置 configVersion: v2.7 enabled: true configs:- side: consumeraddresses:- 0.0.0.0parameters:timeout: 55這樣配置之后&#xff0c;當服務端響應超過55毫秒時&#xff0c;在服務消費者的控制臺就會看到超時信息

(保姆級教程傻瓜式操作)樹莓派--基于opencv實現人臉識別

前言 因為當時沒有邊實驗邊記錄&#xff0c;所以這篇文章可能存在疏漏。不過很多地方我推薦了我參考過的博客或者視頻&#xff0c;希望盡可能地解答您的疑惑&#xff0c;如果您仍有不懂的地方&#xff0c;歡迎評論&#xff0c;如果我知道答案&#xff0c;我會很樂意為您解答。 …

私活更好用:SpringBoot開源項目!!【送源碼】

今天分享一款非常香的SpringBoot大屏開源項目&#xff0c;非常適合接私活用。 這是一款基于SpringBoot代碼生成器的快速開發平臺&#xff01;采用前后端分離架構&#xff1a;SpringBoot&#xff0c;Mybatis&#xff0c;Shiro&#xff0c;JWT&#xff0c;Vue&Ant Design。強…

MQTT_介紹_1.1

歷史 1999年&#xff1a;MQTT最初由IBM的Andy Stanford-Clark和Cirrus Link的Arlen Nipper開發&#xff0c;用于滿足石油和天然氣公司在遠程地區監控設備的需求。 2006年&#xff1a;IBM發布了MQTT的最初開源實現&#xff0c;但此時MQTT并未獲得廣泛的關注。 2010年&#xff…

三大平臺直播視頻下載保存方法

終于解決了視頻號下載的問題&#xff0c;2024年5月15日親測可用。 而且免費。 教程第二部分&#xff0c;有本地電腦無法下載的解決方案。 第一部分&#xff1a;使用教程&#xff08;正常&#xff09; 第1步&#xff1a;下載安裝包 下載迅雷網盤搜索&#xff1a;大海福利合集…

【Python報錯】Python安裝模塊時報錯Fatal error in launcher

【Python報錯】Python安裝模塊時報錯Fatal error in launcher 最近需要用到python下載一個小工具&#xff0c;自信敲下回車鍵本想看到黑乎乎的終端上會出現快速跳躍的命令代碼&#xff0c;沒想到&#xff0c;報錯了...... Fatal error in launcher: Unable to create process …

關于Windows中的NTUSER.DAT文件的知識,看這篇文章就差不多了

每個用戶配置文件中都隱藏著一個名為NTUSER.DAT的文件。此文件包含每個用戶的設置和首選項,因此你不應該刪除它,也可能不應該編輯它。Windows會自動為你加載、更改和保存該文件。 NTUSER.DAT包含你的用戶配置文件設置 每次更改Windows和已安裝程序的外觀和行為時,無論是桌…

【Qt】Qt開源項目

1、Flameshot 截圖工具 1.1 簡介 Flameshot是一款功能強大但易于使用的屏幕截圖軟件,中文名稱火焰截圖。 Flameshot 簡單易用并有一個CLI版本,所以可以從命令行來進行截圖。 Flameshot 是一個Linux發行版中完全免費且開源的截圖工具 1.2 源碼 github:https://github.com…

智能監控與安全管理:安全帽檢測算法的實踐與應用

在工地、煤礦等高危工作環境中&#xff0c;安全帽的佩戴至關重要。安全帽能夠有效防止因墜落物體或碰撞等引起的頭部傷害&#xff0c;從而保護工作人員的生命安全。然而&#xff0c;傳統的檢查人員佩戴安全帽的方式主要依賴于現場監督和巡查&#xff0c;這種方法不僅耗費大量人…

分頁查詢(PageHelper)

PageHelper依賴 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>${pagehelper}</version> </dependency>DTO接收類 package com.sky.dto;import l…

不用投稿郵箱,怎樣向各大新聞媒體投稿?

身為單位的信息宣傳員,我深知肩上責任重大。每個月,完成單位在媒體上投稿發表文章的考核任務,就如同一場無聲的賽跑,既要保證速度,更要注重質量。起初,我遵循“前輩們”的老路,一頭扎進了郵箱投稿的海洋。但很快,現實給了我一記重拳——郵箱投稿的競爭猶如千軍萬馬過獨木橋,稿件…

Java練習題(二)

6、在異常處理中&#xff0c;如釋放資源&#xff0c;關閉數據庫、關閉文件應由&#xff08;C &#xff09;語 句來完成。 A.try子句 B.catch子句 C.finally子句 D.throw子句 finally子句一般是用來清理這些資源的 7、以下代碼段執行后的輸出結果為…

Hadoop3:客戶端向HDFS寫數據流的流程講解(較枯燥)

一、場景描述 我們登陸HDFS的web端&#xff0c;上傳一個大文件。 二、流程圖 三、講解 流程1&#xff08;Client與NameNode交互&#xff09; 1、HDFS client創建DistributedFileSystem&#xff0c;通過dfs與NameNode進行2次&#xff08;一來一回4次&#xff09;對話&#x…

x264 幀類型代價計算原理:slicetype_mb_cost 函數分析

slicetype_mb_cost 函數 函數功能 計算每個宏塊 MB 的代價 cost。函數參數分析 x264_t *h:全局編碼結構體x264_mb_analysis_t *a:宏塊分析結構體x264_frame_t **frames:系列幀數據結構體int p0:幀序號之一,一般指向靠前幀int p1:幀序號之一,一般指向靠后幀int b:幀標志…

如何修改antd的pagination翻頁組件為中文?

很簡單 加一個屬性 locale{{ items_per_page: 頁 }}<PaginationpageSize{pagination.pageSize}locale{{ items_per_page: 頁 }}current{pagination.current}showTotal{(total, range) > 共有 ${pagination.total} 條數據}style{{ textAlign: right }}onChange{(page: num…

Go微服務開源框架kratos的依賴注入關系總結

該文章為學習開源微服務框架kratos的學習筆記&#xff01;官方文檔見&#xff1a;簡介 | Kratos Kratos 一套輕量級 Go 微服務框架&#xff0c;包含大量微服務相關框架及工具。 一、Kratos 項目結構簡介 通過 Kratos 工具生成的 Go工程化項目模板如下&#xff1a; applicati…

【半夜學習MySQL】復合查詢(含多表查詢、自連接、單行/多行子查詢、多列子查詢、合并查詢等詳解)

&#x1f3e0;關于專欄&#xff1a;半夜學習MySQL專欄用于記錄MySQL數據相關內容。 &#x1f3af;每天努力一點點&#xff0c;技術變化看得見 文章目錄 回顧基本查詢多表查詢自連接子查詢單行子查詢多行子查詢多列子查詢在from子句中使用子查詢合并查詢 回顧基本查詢 下面使用…

計算機網絡 -- 序列化與反序列化

一 協議的重要性 我們都知道&#xff0c;在進行網絡通信的過程中&#xff0c;通信的雙方可以是不同的設備&#xff0c;不同的平臺&#xff0c;不同的平臺&#xff0c;比如說&#xff0c;手機用戶和電腦用戶進行通信&#xff0c;ios系統和安卓系統進行通信。 自己的數據&#xf…

抖店商品詳情API接口(店鋪|標題|主圖|價格|SKU屬性等)

抖店商品詳情API接口(店鋪|標題|主圖|價格|SKU屬性等) 抖店商品詳情API接口是指通過調用抖音開放平臺提供的接口&#xff0c;獲取抖店上商品的詳細信息的方法。 抖店開放平臺提供了一系列的接口&#xff0c;可以用于獲取商品的基本信息、價格、庫存、銷量、評價等各種信息。以…