解析linux根文件系統的掛載過程

------------------------------------------
本文系本站原創,歡迎轉載!
轉載請注明出處:http://ericxiao.cublog.cn/
------------------------------------------
一:前言
前段時間在編譯kernel的時候發現rootfs掛載不上。相同的root選項設置舊版的image卻可以。為了徹底解決這個問題。研究了一下rootfs的掛載過程。特總結如下,希望能給這部份知識點比較迷茫的朋友一點幫助。
二:rootfs的種類
總的來說,rootfs分為兩種:虛擬rootfs和真實rootfs.現在kernel的發展趨勢是將更多的功能放到用戶空間完成。以保持內核的精簡。虛擬rootfs也是各linux發行廠商普遍采用的一種方式。可以將一部份的初始化工作放在虛擬的rootfs里完成。然后切換到真實的文件系統.
在虛擬rootfs的發展過程中。又有以下幾個版本:
initramfs:
?Initramfs是在 kernel 2.5中引入的技術,實際上它的含義就是:在內核鏡像中附加一個cpio包,這個cpio包中包含了一個小型的文件系統,當內核啟動時,內核將這個cpio包解開,并且將其中包含的文件系統釋放到rootfs中,內核中的一部分初始化代碼會放到這個文件系統中,作為用戶層進程來執行。這樣帶來的明顯的好處是精簡了內核的初始化代碼,而且使得內核的初始化過程更容易定制。這種這種方式的rootfs是包含在kernel image之中的.
?

cpio-initrd: cpio格式的rootfs

image-initrd:傳統格式的rootfs
關于這兩種虛擬文件系統的制作請自行參閱其它資料
?
三:rootfs文件系統的掛載過程
這里說的rootfs不同于上面分析的rootfs。這里指的是系統初始化時的根結點。即/結點。它是其于內存的rootfs文件系統。這部份之前在<< linux啟動過程分析>>和文件系統中已經分析過。為了知識的連貫性這里再重復一次。
Start_kernel()àmnt_init():

void __init mnt_init(void)

{
???????? ……
???????? ……
???????? init_rootfs();
???????? init_mount_tree();
}
?
Init_rootfs的代碼如下:

int __init init_rootfs(void)

{
???????? int err;
?

???????? err = bdi_init(&ramfs_backing_dev_info);

???????? if (err)
?????????????????? return err;
?
???????? err = register_filesystem(&rootfs_fs_type);
???????? if (err)
?????????????????? bdi_destroy(&ramfs_backing_dev_info);
?
???????? return err;
}
這個函數很簡單。就是注冊了rootfs的文件系統.
init_mount_tree()代碼如下:

static void __init init_mount_tree(void)

{
???????? struct vfsmount *mnt;
???????? struct mnt_namespace *ns;
???????? struct path root;
?

???????? mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);

???????? if (IS_ERR(mnt))

?????????????????? panic("Can't create rootfs");

???????? ns = kmalloc(sizeof(*ns), GFP_KERNEL);
???????? if (!ns)

?????????????????? panic("Can't allocate initial namespace");

???????? atomic_set(&ns->count, 1);
???????? INIT_LIST_HEAD(&ns->list);
???????? init_waitqueue_head(&ns->poll);
???????? ns->event = 0;

???????? list_add(&mnt->mnt_list, &ns->list);

???????? ns->root = mnt;
???????? mnt->mnt_ns = ns;
?
???????? init_task.nsproxy->mnt_ns = ns;
???????? get_mnt_ns(ns);
?
???????? root.mnt = ns->root;
???????? root.dentry = ns->root->mnt_root;
?
???????? set_fs_pwd(current->fs, &root);
???????? set_fs_root(current->fs, &root);
}
在這里,將rootfs文件系統掛載。它的掛載點默認為”/”.最后切換進程的根目錄和當前目錄為”/”.這也就是根目錄的由來。不過這里只是初始化。等掛載完具體的文件系統之后,一般都會將根目錄切換到具體的文件系統。所以在系統啟動之后,用mount命令是看不到rootfs的掛載信息的.
?
四:虛擬文件系統的掛載
根目錄已經掛上去了,可以掛載具體的文件系統了.
在start_kernel()àrest_init()àkernel_init():

static int __init kernel_init(void * unused)

