imx6ull-驅動開發篇29——Linux阻塞IO 實驗

目錄

實驗程序編寫

blockio.c

blockioApp.c

Makefile 文件

運行測試


在之前的文章里,Linux阻塞和非阻塞 IO(上),我們學習了Linux應用程序了兩種操作方式:阻塞和非阻塞 IO。

在Linux 中斷實驗中,Linux 中斷實驗,我們直接在應用程序中通過 read 函數不斷的讀取按鍵狀態,當按鍵有效的時候就打印出按鍵值。缺點就是:imx6uirqApp 這個測試應用程序擁有很高的 CPU 占用率。

本節實驗,我們使用阻塞 IO 的方式,實現同樣的功能,但大大降低CPU的占用率。

實驗程序編寫

在中斷實驗代碼的基礎上修改,主要是對其添加阻塞訪問相關的代碼。

blockio.c

代碼如下:

#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 <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define IMX6UIRQ_CNT		1			/* 設備號個數 	*/
#define IMX6UIRQ_NAME		"blockio"	/* 名字 		*/
#define KEY0VALUE			0X01		/* KEY0按鍵值 	*/
#define INVAKEY				0XFF		/* 無效的按鍵值 */
#define KEY_NUM				1			/* 按鍵數量 	*//* 中斷IO描述結構體 */
struct irq_keydesc {int gpio;								/* gpio */int irqnum;								/* 中斷號     */unsigned char value;					/* 按鍵對應的鍵值 */char name[10];							/* 名字 */irqreturn_t (*handler)(int, void *);	/* 中斷服務函數 */
};/* imx6uirq設備結構體 */
struct imx6uirq_dev{dev_t devid;			/* 設備號 	 */	struct cdev cdev;		/* cdev 	*/                 struct class *class;	/* 類 		*/struct device *device;	/* 設備 	 */int major;				/* 主設備號	  */int minor;				/* 次設備號   */struct device_node	*nd; /* 設備節點 */	atomic_t keyvalue;		/* 有效的按鍵鍵值 */atomic_t releasekey;	/* 標記是否完成一次完成的按鍵,包括按下和釋放 */struct timer_list timer;/* 定義一個定時器*/struct irq_keydesc irqkeydesc[KEY_NUM];	/* 按鍵init述數組 */unsigned char curkeynum;				/* 當前init按鍵號 */wait_queue_head_t r_wait;	/* 讀等待隊列頭 */
};struct imx6uirq_dev imx6uirq;	/* irq設備 *//* @description		: 中斷服務函數,開啟定時器		*				  	  定時器用于按鍵消抖。* @param - irq 	: 中斷號 * @param - dev_id	: 設備結構。* @return 			: 中斷執行結果*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 0;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));	/* 10ms定時 */return IRQ_RETVAL(IRQ_HANDLED);
}/* @description	: 定時器服務函數,用于按鍵消抖,定時器到了以后*				  再次讀取按鍵值,如果按鍵還是處于按下狀態就表示按鍵有效。* @param - arg	: 設備結構變量* @return 		: 無*/
void timer_function(unsigned long arg)
{unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;num = dev->curkeynum;keydesc = &dev->irqkeydesc[num];value = gpio_get_value(keydesc->gpio); 	/* 讀取IO值 */if(value == 0){ 						/* 按下按鍵 */atomic_set(&dev->keyvalue, keydesc->value);}else{ 									/* 按鍵松開 */atomic_set(&dev->keyvalue, 0x80 | keydesc->value);atomic_set(&dev->releasekey, 1);	/* 標記松開按鍵,即完成一次完整的按鍵過程 */}               /* 喚醒進程 */if(atomic_read(&dev->releasekey)) {	/* 完成一次按鍵過程 *//* wake_up(&dev->r_wait); */wake_up_interruptible(&dev->r_wait);}
}/** @description	: 按鍵IO初始化* @param 		: 無* @return 		: 無*/
static int keyio_init(void)
{unsigned char i = 0;char name[10];int ret = 0;imx6uirq.nd = of_find_node_by_path("/key");if (imx6uirq.nd== NULL){printk("key node not find!\r\n");return -EINVAL;} /* 提取GPIO */for (i = 0; i < KEY_NUM; i++) {imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd ,"key-gpio", i);if (imx6uirq.irqkeydesc[i].gpio < 0) {printk("can't get key%d\r\n", i);}}/* 初始化key所使用的IO,并且設置成中斷模式 */for (i = 0; i < KEY_NUM; i++) {memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(name));	/* 緩沖區清零 */sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);		/* 組合名字 */gpio_request(imx6uirq.irqkeydesc[i].gpio, name);gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);	imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i);
#if 0imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endif}/* 申請中斷 */imx6uirq.irqkeydesc[0].handler = key0_handler;imx6uirq.irqkeydesc[0].value = KEY0VALUE;for (i = 0; i < KEY_NUM; i++) {ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, imx6uirq.irqkeydesc[i].name, &imx6uirq);if(ret < 0){printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);return -EFAULT;}}/* 創建定時器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_function;/* 初始化等待隊列頭 */init_waitqueue_head(&imx6uirq.r_wait);return 0;
}/** @description		: 打開設備* @param - inode 	: 傳遞給驅動的inode* @param - filp 	: 設備文件,file結構體有個叫做private_data的成員變量* 					  一般在open的時候將private_data指向設備結構體。* @return 			: 0 成功;其他 失敗*/
static int imx6uirq_open(struct inode *inode, struct file *filp)
{filp->private_data = &imx6uirq;	/* 設置私有數據 */return 0;
}/** @description     : 從設備讀取數據 * @param - filp    : 要打開的設備文件(文件描述符)* @param - buf     : 返回給用戶空間的數據緩沖區* @param - cnt     : 要讀取的數據長度* @param - offt    : 相對于文件首地址的偏移* @return          : 讀取的字節數,如果為負值,表示讀取失敗*/
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;#if 0/* 加入等待隊列,等待被喚醒,也就是有按鍵按下 */ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); if (ret) {goto wait_error;} 
#endifDECLARE_WAITQUEUE(wait, current);	/* 定義一個等待隊列 */if(atomic_read(&dev->releasekey) == 0) {	/* 沒有按鍵按下 */add_wait_queue(&dev->r_wait, &wait);	/* 將等待隊列添加到等待隊列頭 */__set_current_state(TASK_INTERRUPTIBLE);/* 設置任務狀態 */schedule();							/* 進行一次任務切換 */if(signal_pending(current))	{			/* 判斷是否為信號引起的喚醒 */ret = -ERESTARTSYS;goto wait_error;}__set_current_state(TASK_RUNNING);      /* 將當前任務設置為運行狀態 */remove_wait_queue(&dev->r_wait, &wait);    /* 將對應的隊列項從等待隊列頭刪除 */}keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if (releasekey) { /* 有按鍵按下 */	if (keyvalue & 0x80) {keyvalue &= ~0x80;ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));} else {goto data_error;}atomic_set(&dev->releasekey, 0);/* 按下標志清零 */} else {goto data_error;}return 0;wait_error:set_current_state(TASK_RUNNING);		/* 設置任務為運行態 */remove_wait_queue(&dev->r_wait, &wait);	/* 將等待隊列移除 */return ret;data_error:return -EINVAL;
}/* 設備操作函數 */
static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,
};/** @description	: 驅動入口函數* @param 		: 無* @return 		: 無*/
static int __init imx6uirq_init(void)
{/* 1、構建設備號 */if (imx6uirq.major) {imx6uirq.devid = MKDEV(imx6uirq.major, 0);register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);} else {alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}/* 2、注冊字符設備 */cdev_init(&imx6uirq.cdev, &imx6uirq_fops);cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);/* 3、創建類 */imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.class)) {	return PTR_ERR(imx6uirq.class);}/* 4、創建設備 */imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.device)) {return PTR_ERR(imx6uirq.device);}/* 5、始化按鍵 */atomic_set(&imx6uirq.keyvalue, INVAKEY);atomic_set(&imx6uirq.releasekey, 0);keyio_init();return 0;
}/** @description	: 驅動出口函數* @param 		: 無* @return 		: 無*/
static void __exit imx6uirq_exit(void)
{unsigned i = 0;/* 刪除定時器 */del_timer_sync(&imx6uirq.timer);	/* 刪除定時器 *//* 釋放中斷 */	for (i = 0; i < KEY_NUM; i++) {free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);gpio_free(imx6uirq.irqkeydesc[i].gpio);}cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);device_destroy(imx6uirq.class, imx6uirq.devid);class_destroy(imx6uirq.class);
}module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");

