核心建立的使用者程序printf不能輸出一問的研究

2021-05-22 00:24:36 字數 3203 閱讀 4675

.close_on_exec_init = },        

.open_fds_init     = },              

.fd_array =           

} 我們從這裡可以看到,核心的第一程序是沒有帶開啟檔案資訊的.

我們來看一下使用者空間的init程序的建立過程:

start_kernel() -à rest_init()中**片段如下:

static void noinline __init_refok rest_init(void)

__releases(kernel_lock)

該函式建立了兩個程序,然後本程序將做為idle程序在輪轉.

在建立kernel_init程序的時候,帶的引數是clone_fs | clone_sighand.它沒有攜帶clone_files標誌.也就是說,kernel_init中的檔案描述符資訊是從核心第一程序中複製過去的.並不和它共享.以後,kernel_init程序中,任何關於files的開啟,都不會影響到父程序.

然後在kernel_init() à init_post()中有:

static int noinline init_post(void)

從上面的**中可以看到,它先open了/dev/console.在open的時候,會去找程序沒使用的最小檔案序號.而,當前程序沒有開啟任何檔案,所以sys_open()的時候肯定會找到0.然後,兩次呼叫sys_dup(0)來複製檔案描述符0.複製後的檔案找述符肯定是1.2.這樣,0.1.2就建立起來了.

然後這個程序呼叫run_init_process() à kernel_execve()將當前程序替換成了使用者空間的乙個程序,這也就是使用者空間init程序的由來.此後,使用者空間的程序全是它的子孫程序.也就共享了這個0.1.2的檔案描述符了.這也就是我們所說的stderr.stdio,stdout.

從使用者空間寫個程式測試一下:

#include

#include

#include

#include

#include

main()

執行這個程式,我們會看到,0,1,2描述符的資訊全為/dev/consle.

四:核心建立使用者空間程序的過程

在核心中建立使用者空間程序的相應介面為call_usermodehelper().

實現上,它將要建立的程序資訊鏈入乙個工作佇列中,然後由工作佇列處理函式呼叫kernel_thread()建立乙個子程序,然後在這個程序裡呼叫kernel_execve()來建立使用者空間程序.

在這裡要注意工作佇列和下半部機制的差別.工作佇列是利用乙個核心程序來完成工作的,它和下半部無關.也就是說,它並不在乙個中斷環境中.

那就是說,這樣建立出來的程序,其實就是核心環境,它沒有開啟0,1.2的檔案描述符.

可能也有人會這麼說:那我就不在核心環境下建立使用者程序不就行了?

例如,我在init_module的時候,建立乙個核心執行緒,然後在這個核心執行緒裡,kernel_execve()乙個使用者空間程序不就可以了嗎?

的確,在這樣的情況下,建立的程序不是乙個核心環境,因為在呼叫init_module()的時候,已經通過系統呼叫進入kernel,這時的環境是對應使用者程序環境.但是別忘了.在系統呼叫環境下,再進行系統呼叫是不會成功的(kernel_execve也對應乙個系統呼叫.)

舉例印證如下:

mdoule**:

#include

#include

#include

#include

#include

#include

#include

module_license("gpl");

module_author( "ericxiao:[email protected]" );

static int exeuser_init()

; char *env =

; printk("exeuser_init .../n");

ret = call_usermodehelper(argv[0], argv, env,umh_wait_exec);

return 0;

} static int exeuser_exit()

module_init(exeuser_init);

module_exit(exeuser_exit);

使用者空間程式**:

#include

#include

#include

#include

#include

#include

int main(int argc,char *argv,char *env)

for(i=0; i>/var/console",i,argv[i]);

system(printfmt);     

} tty = ttyname(0);

if(tty == null)

system("echo tty0 is null >> /var/console");

else

tty = ttyname(1);

if(tty == null)

system("echo tty1 is null >> /var/console");

&nb

sp;   else

tty = ttyname(2);

if(tty == null)

system("echo tty2 is null >> /var/console");

else

tty = ttyname(fd);

if(tty == null)

system("echo fd is null >> /var/console");

else

return 0;

} 插入模組過後,呼叫使用者空間的程式,然後這個程式將程序環境輸出到/var/console中,完了可以看到.這個程序輸出的0,1,2描述符資訊全部null.

千萬要注意,在測試的使用者空間程式,不能開啟檔案.這樣會破壞該程序的原始檔案描述符環境(因為這個問題.狠調了乙個晚上,汗顏…).

這樣.使用者空間的printf當然就不能列印出東西了.

四:小結

乙個小問題.卻能引申這麼多東西,看來以後不能放過工作和學習中的任何乙個問題.刨根到底,總會有收穫的.

核心建立的使用者程序printf不能輸出一問的研究

fork 與execve 中stderr,stdio.stdout的繼承關係 其實用繼承這個詞好像不太準確,要準確一點,可能複製更適合.首先有二點 1 父程序fork出子程序後,是共享所有檔案描述符的 實際上也包括socket 2 程序在execve後,除了用o cloexec標誌開啟的檔案外,其它...

核心建立的使用者程序printf不能輸出一問的研究

一 前言 上個星期同事無意間說起,在用核中建立的使用者空間程序中,使用printf不能顯示的問題.這個問題我當時一時半會沒有解釋清楚.現在就從linux kernel的源 的角度來分析該問題的原因所在.二 fork 與execve 中stderr,stdio.stdout的繼承關係 其實用繼承這個詞...

建立特定使用者的程序

用於建立特定使用者的程序,比如在服務程序中建立基於當前使用者的程序等等 bool gettokenbyname handle htoken,lptstr lpname handle hprocesssnap null bool bret false processentry32 pe32 pe32....