




已閱讀5頁,還剩59頁未讀, 繼續(xù)免費閱讀
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
課 程 實 驗 報 告 課程名稱: 操作系統(tǒng) 專業(yè)班級:計算機科學與技術xxxx班 學 號: xxxxxxx 姓 名: xxxx 指導老師: xxxxx 報告日期: 20xx年xx月xx日 計算機科學與技術學院課程設計概述一、 實驗目的掌握Linux操作系統(tǒng)的使用方法;了解Linux系統(tǒng)內核代碼結構;掌握實例操作系統(tǒng)的實現(xiàn)方法。二、 實驗要求1、 掌握Linux操作系統(tǒng)的使用方法,包括鍵盤命令、系統(tǒng)調用;掌握在Linux下的編程環(huán)境。l 編一個C程序,其內容為實現(xiàn)文件拷貝的功能;l 編一個C程序,其內容為分窗口同時顯示三個并發(fā)進程的運行結果。要求用到Linux下的圖形庫。2、 掌握系統(tǒng)調用的實現(xiàn)過程,通過編譯內核方法,增加一個新的系統(tǒng)調用。另編寫一個應用程序,調用新增加的系統(tǒng)調用。實現(xiàn)的功能是:文件拷貝;3、 掌握增加設備驅動程序的方法。通過模塊方法,增加一個新的設備驅動程序,其功能可以簡單。實現(xiàn)字符設備的驅動;4、 了解和掌握/proc文件系統(tǒng)的特點和使用方法l 了解/proc文件的特點和使用方法l 監(jiān)控系統(tǒng)狀態(tài),顯示系統(tǒng)中若干部件使用情況l 用圖形界面實現(xiàn)系統(tǒng)監(jiān)控狀態(tài)。5、 設計并實現(xiàn)一個模擬的文件系統(tǒng)(選作)三、 設計說明Linux系統(tǒng)版本12.10新編譯的內核版本3.6.11虛擬機VMware Fusion 3.0環(huán)境MAC OS X 10.68實驗一一、 實驗目的 1. 編寫一個C程序實現(xiàn)文件拷貝的功能2. 編寫一個C程序,其內容為分窗口同時顯示三個并發(fā)進程的運行結果,要求用到Linux下的圖形庫。二、 實驗過程1. 文件拷貝功能要實現(xiàn)文件拷貝功能,主要用到的函數(shù)是open、write、read。以前在windows下寫C語言打開文件常用的fopen,此時不能用,因為fopen是ANSIC標準中的C語言庫函數(shù),在不同的系統(tǒng)中應該調用不同的內核api ;所以應該直接使用linux中的系統(tǒng)函數(shù)open。主要用到的頭文件:Unistd.h包含了許多Linux系統(tǒng)服務的函數(shù)原型,如:read、writeFcntl.h定義了很多宏和open,fcntl函數(shù)原型Stdio.h標準輸入輸出頭文件sys/types.h此頭文件包含適當時應使用的多個基本派生類型sys/stat.h 包含了獲取文件屬性的一些函數(shù)errno.h用于調試錯誤代碼是所需要的一些errno變量string.h包含了處理字符串的一些函數(shù)設計思路:由命令行參數(shù)獲取2個文件名,根據(jù)其文件名和路徑分別打開該2個文件,設置一個循環(huán),從源文件復制N個字節(jié)到目的文件,直到源文件指針到文件尾,最后關閉2個文件。在可能出錯的地方需要加上相應的報錯代碼和中斷,并輸出錯誤信息,以方便調試或是往后應用在第2小題中可能發(fā)生的錯誤。理清楚設計思路后,根據(jù)需求寫出相應的源代碼見后頁源程序代碼scopy.c ;在Linux終端使用編譯命令 gcc o scopy scopy.c將程序編譯并生產exe可執(zhí)行文件。然后手動創(chuàng)建一個測試文件test.txt ,在終端輸入命令./scopy test.txt target.txt這樣就能將源文件test.txt復制到目標文件target.txt2. 分窗口顯示并發(fā)進程的運行結果 安裝Linux下的GTK+:首先要在Linux下載GTK+相關庫文件并安裝。在終端輸入sudo apt-get install gnome-core-devel ,然后根據(jù)提示操作,就會安裝 libgtk2.0-dev libglib2.0-dev 等開發(fā)所需的相關庫文件。 $sudo apt-get install build-essential$sudo apt-get install gnome-core-devel$sudo apt-get install pkg-config$sudo apt-get install libgtk2.0*編譯GTK+代碼時需要包含的頭文件是gtk/gtk.h,此外,還必須連接若干庫;比如編譯test.c時用以下命令。gcc o test test.c pkg-config -cflags -libs gtk+-2.0在編寫代碼時需要用到的控件、窗口等視窗物件形態(tài),用類GtkWidget定義其為指針類型。編寫一個GTK+程序的基本步驟如下:l 初始化Gtkl 建立控件l 登記消息與消息處理函數(shù)l 執(zhí)行消息循環(huán)函數(shù)gtk_main()之后所設計的3個進程,基本上都是以這樣的方式編寫代碼的,因為之前曾用過OpenGL,所以在這方面掌握的比較快。初始化主要使用的函數(shù)有gtk_init(&argc,&argv); /啟動GTK gtk_window_new(GTK_WINDOW_TOPLEVEL); /創(chuàng)建窗口 gtk_window_set_title(GTK_WINDOW(window),標題名); /設置窗口標題名 gtk_widget_set_usize(window, 200, 200); /設置窗口大小 gtk_widget_show(window); /顯示窗口建立控件的一般流程/*創(chuàng)建表格準備封裝*/gtk_table_new (/創(chuàng)建多少列gint rows,/創(chuàng)建多少欄gint columns,/用來決定表格如何來定大小gint homogeneous);/*這個函數(shù)是將表格table,結合到窗口window里*/gtk_container_add(GTK_CONTAINER(window),table);gtk_widget_show(table);/ 顯示該表格/*要把物件放進box中,可用以下函數(shù)*/void gtk_table_attach_defaults (GtkTable*table,/參數(shù)(table)是選定某表格GtkWidget*widget,/(child)是想放進去的物件gintleft_attach,/以下參數(shù)是指定把物件放在哪里, 及用多少個boxesgintright_attach,ginttop_attach,gintbottom_attach);三、 實驗結果1. 文件拷貝功能 2. 分窗口顯示并發(fā)進程的運行結果四、 源代碼1. 文件拷貝功能#include #include #include #include #include #include #include #define BUFFER_SIZE 1024 /緩沖區(qū)大小int main(int argc,char *argv) int from_fd,to_fd; int bytes_read,bytes_write; char bufferBUFFER_SIZE; /設定一個緩沖區(qū) char *ptr; if(argc!=3) /三個參數(shù) fprintf(stderr,Usage:%s fromfile tofilena,argv0); return(-1); /* 打開源文件 */ if(from_fd=open(argv1,O_RDONLY)=-1) fprintf(stderr,Open %s Error:%sn,argv1,strerror(errno); return(-1); /* 創(chuàng)建目的文件 */ if(to_fd=open(argv2,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR)=-1) fprintf(stderr,Open %s Error:%sn,argv2,strerror(errno); return(-1); while(bytes_read=read(from_fd,buffer,BUFFER_SIZE) /* 出錯*/ if(bytes_read=-1)&(errno!=EINTR) break; else if(bytes_read0) ptr=buffer; while(bytes_write=write(to_fd,ptr,bytes_read) /* 出錯*/ if(bytes_write=-1)&(errno!=EINTR)break; /* 寫完了所有讀的字節(jié) */ else if(bytes_write=bytes_read) break; /* 只寫了一部分,繼續(xù)寫 */ else if(bytes_write0) ptr+=bytes_write; bytes_read-=bytes_write; /* 寫的時候出錯*/ if(bytes_write=-1)break; close(from_fd); close(to_fd); return(1);2. 分窗口顯示并發(fā)進程的運行結果#include #include #include gint progress_timeout( gpointer pbar ) gdouble new_val; char s10; new_val = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (pbar) + 0.01; if (new_val 1.0) new_val = 0.0; sprintf (s, %.0f%, new_val*100); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pbar), new_val); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pbar),s); return TRUE; void destroy_progress( GtkWidget *widget) gtk_main_quit ();void show(int argc,char *argv,char *title ) GtkWidget *window; GtkWidget *vbox; GtkWidget *pbar; GtkWidget *pbar2; GtkWidget *button; GtkWidget *label; int timer; char id_char50; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_resizable (GTK_WINDOW (window), TRUE); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); g_signal_connect (G_OBJECT (window), destroy, G_CALLBACK (destroy_progress), NULL); gtk_window_set_title (GTK_WINDOW (window), title); gtk_container_set_border_width (GTK_CONTAINER (window), 0); vbox = gtk_vbox_new (FALSE, 10); gtk_container_set_border_width (GTK_CONTAINER (vbox), 10); gtk_container_add (GTK_CONTAINER (window), vbox); gtk_widget_show (vbox); sprintf (id_char, 本進程ID:%d, getpid (); label = gtk_label_new (id_char); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); gtk_widget_show (label); sprintf (id_char, 父進程ID:%d, getppid (); label = gtk_label_new (id_char); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); gtk_widget_show (label); pbar = gtk_progress_bar_new (); gtk_box_pack_start (GTK_BOX (vbox), pbar, FALSE, FALSE, 0); gtk_widget_show (pbar); timer = gtk_timeout_add (100, progress_timeout, pbar); button = gtk_button_new_with_label (close); g_signal_connect_swapped (G_OBJECT (button), clicked, G_CALLBACK (gtk_widget_destroy), window); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_widget_grab_default (button); gtk_widget_show (button); gtk_widget_show (window); gtk_main ();int main(int argc, char *argv) int pid = fork (); if (pid 0)printf (error!n); else if (pid = 0) int pid = fork (); if (pid 0) printf (error!n);else if (pid = 0) show (argc,argv,process3);else show (argc,argv,process2); elseshow (argc,argv,process1); 實驗二一、 實驗目的 掌握系統(tǒng)調用的實現(xiàn)過程,通過編譯內核方法,增加一個新的系統(tǒng)調用。另編寫一個應用程序,調用新增加的系統(tǒng)調用。實現(xiàn)的功能是:文件拷貝。二、 實驗過程 1. 下載并解壓內核到官方網(wǎng)站/下載內核linux-3.6.11.tar.xz,將其放入/usr/src中,打開終端,使用下列命令對其解壓:#tar -xvf linux-3.6.11該目錄用來存放內核的源碼。 2. 修改內核首先對系統(tǒng)調用模塊添加一個自定義函數(shù)helloworld.c,即添加一個新的自定義函數(shù)到/arch/x86/kernel中。在/include/linux/syscalls.h中添加如下代碼,進行對系統(tǒng)調用模塊的修改asmlinkage int sys_helloworld(const char* s_file, const char* t_file); 接著在arch/x86/syscalls/syscall_32.tbl添加350 i386 helloworldsys_helloworld 在arch/x86/kernel/Makefile中添加 obj-y+= helloworld.o 3. 編譯內核依次在終端鍵入以下命令進行對內核的編譯,此過程要持續(xù)2小時左右sudo apt-get install fakeroot build-essential kernel-package libncurses5 libncurses5-devcp /boot/config-3.5.0-17-generic ./.configmake menuconfigload .configmake-kpkg cleanfakeroot make-kpkg -initrd kernel_image kernel_headers 4. 安裝內核編譯完成內核咋終端輸入以下命令對內核進行安裝sudo dpkg -i linux-image-3.6.11.Custom_i386.debsudo dpkg -i linux-headers-3.6.11.Custom_i386.deb 5. 系統(tǒng)調用使用如下命令實現(xiàn)對系統(tǒng)函數(shù)字符拷貝的調用gcc -o testsys.c testsys ./testsys三、 實驗結果四、 源代碼helloworld.c#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Move somewhere else to avoid recompiling? */#include #include #include #include #ifndef SET_UNALIGN_CTL# define SET_UNALIGN_CTL(a,b)(-EINVAL)#endif#ifndef GET_UNALIGN_CTL# define GET_UNALIGN_CTL(a,b)(-EINVAL)#endif#ifndef SET_FPEMU_CTL# define SET_FPEMU_CTL(a,b)(-EINVAL)#endif#ifndef GET_FPEMU_CTL# define GET_FPEMU_CTL(a,b)(-EINVAL)#endif#ifndef SET_FPEXC_CTL# define SET_FPEXC_CTL(a,b)(-EINVAL)#endif#ifndef GET_FPEXC_CTL# define GET_FPEXC_CTL(a,b)(-EINVAL)#endif#ifndef GET_ENDIAN# define GET_ENDIAN(a,b)(-EINVAL)#endif#ifndef SET_ENDIAN# define SET_ENDIAN(a,b)(-EINVAL)#endif#ifndef GET_TSC_CTL# define GET_TSC_CTL(a)(-EINVAL)#endif#ifndef SET_TSC_CTL# define SET_TSC_CTL(a)(-EINVAL)#endifasmlinkage int sys_helloworld(const char* s_file, const char* t_file) /printk(hello,world!n); int bytes_read, bytes_write; /file id int from_fd, to_fd; char buffer100; char *ptr; mm_segment_t old_fs; old_fs = get_fs(); set_fs(KERNEL_DS); if (from_fd = sys_open(s_file,O_RDONLY,0) = -1) return -1; if (to_fd = sys_open(t_file,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR) = -1) return -2; /read source file while(bytes_read=sys_read(from_fd,buffer,1) if(bytes_read=-1) break; else if(bytes_read0) ptr=buffer; /write to the target file while(bytes_write=sys_write(to_fd,ptr,bytes_read) if(bytes_write=-1)break; else if(bytes_write=bytes_read) break; else if(bytes_write0) ptr+=bytes_write; bytes_read-=bytes_write; if(bytes_write=-1)break; set_fs(old_fs); return 0; testsys.c#include #include int main(void)syscall(350,./test.txt,./target.txt);return 0;實驗三一、 實驗目的 掌握增加設備驅動程序的方法。通過模塊方法,增加一個新的設備驅動程序,其功能可以簡單。實現(xiàn)字符設備的驅動。二、 實驗過程 通過以下指令進行字符驅動程序的安裝sudo make sudo insmod f device.ko添加設備sudo mknod /dev/device c 147 0 實現(xiàn)功能sudo ./sdevice刪除字符設備sudo rmmod device三、 實驗結果四、 源代碼device.c#include #include #include #include MODULE_LICENSE(GPL);#define MAJOR_NUM 147static ssize_t d_read(struct file *, char *, size_t, loff_t*);static ssize_t d_write(struct file *, const char *, size_t, loff_t*);static ssize_t d_open(struct inode *,struct file *);static ssize_t d_release(struct inode *,struct file *);struct file_operations d_fops= .read=d_read, .write=d_write, .open=d_open, .release=d_release,;static char var1024=Device test:;int counter=12;static int _init device_init(void) int result; result = register_chrdev(MAJOR_NUM, device, &d_fops); if(result) printk(device register failure); else printk(device register success); return result;static void _exit device_exit(void) unregister_chrdev(MAJOR_NUM, device); printk(unloading the device.);static ssize_t d_read(struct file *filp, char *buf, size_t len, loff_t *off) if(copy_to_user(buf,var,1024*sizeof(char) return - EFAULT; return sizeof(char);static ssize_t d_write(struct file *filp,const char *buf,size_t len,loff_t *off) if(copy_from_user(var+counter,buf,len*sizeof(char) return - EFAULT; counter+=len; return sizeof(char); static ssize_t d_open(struct inode *inode,struct file *file) printk(KERN_INFO open it!n); return 0;static ssize_t d_release(struct inode *inode,struct file *file) printk(KERN_INFO close itn); return 0; module_init(device_init);module_exit(device_exit);sdevice.c#include #include #include #include #include main() int fd; char buffer1024; int num; fd=open(/dev/device,O_RDWR,S_IRUSR|S_IWUSR); if(fd!=-1) read(fd,buffer,1024*sizeof(char); printf(The device is %sn,buffer); printf(Please input the characters written to device:n); fgets(buffer,1024,stdin); num=strlen(buffer)-1; write(fd,buffer,num*sizeof(char); read(fd,buffer,1024*sizeof(char); printf(The device is %sn,buffer); close(fd); else printf(Device open failuren); 實驗四一、 實驗目的了解和掌握/proc文件系統(tǒng)的特點和使用方法了解/proc文件的特點和使用方法監(jiān)控系統(tǒng)狀態(tài),顯示系統(tǒng)中若干部件使用情況用圖形界面實現(xiàn)系統(tǒng)監(jiān)控狀態(tài)。二、 實驗過程通過在訪問/kernel里的/proc文件獲得meninfo等相關信息,并用gtk使其以圖形方式顯示,相關功能包括:(1)獲取并顯示主機名(2)獲取并顯示系統(tǒng)啟動的時間 (未實現(xiàn))(3)顯示系統(tǒng)到目前為止持續(xù)運行的時間 (未實現(xiàn))(4)顯示系統(tǒng)的版本號(5)顯示cpu的型號和主頻大小(6)同過pid或者進程名查詢一個進程,并顯示該進程的詳細信息,提供殺掉該進程的功能。(7)顯示系統(tǒng)所有進程的一些信息,包括pid,ppid,占用內存大小,優(yōu)先級等等(8)cpu使用率的圖形化顯示(2分鐘內的歷史紀錄曲線) (未實現(xiàn))(9)內存和交換分區(qū)(swap)使用率的圖形化顯示(2分鐘內的歷史紀錄曲線) (未實現(xiàn))(10)在狀態(tài)欄顯示當前時間 (未實現(xiàn))(11)在狀態(tài)欄顯示當前cpu使用率(12)在狀態(tài)欄顯示當前內存使用情況(13)用新線程運行一個其他程序 (未實現(xiàn))(14)關機功能(未實現(xiàn))三、 實驗結果四、 源代碼creatwindow.hvoid end_program(GtkWidget *widget, gpointer data) gtk_main_quit();void creatWindow(void) int i; window = g_object_new(GTK_TYPE_WINDOW,title, System Monitor,default_height, windowHeight,default_width, windowWidth,NULL); gtk_container_border_width(GTK_CONTAINER(window),10); notebook = g_object_new(GTK_TYPE_NOTEBOOK, NULL); for (i=0;ipage_quantity;+i) page_titlei=g_object_new(GTK_TYPE_LABEL, label, page_title_chari, NULL); page_tablei = gtk_table_new(gridQuanV,gridQuanH,FALSE); g_signal_connect(window, destroy, G_CALLBACK(end_program), NULL); gtk_container_add(GTK_CONTAINER(window),GTK_WIDGET(notebook); for (i=0;ipage_quantity;+i) gtk_notebook_append_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(page_tablei), GTK_WIDGET(page_titlei); generaldef.hconst int DebugMode=1;const float RefershCycle=1;char *markup1;#define _SVID_SOURCE#include #include #include #include #include #include #include /*=*/#define page_quantity 5const int windowHeight=600;const int windowWidth=900;const int gridQuanV=100;const int gridQuanH=100;GtkWindow *window;GtkNotebook *notebook;GtkLabel *page_titlepage_quantity;char* page_title_charpage_quantity=System Brief,Resource Usage,Process Info,Module Info,Main Filesystem Info,;GtkWidget *page_tablepage_quantity;#include creatwindow.h/*=*/GtkImage *logo;GtkLabel* hostNameLabel;GtkLabel* hostNameText;GtkLabel* linuxSystemLabel;GtkLabel* kernelVersionText;GtkLabel* hardwareBriefLabel;GtkLabel* cpuInfoText;GtkLabel* memInfoText;GtkLabel* authorInfo;GtkHSeparator* hSep1;#include page1.h/*=*/GtkFrame *page2Framepage_quantity;char* page2FrameNamepage_quantity=System Overall Load,CPU Usage,Memory Usage,Network Activity,;int* cpuStruct1;int* cpuStruct2;int countFlag=0;float overallLoad3;GtkLabel* avgLoadLabel;char overallLoadLabelBuffer512;float cpuUsage;char cpuUsageText512;GtkProgress* cpuUsageProgressBar;int memAll;int memFree;int swapAll;int swapFree;float memPercent;float swapPercent;GtkProgress* memProgressBar;GtkProgress* swapProgressBar;char memText512;char swapText512;int netReceive;int netSend;float rece
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 參加小區(qū)活動方案
- 2025至2030中國科技木行業(yè)市場發(fā)展分析及發(fā)展前景與投融資報告
- 2025至2030中國磷脂酰肌醇4,5雙磷酸3激酶催化亞基Beta亞型行業(yè)發(fā)展趨勢分析與未來投資戰(zhàn)略咨詢研究報告
- 2025至2030中國皮帶行業(yè)市場發(fā)展分析及前景預測與投資發(fā)展戰(zhàn)略報告
- 2025至2030中國病人計劃軟件行業(yè)發(fā)展趨勢分析與未來投資戰(zhàn)略咨詢研究報告
- 2025至2030中國男士秋冬睡衣行業(yè)發(fā)展趨勢分析與未來投資戰(zhàn)略咨詢研究報告
- 2025至2030中國瑜伽工作室系統(tǒng)行業(yè)發(fā)展趨勢分析與未來投資戰(zhàn)略咨詢研究報告
- 2025至2030中國玻璃棉吸聲板行業(yè)發(fā)展趨勢分析與未來投資戰(zhàn)略咨詢研究報告
- 2025至2030中國貓美容用品行業(yè)發(fā)展趨勢分析與未來投資戰(zhàn)略咨詢研究報告
- 2025至2030中國牙尖定位器行業(yè)發(fā)展趨勢分析與未來投資戰(zhàn)略咨詢研究報告
- 2025發(fā)展對象考試題庫與參考答案
- 絕交協(xié)議書2025修訂版:規(guī)范情感解除流程的法律文件
- 煤礦安全用電培訓課件
- GB/T 45019-2024道路用玄武巖纖維瀝青混合料
- 2024版新能源汽車充電站場地租賃及充電服務合同2篇
- 特種設備日管控、周排查、月調度模板
- 急性髓系白血病護理個案
- 兒童膿毒血癥護理
- 頂板事故應急演練
- 智研咨詢發(fā)布:中國企業(yè)數(shù)字化解決方案行業(yè)市場發(fā)展環(huán)境及前景研究報告
- 《大學計算機基礎案例教程(微課版)第2版》全套教學課件
評論
0/150
提交評論