關鍵代碼分析如下:

設備文件名字為“blockio”,當驅動程序加載成功以后就會在根文件系統中出現一個名為“/dev/blockio”的文件。

#define IMX6UIRQ_NAME "blockio" /* 名字 */

imx6uirq 設備結構體中,添加一個等待隊列頭 r_wait,因為在 Linux 驅動中處理阻塞 IO需要用到等待隊列。

wait_queue_head_t r_wait; /* 讀等待隊列頭 */

timer_function函數里,定時器中斷處理函數執行,表示有按鍵按下,先判斷一下是否是一次有效的按鍵,如果是的話就通過 wake_up 或者 wake_up_interruptible 函數來喚醒等待隊列r_wait。

	/* 喚醒進程 */if(atomic_read(&dev->releasekey)) {	/* 完成一次按鍵過程 *//* wake_up(&dev->r_wait); */wake_up_interruptible(&dev->r_wait);}

keyio_init函數,調用 init_waitqueue_head 函數初始化等待隊列頭 r_wait。

/* 初始化等待隊列頭 */init_waitqueue_head(&imx6uirq.r_wait);

imx6uirq_read函數,采用等待事件來處理 read 的阻塞訪問, wait_event_interruptible 函數等待releasekey 有效,也就是有按鍵按下。

如果按鍵沒有按下的話進程就會進入休眠狀態,因為采用了 wait_event_interruptible 函數,因此進入休眠態的進程可以被信號打斷。

#if 0/* 加入等待隊列,等待被喚醒,也就是有按鍵按下 */ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); if (ret) {goto wait_error;} 
#endif

imx6uirq_read函數,使用等待隊列實現阻塞訪問的關鍵代碼:

  • 首先使用 DECLARE_WAITQUEUE 宏定義一個等待隊列,
  • 如果沒有按鍵按下的話,就使用 add_wait_queue 函數將當前任務的等待隊列,添加到等待隊列頭 r_wait 中。
  • 隨后調用__set_current_state 函數,設置當前進程的狀態為 TASK_INTERRUPTIBLE,也就是可以被信號打斷。
  • 接下來調用 schedule 函數進行一次任務切換,當前進程就會進入到休眠態。如果有按鍵按下,那么進入休眠態的進程就會喚醒,然后接著從休眠點開始運行。
  • 通過 signal_pending 函數,判斷一下進程是不是由信號喚醒的,如果是由信號喚醒的話就直接返回-ERESTARTSYS 這個錯誤碼。
  • 如果不是由信號喚醒的(也就是被按鍵喚醒的),那么就調用__set_current_state 函數將任務狀態設置為 TASK_RUNNING,然后調用 remove_wait_queue 函數將進程從等待隊列中刪除。
	DECLARE_WAITQUEUE(wait, current);	/* 定義一個等待隊列 */if(atomic_read(&dev->releasekey) == 0) {	/* 沒有按鍵按下 */add_wait_queue(&dev->r_wait, &wait);	/* 將等待隊列添加到等待隊列頭 */__set_current_state(TASK_INTERRUPTIBLE);/* 設置任務狀態 */schedule();							/* 進行一次任務切換 */if(signal_pending(current))	{			/* 判斷是否為信號引起的喚醒 */ret = -ERESTARTSYS;goto wait_error;}__set_current_state(TASK_RUNNING);      /* 將當前任務設置為運行狀態 */remove_wait_queue(&dev->r_wait, &wait);    /* 將對應的隊列項從等待隊列頭刪除 */}

總結一下,使用等待隊列實現阻塞訪問的步驟:

  • 將任務或者進程加入到等待隊列頭,
  • 在合適的點喚醒等待隊列,一般都是中斷處理函數里面。

blockioApp.c

測試app的代碼和中斷實驗的代碼一致,代碼如下:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"/** @description		: main主程序* @param - argc 	: argv數組元素個數* @param - argv 	: 具體參數* @return 			: 0 成功;其他 失敗*/
int main(int argc, char *argv[])
{int fd;int ret = 0;char *filename;unsigned char data;if (argc != 2) {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;}while (1) {ret = read(fd, &data, sizeof(data));if (ret < 0) {  /* 數據讀取錯誤或者無效 */} else {		/* 數據讀取正確 */if (data)	/* 讀取到數據 */printf("key value = %#X\r\n", data);}}close(fd);return ret;
}

Makefile 文件

makefile文件只需要修改?obj-m 變量的值,改為blockio.o。

內容如下:

KERNELDIR := /home/huax/linux/linux_test/linux-imx-rel_imx_4.1.15_2.1.0_gaCURRENT_PATH := $(shell pwd)
obj-m := blockio.obuild: kernel_modules
kernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

運行測試

編譯代碼:

make -j32 //編譯makefile文件
arm-linux-gnueabihf-gcc blockioApp.c -o blockioApp   //編譯測試app

編譯成功以后,就會生成一個名為“blockio.ko”的驅動模塊文件,和blcokioApp 這個應用程序。

將編譯出來 blockio.ko 和 blockioApp 這兩個文件拷貝到 rootfs/lib/modules/4.1.15目錄中,重啟開發板。

進入到目錄 lib/modules/4.1.15 中,輸入如下命令加載 blockio.ko 驅動模塊:

depmod //第一次加載驅動的時候需要運行此命令
modprobe blockio.ko //加載驅動

加載成功以后,使用如下命令打開 blockioApp 這個測試 APP,并且以后臺模式運行:

./blockioApp /dev/blockio &

按下正點原子開發板上的 KEY0 按鍵,結果如圖:

當按下 KEY0 按鍵以后 blockioApp 這個測試 APP 就會打印出按鍵值。

輸入“top”命令,查看 blockioAPP 這個應用 APP 的 CPU 使用率,如圖:

可以看出,當我們在按鍵驅動程序里面加入阻塞訪問以后, blockioApp 這個應用程序的 CPU 使用率從 99.6%降低到了 0.0%。

我們可以使用“kill”命令關閉后臺運行的應用程序,比如我們關閉掉 blockioApp 這個后臺運行的應用程序。先查看 blockioApp 這個應用程序的 PID:

使用如下命令可“殺死”指定 PID 的進程:

kill -9 149

“./blockioApp /dev/blockio”這個應用程序已經被“殺掉”了。

再輸入“ps”命令查看當前系統運行的進程,會發現 blockioApp 已經不見了。

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

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

相關文章

97. 小明逛公園,Floyd 算法,127. 騎士的攻擊,A * 算法

97. 小明逛公園Floyd 算法dijkstra, bellman_ford 是求單個起點到單個終點的最短路徑&#xff0c;dijkstra無法解決負權邊的問題&#xff0c; bellman_ford解決了負權邊的問題&#xff0c;但二者都是基于單起點和單終點。而Floyd 算法旨在解決多個起點到多個終點的最短路徑問題…

?崩壞世界觀中的安全漏洞與哲學映射:從滲透測試視角解構虛擬秩序的脆弱性?

?崩壞世界觀&#xff1a;游戲中的世界&#xff0c;是真實&#xff0c;也是虛幻的&#xff01;對于游戲中的NPC角色而言&#xff0c;TA們生存的世界&#xff0c;是真實的&#xff01;對于游戲玩家而言&#xff0c;游戲中的世界&#xff0c;是虛擬的&#xff01;通過沉浸式的游戲…