{
???????? ……
???????? ……
???????? do_basic_setup();

if (!ramdisk_execute_command)

?????????????????? ramdisk_execute_command = "/init";

?

???????? if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

?????????????????? ramdisk_execute_command = NULL;

?????????????????? prepare_namespace();
???????? }
?
???????? /*

???????? ?* Ok, we have completed the initial bootup, and

???????? ?* we're essentially up and running. Get rid of the

???????? ?* initmem segments and start the user-mode stuff..

???????? ?*/
???????? init_post();
???????? return 0;
}
do_basic_setup()是一個很關鍵的函數,所有直接編譯在kernel中的模塊都是由它啟動的。代碼片段如下:

static void __init do_basic_setup(void)

{
???????? /* drivers will send hotplug events */
???????? init_workqueues();
???????? usermodehelper_init();
???????? driver_init();
???????? init_irq_proc();
???????? do_initcalls();
}
Do_initcalls()用來啟動所有在__initcall_start和__initcall_end段的函數,而靜態編譯進內核的modules也會將其入口放置在這段區間里。
跟根文件系統相關的初始化函數都會由rootfs_initcall()所引用。注意到有以下初始化函數:
rootfs_initcall(populate_rootfs);
也就是說會在系統初始化的時候會調用populate_rootfs進行初始化。代碼如下:

static int __init populate_rootfs(void)

{
???????? char *err = unpack_to_rootfs(__initramfs_start,
??????????????????????????? ?__initramfs_end - __initramfs_start, 0);
???????? if (err)
?????????????????? panic(err);
???????? if (initrd_start) {

#ifdef CONFIG_BLK_DEV_RAM

?????????????????? int fd;

?????????????????? printk(KERN_INFO "checking if image is initramfs...");

?????????????????? err = unpack_to_rootfs((char *)initrd_start,

??????????????????????????? initrd_end - initrd_start, 1);

?????????????????? if (!err) {

??????????????????????????? printk(" it is/n");

??????????????????????????? unpack_to_rootfs((char *)initrd_start,

???????????????????????????????????? initrd_end - initrd_start, 0);

??????????????????????????? free_initrd();
??????????????????????????? return 0;
?????????????????? }

?????????????????? printk("it isn't (%s); looks like an initrd/n", err);

?????????????????? fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);

?????????????????? if (fd >= 0) {

??????????????????????????? sys_write(fd, (char *)initrd_start,

?????????????????????????????????????????????? initrd_end - initrd_start);

??????????????????????????? sys_close(fd);
??????????????????????????? free_initrd();
?????????????????? }
#else

?????????????????? printk(KERN_INFO "Unpacking initramfs...");

?????????????????? err = unpack_to_rootfs((char *)initrd_start,

??????????????????????????? initrd_end - initrd_start, 0);

?????????????????? if (err)
??????????????????????????? panic(err);
?????????????????? printk(" done/n");
?????????????????? free_initrd();
#endif
???????? }
???????? return 0;
}
unpack_to_rootfs:顧名思義就是解壓包,并將其釋放至rootfs。它實際上有兩個功能,一個是釋放包,一個是查看包,看其是否屬于cpio結構的包。功能選擇是根據最后的一個參數來區分的.
在這個函數里,對應我們之前分析的三種虛擬根文件系統的情況。一種是跟kernel融為一體的initramfs.在編譯kernel的時候,通過鏈接腳本將其存放在__initramfs_start至__initramfs_end的區域。這種情況下,直接調用unpack_to_rootfs將其釋放到根目錄.如果不是屬于這種形式的。也就是__initramfs_start和__initramfs_end的值相等,長度為零。不會做任何處理。退出.
?
對應后兩種情況。從代碼中看到,必須要配制CONFIG_BLK_DEV_RAM才會支持image-initrd。否則全當成cpio-initrd的形式處理。
對于是cpio-initrd的情況。直接將其釋放到根目錄。對于是image-initrd的情況。將其釋放到/initrd.image.最后將initrd內存區域歸入伙伴系統。這段內存就可以由操作系統來做其它的用途了。
接下來,內核對這幾種情況又是怎么處理的呢?不要著急。往下看:
?
回到kernel_init()這個函數:

static int __init kernel_init(void * unused)

