tt_ioctl在涉及到termios的結構操作時,用到了一個技巧,將結構體首地址強轉為字元類型,通過put_fs_byte和get_fs_byte來進行賦值,還有verify_area()函數,在核心態操作使用者態記憶體資料結構時,要進行的檢查工作,保證寫時複製操作。
代碼:
tty是要操作的tty結構,termios是使用者態傳經來的termios指標儲存tty結構中的termios,這裡注意在運用強轉時,總是對地址進行操作的。
static int get_termios(struct tty_struct * tty, struct termios * termios)
{
int i;
verify_area(termios, sizeof (*termios)); //首先對使用者空間進行檢查,保證有足夠的可用空間
for (i=0 ; i< (sizeof (*termios)) ; i++)
put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios );
return 0;
}
對verify_area()函數的說明,為什麼要用該函數:
對於在使用者態的應用程式來講 當fork一個新子進程時 ,如果父進程不是0號進程的話,則以後兩個進程不管誰要該資料都發生寫時複製(因為此時頁目錄改為唯讀),從而保證每個進程都有自己的唯一一份私人地址空間,私人資料空間。 但核心態具有最高優先順序,核心態此時可以修改使用者態的資料而不發生寫時複製,從而不能保證每一個進程都有自己唯一一份資料空間的原則,即弄亂了應用程式的資料空間(此時該指標處一般都有資料,因而可以不發生缺頁中斷)。
1、除了task0外,其他父進程fork子進程時,兩個進程共用資料與代碼空間,這時他們的頁表項均被設定為唯讀,因而在使用者態任何一方要修改資料均發生寫時複製。但在核心態任何進程都有最高許可權,他們可以修改對應自己的記憶體空間資料而不管他是否唯讀。
假設此時,父進程在核心態修改了父進程的使用者態資料而此時並沒有發生寫時複製,從而造成在子進程不知道的情況下修改了子進程的資料。這就造成了各個進程之間相互幹擾,違反了進程的獨立性原則。
若此時,子進程在核心態修改了使用者態資料,對父進程的幹擾同樣成立(task0除外)
2、在進程共用時,如果某一進程找到了另一進程的共用代碼,那麼在共用成功的前提下,他會把另一進程對應共用頁表項設定成唯讀,即進程若要在使用者態修改這裡的資料也要發生寫時複製。但要其中任意一進程在核心態修改了這裡的資料,也將會造成直接修改另一進程的資料
3、這兩種情況之所以能發生,主要的原因在於多個進程之間共用相同的一部分記憶體頁面,不論你在使用者態還是核心態修改資料,都要保證達到寫時複製的效果。否則你就有可能dirty了其他進程的資料。verify_area就是要保證在核心態的寫時複製機制。 即任何一方要修改共用資料的結果是 作業系統為其另行分配一物理頁面。即上面所說的共用情況下,存在對應的共用資料頁面,它並不是缺頁,所以這是put_fs_long所達不到的
因此為了達到具有使用者態的寫時複製效果,核心態採用了verify_area來實現該功能
static int set_termios(struct tty_struct * tty, struct termios * termios)
{
int i;
for (i=0 ; i< (sizeof (*termios)) ; i++)
((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
change_speed(tty);
return 0;
}
static int get_termio(struct tty_struct * tty, struct termio * termio)
{
int i;
struct termio tmp_termio;
verify_area(termio, sizeof (*termio));
tmp_termio.c_iflag = tty->termios.c_iflag;
tmp_termio.c_oflag = tty->termios.c_oflag;
tmp_termio.c_cflag = tty->termios.c_cflag;
tmp_termio.c_lflag = tty->termios.c_lflag;
tmp_termio.c_line = tty->termios.c_line;
for(i=0 ; i < NCC ; i++)
tmp_termio.c_cc[i] = tty->termios.c_cc[i];
for (i=0 ; i< (sizeof (*termio)) ; i++)
put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio );
return 0;
}
/*
* This only works as the 386 is low-byt-first
*/
static int set_termio(struct tty_struct * tty, struct termio * termio)
{
int i;
struct termio tmp_termio;
for (i=0 ; i< (sizeof (*termio)) ; i++)
((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
*(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag;
*(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag;
*(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag;
*(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag;
tty->termios.c_line = tmp_termio.c_line;
for(i=0 ; i < NCC ; i++)
tty->termios.c_cc[i] = tmp_termio.c_cc[i];
change_speed(tty);
return 0;
}
該函數根據傳入的裝置號,和cmd來進行終端的設定,arg是使用者緩衝區的指標
int tty_ioctl(int dev, int cmd, int arg)
{
struct tty_struct * tty;
if (MAJOR(dev) == 5) {
dev=current->tty;
if (dev<0)
panic("tty_ioctl: dev<0");
} else
dev=MINOR(dev);
tty = dev + tty_table;
switch (cmd) {
case TCGETS: