内容:编写一个C程序,使用Linux下的GTK图形库,分窗口显示三个并发进程的运行。
一个linux下多进程的实例,同时练习GTK编程。
分三个文件,分别是创建进程到主函数threeProc.c、建立子进程窗口的函数procBar.h和另外一个畸形窗口创建函数showImage.h。
实验过程:编辑源程序,将三个源程序和一个图片置于一个文件夹中,执行如下命令:
注意命令:gcc -o sb *.c `pkg-config --cflags --libs gtk+-2.0`
编译命令中使用的单引号类型是很重要的。这里使用了“命令替换”。
命令替换(command substitution)使得可以捕获一个命令的输出而在另一个命令中替换它。
这个单引号不是回车键左边的那个,而是ESC键下面的那个。
源程序及图片文件:(也可以直接打包下载:http://download.csdn.net/detail/creazyapple/4088119)
/* * threeProc.c * SystemMonitor * Created on: 2012-2-22 * Author: zhushengben * Create threee processes , * Each process have it's own window , and just simply show a lab * All the processes are synchronic. * Authored by ZhuShengben hust * 2012.2.14 */ #include <stdio.h> #include <unistd.h> #include <gtk/gtk.h> #include "proBar.h" #include "showImage.h" /* * 如果你的 "delete_event" 信号处理函数返回 FALSE,GTK 会发出 "destroy" 信号。 */ gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) { /* 打印信息 */ // g_print("delete event occurred\n"); gtk_main_quit(); // return (TRUE); } /* destroy a window * 另一个回调函数 */ void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } void initProcWind(int argc, char *argv[], char *pst) { /* GtkWidget 是构件的存储类型 */ GtkWidget * window; GtkWidget *vbox; /* 这个函数在所有的 GTK 程序都要调用。参数由命令行中解析出来并且送到该程序中*/ gtk_init(&argc, &argv); /* Create a new window */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); /* * 当窗口收到 "delete_event" 信号 (这个信号由窗口管理器发出,通常是“关闭” * 选项或是标题栏上的关闭按钮发出的),我们让它调用在前面定义的 delete_event() 函数。 * 传给回调函数的 data 参数值是 NULL,它会被回调函数忽略。 */ gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC( delete_event), NULL); /* G_CALLBACK (delete_event), NULL); */ /* * 在这里我们连接 "destroy" 事件到一个信号处理函数。 * 对这个窗口调用 gtk_widget_destroy() 函数或在 "delete_event" 回调函数中返回 FALSE 值 * 都会触发这个事件。 */ gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(destroy), NULL); /* 容器 */ vbox = gtk_vbox_new(FALSE, 5); gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show(vbox); createProgressBar(vbox, FALSE, FALSE); /* 最后一步是显示新创建的窗口 */ gtk_widget_show(window); /* 所有的 GTK 程序必须有一个 gtk_main() 函数。程序运行停在这里 * 等待事件 (如键盘事件或鼠标事件) 的发生。*/ gtk_main(); } int main(int argc, char *argv[]) { int pid_1, pid_2; /* 创建子进程直到创建成功 */ while ((pid_1 = fork()) == -1) ; /* 处理创建的第一个子进程 */ if (pid_1 == 0) { initProcWind(argc, argv, "I'm the first sonProc"); } /* 处理父进程 */ else { /* 创建第二个子进程直到成功 */ while ((pid_2 = fork()) == -1) ; /* 处理第二个子进程 */ if (pid_2 == 0) initProcWind(argc, argv, "I'm the second sonProc"); else { /* 创建第二个子进程直到成功 */ while ((pid_2 = fork()) == -1) ; /* 处理第二个子进程 */ if (pid_2 == 0) initProcWind(argc, argv, "I'm the second sonProc"); else { /* 处理父进程 */ showImage(argc, argv); } } } return 0; } /* * progressBar.h * systemMonitor * Created on: 2012-2-19 * Author: zhushengben */ #ifndef PROGRESSBAR_H_ #define PROGRESSBAR_H_ typedef struct ProgressData { GtkWidget *pbar; int timer; gboolean activity_mode; gboolean showCpuProgress; } ProgressData; /* 更新进度条,这样就能够看到进度条的移动 */ gint progress_timeout(gpointer data) { ProgressData *pdata = (ProgressData *) data; gdouble new_val; if (pdata->activity_mode) gtk_progress_bar_pulse(GTK_PROGRESS_BAR(pdata->pbar)); else { /* 使用在调整对象中设置的取值范围计算进度条的值 */ new_val = gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(pdata->pbar)) + 0.01; if (new_val > 1.0) new_val = 0.0; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pdata->pbar), new_val); } /* 这是一个 timeout 函数,返回 TRUE,这样它就能够继续被调用 */ return TRUE; } /* 回调函数,切换在进度条你的滑槽上的文本显示 */ void toggle_show_text(GtkWidget *widget, ProgressData *pdata) { const gchar *text; text = gtk_progress_bar_get_text(GTK_PROGRESS_BAR(pdata->pbar)); if (text && *text) gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pdata->pbar), ""); else gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pdata->pbar), "Ereasing..."); } /* 回调函数,切换进度条的活动模式 */ void _toggle_activity_mode(GtkWidget *widget, ProgressData *pdata) { pdata->activity_mode = !pdata->activity_mode; if (pdata->activity_mode) gtk_progress_bar_pulse(GTK_PROGRESS_BAR(pdata->pbar)); else gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pdata->pbar), 0.0); } /* 回调函数,切换进度条的移动方向 */ void toggle_orientation(GtkWidget *widget, ProgressData *pdata) { switch (gtk_progress_bar_get_orientation(GTK_PROGRESS_BAR(pdata->pbar))) { case GTK_PROGRESS_LEFT_TO_RIGHT: gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pdata->pbar), GTK_PROGRESS_RIGHT_TO_LEFT); break; case GTK_PROGRESS_RIGHT_TO_LEFT: gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pdata->pbar), GTK_PROGRESS_LEFT_TO_RIGHT); break; default: ;// 什么也不做 } } /* 清除分配的内存,删除定时器(timer) */ void destroy_progress(GtkWidget *widget, ProgressData *pdata) { gtk_timeout_remove(pdata->timer); pdata->timer = 0; g_free(pdata); } int createProgressBar(GtkWidget *vbox, gboolean activity_mode, gboolean left_right) { ProgressData *pdata; GtkWidget *align; GtkWidget *separator; GtkWidget *table; GtkWidget *button; GtkWidget *check; /* 为传递到回调函数中的数据分配内存 */ pdata = g_malloc(sizeof(ProgressData)); pdata->showCpuProgress = left_right; /* 创建一个居中对齐的对象 */ align = gtk_alignment_new(0.5, 0.5, 0, 0); gtk_box_pack_start(GTK_BOX(vbox), align, FALSE, FALSE, 5); gtk_widget_show(align); /* 创建进度条 */ pdata->pbar = gtk_progress_bar_new(); gtk_container_add(GTK_CONTAINER(align), pdata->pbar); pdata->activity_mode = activity_mode; gtk_widget_show(pdata->pbar); /* 加一个定时器(timer),以更新进度条的值 */ pdata->timer = gtk_timeout_add(100, progress_timeout, pdata); separator = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE, FALSE, 0); gtk_widget_show(separator); /* 行数、列数、同质性(homogeneous) */ table = gtk_table_new(2, 2, FALSE); gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, TRUE, 0); gtk_widget_show(table); /* 添加一个复选按钮,以选择是否显示在滑槽里的文本 */ check = gtk_check_button_new_with_label("show text"); gtk_table_attach(GTK_TABLE(table), check, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 5, 5); g_signal_connect(G_OBJECT(check), "clicked", G_CALLBACK(toggle_show_text), pdata); gtk_widget_show(check); /* 添加一个复选按钮,切换活动状态 */ check = gtk_check_button_new_with_label("Activity mode"); gtk_table_attach(GTK_TABLE(table), check, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 5, 5); g_signal_connect(G_OBJECT(check), "clicked", G_CALLBACK( _toggle_activity_mode), pdata); gtk_widget_show(check); /* 添加一个复选按钮,切换移动方向 */ check = gtk_check_button_new_with_label("Right to Left"); gtk_table_attach(GTK_TABLE(table), check, 0, 1, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 5, 5); g_signal_connect(G_OBJECT(check), "clicked", G_CALLBACK(toggle_orientation), pdata); gtk_widget_show(check); return 0; } #endif /* PROGRESSBAR_H_ */
/* *showImage.h */ void showImage(int argc, char *argv[]) { GtkWidget *window = NULL; GdkPixbuf *pixbuf = NULL; GdkBitmap *bitmap = NULL; GdkPixmap *pixmap = NULL; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC( gtk_main_quit), NULL); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC( gtk_main_quit), NULL); gtk_window_set_decorated(GTK_WINDOW(window), FALSE); // 设置无边框 gtk_widget_set_app_paintable(window, TRUE); gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); gtk_widget_realize(window); pixbuf = gdk_pixbuf_new_from_file("penguin.gif", NULL); // gdk函数读取png文件 gdk_pixbuf_render_pixmap_and_mask(pixbuf, &pixmap, &bitmap, 120); // alpha小于128认为透明 gtk_widget_shape_combine_mask(window, bitmap, 0, 0); // 设置透明蒙板 gdk_window_set_back_pixmap(window->window, pixmap, FALSE); // 设置窗口背景 g_object_unref(pixbuf); g_object_unref(bitmap); g_object_unref(pixmap); gtk_widget_show_all(window); gtk_main(); }
关于多进程:
int pid; /* 创建子进程直到创建成功 */ while ((pid_1 = fork()) == -1) ; /* fork 创建进程,返回值有三种: * pid == -1 表示创建没成功 * pid == 0 表示子进程 * pid > 0 表示父进程 */ if (pid_1 == 0) { //处理创建的子进程 } else { //父进程处理程序 }
在本例中,我首先用procBar.h中的函数创建三个普通窗口,每个窗口添加一个进度条控件,每个控件设置3个参数来控制它。最后再用showImage.h中的函数创建另外一个窗口。注意,前三个普通窗口共享一段代码,但是他们的三个控制参数互补干扰,由此可见进程的特点:一段程序可以对应多个进程,进程是程序的动态表现,同时也可见进程与线程的区别:每个进程的资源是独立的,进程是资源分配的最小单位。