{
???????? …….
???????? …….
???????? do_basic_setup();
?
???????? /*

???????? ?* check if there is an early userspace init.? If yes, let it do all

???????? ?* the work
???????? ?*/
?
???????? if (!ramdisk_execute_command)

?????????????????? ramdisk_execute_command = "/init";

?

???????? if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

?????????????????? ramdisk_execute_command = NULL;

?????????????????? prepare_namespace();
???????? }
?
???????? /*

???????? ?* Ok, we have completed the initial bootup, and

???????? ?* we're essentially up and running. Get rid of the

???????? ?* initmem segments and start the user-mode stuff..

???????? ?*/
???????? init_post();
???????? return 0;
}
ramdisk_execute_command:在kernel解析引導參數的時候使用。如果用戶指定了init文件路徑,即使用了“init=”,就會將這個參數值存放到這里。
如果沒有指定init文件路徑。默認為/init
對應于前面一段的分析,我們知道,對于initramdisk和cpio-initrd的情況,都會將虛擬根文件系統釋放到根目錄。如果這些虛擬文件系統里有/init這個文件。就會轉入到init_post()。
Init_post()代碼如下:

static int noinline init_post(void)

{
???????? free_initmem();
???????? unlock_kernel();
???????? mark_rodata_ro();
???????? system_state = SYSTEM_RUNNING;
???????? numa_default_policy();
?

???????? if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)

?????????????????? printk(KERN_WARNING "Warning: unable to open an initial console./n");

?
???????? (void) sys_dup(0);
???????? (void) sys_dup(0);
?
???????? if (ramdisk_execute_command) {
?????????????????? run_init_process(ramdisk_execute_command);

?????????????????? printk(KERN_WARNING "Failed to execute %s/n",

???????????????????????????????????? ramdisk_execute_command);
???????? }
?
???????? /*
???????? ?* We try each of these until one succeeds.
???????? ?*

???????? ?* The Bourne shell can be used instead of init if we are

???????? ?* trying to recover a really broken machine.
???????? ?*/
???????? if (execute_command) {
?????????????????? run_init_process(execute_command);

?????????????????? printk(KERN_WARNING "Failed to execute %s.? Attempting "

?????????????????????????????????????????????? "defaults.../n", execute_command);

???????? }
???????? run_init_process("/sbin/init");
???????? run_init_process("/etc/init");
???????? run_init_process("/bin/init");
???????? run_init_process("/bin/sh");
?
???????? panic("No init found.? Try passing init= option to kernel.");
}
從代碼中可以看中,會依次執行指定的init文件,如果失敗,就會執行/sbin/init, /etc/init,, /bin/init,/bin/sh
注意的是,run_init_process在調用相應程序運行的時候,用的是kernel_execve。也就是說調用進程會替換當前進程。只要上述任意一個文件調用成功,就不會返回到這個函數。如果上面幾個文件都無法執行。打印出沒有找到init文件的錯誤。
對于image-hdr或者是虛擬文件系統中沒有包含 /init的情況,會由prepare_namespace()處理。代碼如下:

void __init prepare_namespace(void)

{
???????? int is_floppy;
?
???????? if (root_delay) {

?????????????????? printk(KERN_INFO "Waiting %dsec before mounting root device.../n",

?????????????????? ?????? root_delay);
?????????????????? ssleep(root_delay);
???????? }
?

???????? /* wait for the known devices to complete their probing */

???????? while (driver_probe_done() != 0)
?????????????????? msleep(100);
?
???????? //mtd的處理
???????? md_run_setup();
?
???????? if (saved_root_name[0]) {

?????????????????? root_device_name = saved_root_name;

?????????????????? if (!strncmp(root_device_name, "mtd", 3)) {

??????????????????????????? mount_block_root(root_device_name, root_mountflags);

??????????????????????????? goto out;
?????????????????? }

?????????????????? ROOT_DEV = name_to_dev_t(root_device_name);

?????????????????? if (strncmp(root_device_name, "/dev/", 5) == 0)

??????????????????????????? root_device_name += 5;

???????? }
?
???????? if (initrd_load())
?????????????????? goto out;
?

???????? /* wait for any asynchronous scanning to complete */

???????? if ((ROOT_DEV == 0) && root_wait) {

?????????????????? printk(KERN_INFO "Waiting for root device %s.../n",

??????????????????????????? saved_root_name);

?????????????????? while (driver_probe_done() != 0 ||

??????????????????????????? (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)

??????????????????????????? msleep(100);
???????? }
?

???????? is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

?

???????? if (is_floppy && rd_doload && rd_load_disk(0))

?????????????????? ROOT_DEV = Root_RAM0;
?
???????? mount_root();
out:

???????? sys_mount(".", "/", NULL, MS_MOVE, NULL);

???????? sys_chroot(".");
}
這里有幾個比較有意思的處理,首先用戶可以用root=來指定根文件系統。它的值保存在saved_root_name中。如果用戶指定了以mtd開始的字串做為它的根文件系統。就會直接去掛載。這個文件是mtdblock的設備文件。
否則將設備結點文件轉換為ROOT_DEV即設備節點號
然后,轉向initrd_load()執行initrd預處理后,再將具體的根文件系統掛載。
注意到,在這個函數末尾。會調用sys_mount()來移動當前文件系統掛載點到”/”目錄下。然后將根目錄切換到當前目錄。這樣,根文件系統的掛載點就成為了我們在用戶空間所看到的”/”了.
對于其它根文件系統的情況,會先經過initrd的處理。即

int __init initrd_load(void)

{
???????? if (mount_initrd) {

?????????????????? create_dev("/dev/ram", Root_RAM0);

?????????????????? /*

?????????????????? ?* Load the initrd data into /dev/ram0. Execute it as initrd

?????????????????? ?* unless /dev/ram0 is supposed to be our actual root device,

?????????????????? ?* in that case the ram disk is just set up here, and gets

?????????????????? ?* mounted in the normal path.
?????????????????? ?*/

?????????????????? if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {

??????????????????????????? sys_unlink("/initrd.image");
??????????????????????????? handle_initrd();
??????????????????????????? return 1;
?????????????????? }
???????? }
???????? sys_unlink("/initrd.image");
???????? return 0;
}
建立一個ROOT_RAM)的設備節點,并將/initrd/.image釋放到這個節點中,/initrd.image的內容,就是我們之前分析的image-initrd。
如果根文件設備號不是ROOT_RAM0( 用戶指定的根文件系統不是/dev/ram0就會轉入到handle_initrd()
如果當前根文件系統是/dev/ram0.將其直接掛載就好了。
?
handle_initrd()代碼如下:

static void __init handle_initrd(void)

{
???????? int error;
???????? int pid;
?

???????? real_root_dev = new_encode_dev(ROOT_DEV);

???????? create_dev("/dev/root.old", Root_RAM0);

???????? /* mount initrd on rootfs' /root */

???????? mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY);

???????? sys_mkdir("/old", 0700);

???????? root_fd = sys_open("/", 0, 0);

???????? old_fd = sys_open("/old", 0, 0);

???????? /* move initrd over / and chdir/chroot in initrd root */

???????? sys_chdir("/root");

???????? sys_mount(".", "/", NULL, MS_MOVE, NULL);

???????? sys_chroot(".");
?
???????? /*

???????? ?* In case that a resume from disk is carried out by linuxrc or one of

???????? ?* its children, we need to tell the freezer not to wait for us.

???????? ?*/
???????? current->flags |= PF_FREEZER_SKIP;
?

???????? pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);

???????? if (pid > 0)

?????????????????? while (pid != sys_wait4(-1, NULL, 0, NULL))

??????????????????????????? yield();
?

???????? current->flags &= ~PF_FREEZER_SKIP;

?
???????? /* move initrd to rootfs' /old */
???????? sys_fchdir(old_fd);

???????? sys_mount("/", ".", NULL, MS_MOVE, NULL);

???????? /* switch root and cwd back to / of rootfs */

???????? sys_fchdir(root_fd);
???????? sys_chroot(".");
???????? sys_close(old_fd);
???????? sys_close(root_fd);
?

???????? if (new_decode_dev(real_root_dev) == Root_RAM0) {

?????????????????? sys_chdir("/old");
?????????????????? return;
???????? }
?
???????? ROOT_DEV = new_decode_dev(real_root_dev);
???????? mount_root();
?

???????? printk(KERN_NOTICE "Trying to move old root to /initrd ... ");

???????? error = sys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);

???????? if (!error)
?????????????????? printk("okay/n");
???????? else {

?????????????????? int fd = sys_open("/dev/root.old", O_RDWR, 0);

?????????????????? if (error == -ENOENT)

??????????????????????????? printk("/initrd does not exist. Ignored./n");

?????????????????? else
??????????????????????????? printk("failed/n");

?????????????????? printk(KERN_NOTICE "Unmounting old root/n");

?????????????????? sys_umount("/old", MNT_DETACH);

?????????????????? printk(KERN_NOTICE "Trying to free ramdisk memory ... ");

?????????????????? if (fd < 0) {
??????????????????????????? error = fd;
?????????????????? } else {

??????????????????????????? error = sys_ioctl(fd, BLKFLSBUF, 0);

??????????????????????????? sys_close(fd);
?????????????????? }

?????????????????? printk(!error ? "okay/n" : "failed/n");

???????? }
}
先將/dev/ram0掛載,而后執行/linuxrc.等其執行完后。切換根目錄,再掛載具體的根文件系統.
到這里。文件系統掛載的全部內容就分析完了.
?
五:小結
在本小節里。分析了根文件系統的掛載流程。并對幾個虛擬根文件系統的情況做了詳細的分析。理解這部份,對我們構建linux嵌入式開發系統是很有幫助的.
?
PS:參考資料:ibm技術論壇的<<Linux2.6 內核的 Initrd 機制解析>>?????????????????
附根文件系統掛載流程圖

轉載于:https://www.cnblogs.com/armlinux/archive/2011/03/30/2396825.html

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

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

相關文章

SIFT講解(SIFT的特征點選取以及描述是重點)

目錄SIFT是什么&#xff1f;尺度空間理論SIFT特征點提取SIFT特征點描述SIFT是什么&#xff1f; SIFT ,即尺度不變特征變換( Scale-invariant feature transform&#xff0c;SIFT) ,一種特征描述方法。具有 尺度魯棒性 旋轉魯棒性 光照魯棒性 SIFT本身包括了特征點篩選及特征點…

操作系統多線程實現_操作系統中的線程實現

操作系統多線程實現Each process has an address space. There is one thread of control in every traditional OS. Sometimes, it is viable to have multiple threads of control in the similar address space which is running in quasi-parallel. Though they were separ…

mysql怎么消除冗余,mysql剔除冗余數據

mysql刪除冗余數據-- -- 1. 查詢冗余數據SELECT t.id FROM t_lifeservice_orders t WHERE t.orderStatus 2 GROUP BY t.channelCode, t.orderNum, t.orderStatus HAVING COUNT(t.orderStatus) > 1;-- -- 2. 定義刪除冗余數據存儲過程DROP PROCEDURE IF EXISTS proc_delete_…

04-圖像的形狀繪制

一、線段繪制 cv2.line(dst,(100,100),(400,400),(0,0,255),2,cv2.LINE_AA) 參數一&#xff1a;目標圖片數據 參數二&#xff1a;當前線段繪制的起始位置&#xff08;也就是兩點確定一條直線&#xff09; 參數三&#xff1a;當前線段繪制的終止位置&#xff08;也就是兩點確定…

(1-e^(-j5w))/(1-e^(-jw))=e^(-j2w)*sin(5w/2)/sin(w/2)的證明過程

問題出現&#xff1a;《數字信號處理第三版》第90頁劉順蘭版 最后一步怎么得到的&#xff1f; 思路&#xff1a;觀察答案&#xff0c;有一個自然對數項。關鍵就是如何提取出這一項。 我的證明過程如下&#xff1a; 參考鏈接&#xff1a; 【和差化積】

php 移植 arm 精簡,php5.4.5移植到arm-linux摘要,lighttpd配置

php5.4.5移植到arm-linux摘要.因為有嵌入WEB服務的需求&#xff0c;再常識了N多的開源的嵌入服務后最終選擇了lighttpd.Apache太大支了&#xff0c;而且在arm上對swf的支持不好.其他的都不怎么理想.lighttpd的移植過程就省略了。這里只摘要了PHP移植,采用fastcgi與lighttpd 協作…

05-圖像的美化

一、彩色圖片直方圖 cv2.calcHist([image],[0],None,[256],[0.0,255.0]) 該方法的所有參數都必須用中括號括起來&#xff01;&#xff01;&#xff01; 參數一&#xff1a;傳入的圖片數據 參數二&#xff1a;用于計算直方圖的通道&#xff0c;這里使用的是灰度直方圖&#xff…

java 檢查目錄是否存在_如何檢查Java目錄是否存在?

java 檢查目錄是否存在We are using the File class that is an abstract representation of file and directory path. To check if a directory exists we have to follow a few steps: 我們正在使用File類 &#xff0c;它是文件和目錄路徑的抽象表示。 要檢查目錄是否存在&a…

Eclipse for android 中設置java和xml代碼提示功能(轉)

1、設置 java 文件的代碼提示功能 打開 Eclipse 依次選擇 Window > Preferences > Java > Editor - Content Assist > Auto activation triggers for Java &#xff0c;設置框中默認是一個點&#xff0c; 現在將它改為&#xff1a; 以下為引用內容&#xff1a; .a…