【離線安裝】CentOS Linux 7 上離線部署Oracle 19c(已成功安裝2次)

1.部署參考鏈接&#xff1a; CentOS 7 rpm方式離線安裝 Oracle 19chttps://blog.csdn.net/Vampire_1122/article/details/123038137?fromshareblogdetail&sharetypeblogdetail&sharerId123038137&sharereferPC&sharesourceweixin_45806267&sharefromfrom…

小白向:Obsidian(Markdown語法學習)快速入門完全指南:從零開始構建你的第二大腦(免費好用的筆記軟件的知識管理系統)、黑曜石筆記

一、認識Obsidian&#xff1a;不只是筆記軟件的知識管理系統 1.1 什么是Obsidian Obsidian是一個基于本地存儲的知識管理系統&#xff0c;它將你的所有筆記以純文本Markdown格式保存在電腦本地。這個名字來源于黑曜石——一種火山熔巖快速冷卻形成的玻璃質巖石&#xff0c;象…

攻防世界—Confusion1—(模板注入ssti)

一.解題在login和register的頁面中發現這個文件路徑接下去就找有什么點可以利用二.ssti通過題目信息可知是一只蛇把一只大象纏繞起來了&#xff0c;蛇代表python&#xff0c;大象代表php這邊通過python可以推測可能是模板注入&#xff0c;這邊我看其他的解題是說通過看報文信息…

【Protues仿真】基于AT89C52單片機的超聲波測距

目錄 1 HCSR04超聲波測距傳感器 1.1 基本參數 1.2 引腳說明 1.3 工作原理&#xff08;時序圖&#xff09; 2 基于AT89C52單片機的超聲波測距電路原理圖 2.1 硬件連接說明 2.2 工作原理 3 基于AT89C52單片機的超聲波測距控制程序 3.1.1 初始化設置 3.1.2 超聲波測距原…

LLM - Agent核心架構:四大“身體”部件

文章目錄一、Agent核心架構&#xff1a;四大“身體”部件1. 核心大腦&#xff1a;大型語言模型&#xff08;LLM&#xff09;2. 記憶系統&#xff1a;短期與長期記憶3. 工具箱&#xff08;Toolkit&#xff09;&#xff1a;從“思想家”到“行動家”4. 驅動循環&#xff08;Engin…

html-docx-js 導出word

2025.08.23今天我學習了如何將html頁面內容導出到word中&#xff0c;并保持原有格式&#xff0c;效果如下&#xff1a;代碼如下&#xff1a;1&#xff1a;列表頁面按鈕<el-button type"warning" plain icon"el-icon-download" size"mini" cli…

Science Robotics 通過人機交互強化學習進行精確而靈巧的機器人操作

機器人操作仍然是機器人技術中最困難的挑戰之一&#xff0c;其方法范圍從基于經典模型的控制到現代模仿學習。盡管這些方法已經取得了實質性進展&#xff0c;但它們通常需要大量的手動設計&#xff0c;在性能方面存在困難&#xff0c;并且需要大規模數據收集。這些限制阻礙了它…

Dism++備份系統時報錯[句柄無效]的解決方法

當使用Dism進行系統備份時遇到“[句柄無效]”的錯誤&#xff0c;這通常是由于某些文件或目錄的句柄無法正確訪問或已被占用所導致。以下是一種有效的解決方法&#xff1a;一、查看日志文件定位日志文件&#xff1a;首先&#xff0c;打開Dism軟件所在的目錄&#xff0c;并找到其…

華為/思科/H3C/銳捷操作系統操作指南

好的,這是一份針對 華為(VRP)、思科(IOS/IOS-XE)、H3C(Comware)和銳捷(Ruijie OS) 這四大主流網絡設備廠商操作系統的對比操作指南。本指南將聚焦于它們的共性和特性,幫助你快速掌握多廠商設備的基本操作。 四大網絡廠商操作系統綜合操作指南 一、 核心概念與模式對…