MySQL 定時器EVENT學習

MySQL 定時器EVENT學習 MySQL從5.1開始支持event功能&#xff0c;類似oracle的job功能。有了這個功能之后我們就可以讓MySQL自動的執行數據匯總等功能&#xff0c;不用像以前需要操作的支持了。如linux crontab功能 。 創建測試表CREATE TABLE t( v VARCHAR(100) NOT NULL…

如何利用FFT(基2時間以及基2頻率)信號流圖求序列的DFT

直接用兩個例子作為模板說明&#xff1a; 利用基2時間抽取的FFT流圖計算序列的DFT 1、按照序列x[k]序號的偶奇分解為x[k]和x2[k]&#xff0c;即x1[k]{1,1,2,1}, x2[k]{-1,-1,1,2} 2、畫出信號流圖并同時進行計算 計算的時候需要參考基本蝶形單元&#xff1a; 關鍵在于 (WN) k…

matlab4.0,matlab?4.0

4.1fort-9:0.5:9if(t>0)y-(3*t^2)5;fprintf(y%.2ft%.2f\n,y,t);elsey(3*t^2)5;fprintf(y%.2ft%.2f\n,y,t);endend編譯結果&#xff1a;y248.00t-9.00y221.75t-8.50y197.00t-8.00y173.75t-7.50y152.00t-7.00y131.75t-6.50y113.00t-6.00y95.75t-5.50y80.00t-5.00y65.75t-4.50y…

圖形學 射線相交算法_計算機圖形學中的陰極射線管

圖形學 射線相交算法陰極射線管 (Cathode Ray Tube) Ferdinand Barun of Strasbourg developed the cathode ray tube in the year 1897. It used as an oscilloscope to view and measure some electrical signals. But several other technologies exist and solid state mov…

Constructor總結

一個類如果沒有構造那么系統為我們在背后創建一個0參數的構造&#xff0c;但是一旦我們創建了但參數的構造&#xff0c;那么默認的構造就沒了。 View Code 1 using System;2 using System.Collections.Generic;3 using System.Linq;4 using System.Text;5 6 namespace Console…

Python連接MySQL及一系列相關操作

一、首先需要安裝包pymysql(python3所對應) 我使用的是Anaconda全家桶&#xff0c;打開cmd&#xff0c;進入Anaconda下的Scripts文件夾下輸入命令&#xff1a;pip install pymysql進行下載安裝 二、我使用的編譯器為Anaconda所帶的Jupyter Notebook 1&#xff0c;在mysql中…

微機原理—可編程計數器/定時器8253概念詳解

目錄前言【1】定時處理方法1、定時的方法&#xff1a;2、定時和計數器【2】8253計數/定時器1、特點&#xff1a;2、芯片引腳以及電路&#xff1a;3、連接方式&#xff1a;4、工作原理&#xff1a;5、寄存器配置a、初始化操作&#xff08;三個通道單獨初始化&#xff09;b、讀出…

php靜態分析工具window,window_SpeedPHP框架核心調試工具,在日常的編程開發當中,開發 - phpStudy...

SpeedPHP框架核心調試工具在日常的編程開發當中&#xff0c;開發者經常會使用到對變量的調試&#xff0c;而sp框架提供的變量調試輸出函數——dump正好滿足了變量調試的需求。下面來介紹一下dump函數的使用方法。dump —— 變量格式化輸出函數用法&#xff1a;dump($vars, $out…

python 溫度轉換程序_Python程序將米轉換為碼

python 溫度轉換程序There are many problems where we have to calculate the distance in yards at the end but initially, the measurements are given in meters. So for such type of problems, the solution is converting the initial parameters into yards and then …

Oracle轉Sqlserver 記錄

使用了微軟的SSMA幫忙&#xff0c;但是目前只有表能幫忙轉&#xff0c;其他的還是要手動改&#xff0c;- - oracle 可以這樣查詢AppServiceInfoaspdb &#xff0c;調用其他庫的表。SQL是&#xff1a; aspdb.dob.AppServiceInfo si數據庫需要和 aspdb ASPDB_Capacity 在siinf…

形參與實參在函數中的傳遞

#include <iostream> #include <cstring> using namespace std; void myFun(int a[]); int main() {int a[10];cout<<"aaa"<<sizeof(a)<<endl;//40 int為4&#xff0c;a為10個int&#xff0c;故為40cout<<"yy"<<…