一文讀懂 DNS:從域名解析到百度訪問全流程

目錄 前言 一、什么是 DNS&#xff1f;—— 互聯網的 “地址簿” 為什么需要 DNS&#xff1f; DNS 的核心參數 二、DNS 解析原理&#xff1a;遞歸與迭代的協作 1. 兩種核心查詢方式 2. 完整解析流程&#xff08;以www.baidu.com為例&#xff09; 緩存清理命令 三、DNS …

初試Docker Desktop工具

文章目錄1. 概述2. 下載3. 安裝4. 注冊5. 登錄6. 啟動7. 容器8. 運行容器8.1 運行容器的鏡像8.2 獲取示例應用8.3 驗證Dockerfile文件8.4 拉取Alpine精簡鏡像8.5 創建鏡像8.6 運行容器8.7 查看前端9. 訪問靜態資源9.1 本地靜態資源9.2 創建服務器腳本9.3 修改Dockerfile文件9.4…

百度披露Q2財報:營收327億,AI新業務收入首超百億

8月20日&#xff0c;百度發布2025年第二季度財報&#xff0c;顯示季度總營收327億元&#xff0c;百度核心營收263億元&#xff0c;歸屬百度核心凈利潤74億元&#xff0c;同比增長35%。受AI驅動&#xff0c;涵蓋智能云在內的AI新業務收入增長強勁&#xff0c;首次超過100億元&am…

【字母異位分組】

思路 核心思路&#xff1a;使用排序后的字符串作為鍵&#xff0c;將原始字符串分組 鍵的選擇&#xff1a;對于每個字符串&#xff0c;將其排序后得到標準形式作為鍵分組存儲&#xff1a;使用哈希表&#xff0c;鍵是排序后的字符串&#xff0c;值是對應的原始字符串列表結果構建…

高防cdn如何緩存網頁靜態資源

為什么需要優化網頁靜態資源的緩存&#xff1f; 網頁靜態資源包括圖片、CSS、JavaScript等文件&#xff0c;它們通常體積大、訪問頻繁。在網頁訪問過程中&#xff0c;如果每次都從源服務器請求這些靜態資源&#xff0c;會導致網絡延遲和帶寬消耗。而優化網頁靜態資源的緩存&am…

使用Pandas進行缺失值處理和異常值檢測——實戰指南

目錄 一、缺失值處理 1.1 缺失值的識別 1.2 刪除缺失值 1.3 填充缺失值 二、異常值檢測 2.1 異常值的定義 2.2 常用檢測方法 IQR&#xff08;四分位數間距&#xff09;法 Z-score&#xff08;標準分數&#xff09;法 三、實戰案例&#xff1a;基因表達數據預處理 四…

B.30.01.1-Java并發編程及電商場景應用

摘要 本文深入探討了Java并發編程的核心概念及其在電商系統中的實際應用。從基礎并發機制到高級并發工具&#xff0c;結合電商業務場景中的典型問題&#xff0c;如高并發秒殺、庫存管理、訂單處理等&#xff0c;提供了實用的解決方案和最佳實踐。 1. Java并發編程基礎 1.1 并發…

怎樣避免游戲檢測到云手機?

以下是一些可能避免游戲檢測到云手機的方法&#xff1a;云手機可能會因網絡配置等因素出現一些異常網絡行為&#xff0c;如網絡延遲的規律性變化等&#xff0c;在使用云手機玩游戲時&#xff0c;盡量保持網絡行為的穩定性和自然性&#xff0c;避免短時間內頻繁切換網絡連接&…

文件上傳 --- uploadlabs靶場

目錄 1 前端和js校驗 抓包改包 2 . 2.1 .htaccess&#xff08;偽靜態&#xff09; 2.2 %00截斷 &#xff08;php5.2&#xff09; 2.3 user_init_ 2.4 3 圖片碼防御 4 競爭型漏洞 思路&#xff1a; 容易出現的問題: 1 前端和js校驗 關閉JS的代碼&#xff0c;上傳PHP…