1. 程式人生 > >2. master和slave的匹配過程

2. master和slave的匹配過程

(一)master的註冊過程

1. 首先來看看master的註冊過程,在mxc_v4l2_capture.c檔案中,module_init(camera_init)函式開始,在camera_init函式中通過

err= platform_driver_register(&mxc_v4l2_driver)

來將mxc_v4l2_driver這個驅動註冊到platform平臺上面,如果有匹配的裝置的話,就會呼叫到mxc_v4l2_driver裡面的probe函式。

(下面的分析都是從流程上面來分析,去掉了函式中一些與分析無關的程式碼,關於函式的詳細分析,看《mxc_v4l2_capture.c

分析》那一節)

2. 下面來看看這個probe函式

static int mxc_v4l2_probe(struct platform_device *pdev) 
{ 
	/* Create cam and initialize it. */ 
	cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL); 

	init_camera_struct(cam, pdev);  //初始化cam_data結構體,分析見2.1
	pdev->dev.release = camera_platform_release; 

	/* Set up the v4l2 device and register it*/ 
	cam->self->priv = cam;    //將cam_data結構體裡面的self裡面的priv指標指向自己,在下面的 v4l2_int_device_register 函式中就會用到。
	v4l2_int_device_register(cam->self); //分析見2.2

	/* register v4l video device */ 
	if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) {    //註冊video裝置
		kfree(cam); 
		cam = NULL; 
		pr_err("ERROR: v4l2 capture: video_register_device failed\n"); 
		return -1; 
	} 
}

在這個函式中,首先是為cam_data結構體分配了記憶體,然後就呼叫init_camera_struct函式來初始化這個cam_data結構體。

2.1 我們來看看這個init_camera_struct函式裡面都做了什麼:

static int init_camera_struct(cam_data *cam, struct platform_device *pdev) 
{ 
	const struct of_device_id *of_id = 
			of_match_device(mxc_v4l2_dt_ids, &pdev->dev); 
	struct device_node *np = pdev->dev.of_node; 
	int ipu_id, csi_id, mclk_source; 
	int ret = 0; 
	struct v4l2_device *v4l2_dev; 

	/* Default everything to 0 */ 
	memset(cam, 0, sizeof(cam_data)); 

	init_MUTEX(&cam->param_lock); 
	init_MUTEX(&cam->busy_lock); 

	cam->video_dev = video_device_alloc(); //分配一個video_device結構體
	*(cam->video_dev) = mxc_v4l_template; //設定ops操作

	video_set_drvdata(cam->video_dev, cam);  //將cam設定為cam->video_dev的私有資料
	dev_set_drvdata(&pdev->dev, (void *)cam); 
	cam->video_dev->minor = -1; 

	v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL); 

	if (v4l2_device_register(&pdev->dev, v4l2_dev) < 0) { 
		dev_err(&pdev->dev, "register v4l2 device failed\n"); 
		video_device_release(cam->video_dev); 
		kfree(v4l2_dev); 
		return -ENODEV; 
	} 
	cam->video_dev->v4l2_dev = v4l2_dev; 

	init_waitqueue_head(&cam->enc_queue); 
	init_waitqueue_head(&cam->still_queue); 

	/* setup cropping */ 
	cam->crop_bounds.left = 0; 
	cam->crop_bounds.width = 640; 
	cam->crop_bounds.top = 0; 
	cam->crop_bounds.height = 480; 
	cam->crop_current = cam->crop_defrect = cam->crop_bounds; 
	ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, 
				cam->crop_current.height, cam->csi); 
	ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, 
				cam->crop_current.top, cam->csi); 
	cam->streamparm.parm.capture.capturemode = 0; 

	cam->standard.index = 0; 
	cam->standard.id = V4L2_STD_UNKNOWN; 
	cam->standard.frameperiod.denominator = 30; 
	cam->standard.frameperiod.numerator = 1; 
	cam->standard.framelines = 480; 
	cam->standard_autodetect = true; 
	cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
	cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod; 
	cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 
	cam->overlay_on = false; 
	cam->capture_on = false; 
	cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY; 

	cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2; 
	cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2; 
	cam->v2f.fmt.pix.width = 288; 
	cam->v2f.fmt.pix.height = 352; 
	cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; 
	cam->win.w.width = 160; 
	cam->win.w.height = 160; 
	cam->win.w.left = 0; 
	cam->win.w.top = 0; 

	cam->ipu_id = ipu_id; 
	cam->csi = csi_id; 
	cam->mclk_source = mclk_source; 
	cam->mclk_on[cam->mclk_source] = false; 

	cam->enc_callback = camera_callback;     //設定回撥函式,這個函式很重要
	init_waitqueue_head(&cam->power_queue); 
	spin_lock_init(&cam->queue_int_lock); 
	spin_lock_init(&cam->dqueue_int_lock); 
 
	cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); 
	cam->self->module = THIS_MODULE; 
	sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi); 
	cam->self->type = v4l2_int_type_master; 
	cam->self->u.master = &mxc_v4l2_master; 

	return 0; 
}

在這個函式中,對cam_data結構體裡面的一些變數進行了初始化,設定了一些預設值。同時注意這個函式的最後幾步操作,它為cam->self分配了記憶體,然後設定cam->self->typev4l2_int_type_master,指定了cam->self->u.master&mxc_v4l2_master。這幾步有什麼作用呢?這個在2.2中分析。

2.2 v4l2_int_device_register數分析

int v4l2_int_device_register(struct v4l2_int_device *d) 
{ 
	if (d->type == v4l2_int_type_slave) 
		sort(d->u.slave->ioctls, d->u.slave->num_ioctls, 
		     sizeof(struct v4l2_int_ioctl_desc), 
		     &ioctl_sort_cmp, NULL); 
	mutex_lock(&mutex); 
	list_add(&d->head, &int_list); 
	v4l2_int_device_try_attach_all(); 
	mutex_unlock(&mutex); 

	return 0; 
} 
EXPORT_SYMBOL_GPL(v4l2_int_device_register);

在這個函式中,首先通過list_add將這個cam->self結構體新增到int_list連結串列中,最重要的操作就是v4l2_int_device_try_attach_all()函式

void v4l2_int_device_try_attach_all(void) 
{ 
	struct v4l2_int_device *m, *s; 

	list_for_each_entry(m, &int_list, head) { 
		if (m->type != v4l2_int_type_master) 
			continue; 

		list_for_each_entry(s, &int_list, head) { 
			if (s->type != v4l2_int_type_slave) 
				continue; 

			/* Slave is connected? */ 
			if (s->u.slave->master) 
				continue; 

			/* Slave wants to attach to master? */ 
			if (s->u.slave->attach_to[0] != 0 
			    && strncmp(m->name, s->u.slave->attach_to, 
				       V4L2NAMESIZE)) 
				continue; 

			if (!try_module_get(m->module)) 
				continue; 

			s->u.slave->master = m; 
			if (m->u.master->attach(s)) { 
				s->u.slave->master = NULL; 
				module_put(m->module); 
				continue; 
			} 
		} 
	} 
} 
EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all);

這個函式首先從int_list連結串列中取出type型別為v4l2_int_type_master的結構體儲存在m中,然後再取出type型別為v4l2_int_type_slave的結構體儲存在s中。然後判斷s->u.slave->master是否存在,如果存在的話就跳過繼續尋找。這個變數就代表了這個slave裝置已經設定了它所對應的master。那麼這個變數是在哪設定的呢?繼續看這個函式,就在後面設定了:s->u.slave->master= m;

這兩個list_for_each_entry的最終結果就是找到master裝置和第一個沒有設定masterslave裝置。

然後將這個slave裝置的u.slave->master設定成找到的master裝置m,然後呼叫mu.master->atach(s)函式,這個函式就是在init_camera_struct函式最後設定的:cam->self->u.master= &mxc_v4l2_master;

static struct v4l2_int_master mxc_v4l2_master = { 
	.attach = mxc_v4l2_master_attach, 
	.detach = mxc_v4l2_master_detach, 
};

所以最終會呼叫到mxc_v4l2_master_attach函式,同時這個函式的行參是這個slave裝置s

static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) 
{ 
	cam_data *cam = slave->u.slave->master->priv; 
	struct v4l2_format cam_fmt; 
	int i; 
	struct sensor_data *sdata = slave->priv; 

	pr_debug("In MVC: mxc_v4l2_master_attach\n"); 
	pr_debug("   slave.name = %s\n", slave->name); 
	pr_debug("   master.name = %s\n", slave->u.slave->master->name); 

	if (slave == NULL) { 
		pr_err("ERROR: v4l2 capture: slave parameter not valid.\n"); 
		return -1; 
	} 

	if (sdata->csi != cam->csi) { 
		pr_debug("%s: csi doesn't match\n", __func__); 
		return -1; 
	} 

	cam->sensor = slave; 

	if (cam->sensor_index < MXC_SENSOR_NUM) { 
		cam->all_sensors[cam->sensor_index] = slave; 
		cam->sensor_index++; 
	} else { 
		pr_err("ERROR: v4l2 capture: slave number exceeds " 
		       "the maximum.\n"); 
		return -1; 
	} 

	for (i = 0; i < cam->sensor_index; i++) { 
		vidioc_int_dev_exit(cam->all_sensors[i]); 
		vidioc_int_s_power(cam->all_sensors[i], 0); 
	} 
 
	cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
	vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); 

	/* Used to detect TV in (type 1) vs. camera (type 0)*/ 
	cam->device_type = cam_fmt.fmt.pix.priv; 

	/* Set the input size to the ipu for this device */ 
	cam->crop_bounds.top = cam->crop_bounds.left = 0; 
	cam->crop_bounds.width = cam_fmt.fmt.pix.width; 
	cam->crop_bounds.height = cam_fmt.fmt.pix.height; 

	/* This also is the max crop size for this device. */ 
	cam->crop_defrect.top = cam->crop_defrect.left = 0; 
	cam->crop_defrect.width = cam_fmt.fmt.pix.width; 
	cam->crop_defrect.height = cam_fmt.fmt.pix.height; 

	/* At this point, this is also the current image size. */ 
	cam->crop_current.top = cam->crop_current.left = 0; 
	cam->crop_current.width = cam_fmt.fmt.pix.width; 
	cam->crop_current.height = cam_fmt.fmt.pix.height; 

	return 0; 
}

在這個函式中設定了cam->sensor= slave;然後又設定了crop的一些引數。而這些crop引數都是根據slave結構體來設定的。在init_camera_struct函式中只是給了這些crop引數一些初始預設值,在這個函式中才是根據真正從slave裝置中獲取到的引數來填充crop引數。

(二)slave的註冊過程

3. 以上是master的註冊過程,再來看看slave的註冊過程。以ov5640.c為例:

3.1 module_i2c_driver(ov5640_i2c_driver);

static struct i2c_driver ov5640_i2c_driver = { 
	.driver = { 
		  .owner = THIS_MODULE, 
		  .name  = "ov564x", 
		  }, 
	.probe  = ov5640_probe, 
	.remove = ov5640_remove, 
	.id_table = ov5640_id, 
}; 

之後就會呼叫到ov5640_probe函式。

3.2 ov5640_probe函式

static int ov5640_probe(struct i2c_client *client, 
			const struct i2c_device_id *id) 
{ 
	struct pinctrl *pinctrl; 
	struct device *dev = &client->dev; 
	int retval; 
	u8 chip_id_high, chip_id_low; 

	/* ov5640 pinctrl */ 
	pinctrl = devm_pinctrl_get_select_default(dev); 
	if (IS_ERR(pinctrl)) { 
		dev_err(dev, "setup pinctrl failed\n"); 
		return PTR_ERR(pinctrl); 
	} 

	/* request power down pin */ 
	pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); 
	if (!gpio_is_valid(pwn_gpio)) { 
		dev_err(dev, "no sensor pwdn pin available\n"); 
		return -ENODEV; 
	} 
	retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, 
					"ov5640_pwdn"); 
	if (retval < 0) 
		return retval; 

	/* request reset pin */ 
	rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); 
	if (!gpio_is_valid(rst_gpio)) { 
		dev_err(dev, "no sensor reset pin available\n"); 
		return -EINVAL; 
	} 
	retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, 
					"ov5640_reset"); 
	if (retval < 0) 
		return retval; 

	/* Set initial values for the sensor struct. */ 
	memset(&ov5640_data, 0, sizeof(ov5640_data)); 
	ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); 
	if (IS_ERR(ov5640_data.sensor_clk)) { 
		dev_err(dev, "get mclk failed\n"); 
		return PTR_ERR(ov5640_data.sensor_clk); 
	} 

	retval = of_property_read_u32(dev->of_node, "mclk", 
					&ov5640_data.mclk); 
	if (retval) { 
		dev_err(dev, "mclk frequency is invalid\n"); 
		return retval; 
	} 

	retval = of_property_read_u32(dev->of_node, "mclk_source", 
					(u32 *) &(ov5640_data.mclk_source)); 
	if (retval) { 
		dev_err(dev, "mclk_source invalid\n"); 
		return retval; 
	} 

	retval = of_property_read_u32(dev->of_node, "csi_id", 
					&(ov5640_data.csi)); 
	if (retval) { 
		dev_err(dev, "csi_id invalid\n"); 
		return retval; 
	} 

	clk_prepare_enable(ov5640_data.sensor_clk); 

	ov5640_data.io_init = ov5640_reset; 
	ov5640_data.i2c_client = client; 
	ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV; 
	ov5640_data.pix.width = 640; 
	ov5640_data.pix.height = 480; 
	ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | 
					   V4L2_CAP_TIMEPERFRAME; 
	ov5640_data.streamcap.capturemode = 0; 
	ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS; 
	ov5640_data.streamcap.timeperframe.numerator = 1; 

	ov5640_regulator_enable(&client->dev); 

	ov5640_reset(); 

	ov5640_power_down(0); 

	retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high); 
	if (retval < 0 || chip_id_high != 0x56) { 
		clk_disable_unprepare(ov5640_data.sensor_clk); 
		pr_warning("camera ov5640 is not found\n"); 
		return -ENODEV; 
	} 
	retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low); 
	if (retval < 0 || chip_id_low != 0x40) { 
		clk_disable_unprepare(ov5640_data.sensor_clk); 
		pr_warning("camera ov5640 is not found\n"); 
		return -ENODEV; 
	} 

	ov5640_power_down(1); 

	clk_disable_unprepare(ov5640_data.sensor_clk); 

	ov5640_int_device.priv = &ov5640_data; 
	retval = v4l2_int_device_register(&ov5640_int_device); 

	pr_info("camera ov5640 is found\n"); 
	return retval; 
}

這個函式主要是設定ov5640_data結構體的一些值,通過幾個of函式來獲取到的資訊填充到這個結構體中。然後最後呼叫到了v4l2_int_device_register這個函式。這個函式同樣在上面的mxc_v4l2_probe中呼叫了。不同的是註冊的ov5640_int_device結構體,在mxc_v4l2_probe中註冊的是cam->self結構體,這兩個結構體都是v4l2_int_device型別的:

static struct v4l2_int_device ov5640_int_device = { 
	.module = THIS_MODULE, 
	.name = "ov564x", 
	.type = v4l2_int_type_slave, 
	.u = { 
		.slave = &ov5640_slave, 
	}, 
};

可以看到,這個ov5640_int_device構體中設定的type型別是v4l2_int_type_slave,同時u設定的是slave。再次對比init_camera_struct函式中master是怎麼設定的:

	cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); 
	cam->self->module = THIS_MODULE; 
	sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi); 
	cam->self->type = v4l2_int_type_master; 
	cam->self->u.master = &mxc_v4l2_master;

然後呼叫v4l2_int_device_register函式,在裡面通過list_add將這個裝置新增到int_list連結串列中,所以這個連結串列中的裝置既包括master裝置,同時也包括slave裝置。在這裡,每新增進去一個裝置,這個int_list連結串列中都會增加一個slave裝置,同時master裝置就是cam->self結構體。從這個連結串列中取出裝置的話,需要根據這個type型別來區分。

之後在v4l2_int_device_register函式繼續呼叫v4l2_int_device_try_attach_all()函式,這個函式在上面已經分析很清楚了,會從int_list連結串列找到master裝置和第一個沒有設定masterslave裝置,然後將這個slave裝置的u.slave->master設定成找到的master裝置m,然後呼叫mu.master->attach(s)函式,完成匹配過程。

這個v4l2_int_device_try_attach_all()函式在master裝置或者slave設備註冊進連結串列的時候,都會呼叫到,都會互相去匹配。

4.當在應用程式執行open的時候,再次通過vidioc_int_g_fmt_cap函式獲取了cam->sensor的資訊,此時經過上面那些步驟,cam->sensor已經指向了找到的slave裝置。重新設定crop的一些值,在這裡通過ipu_csi_set_window_sizeipu_csi_set_window_pos等操作將這些值寫到了暫存器中。

關於這個流程,我畫了一個思維導圖來輔助理解:











相關推薦

2. masterslave匹配過程

(一)master的註冊過程 1. 首先來看看master的註冊過程,在mxc_v4l2_capture.c檔案中,從module_init(camera_init)函式開始,在camera_init函式中通過 err= platform_driver_register(&

面試題(redis masterslave是怎麼實現資料同步的)

Redis的主從同步機制可以確保redis的master和slave之間的資料同步。按照同步內容的多少可以分為全同步和部分同步;按照同步的時機可以分為slave剛啟動時的初始化同步和正常執行過程中的資料修改同步;本文將對這兩種機制的流程進行分析。 全備份過程中,在sla

Jenkins : 安裝 master slave

目錄 Jenkins 是一個可擴充套件的持續整合引擎。主要用於持續、自動地構建、測試軟體專案。本文介紹在 windows 平臺上安裝 Jenkins master 和 slave。 安裝 master 請從 Jenkins 的官網下載安裝包,直接執行,一路 "next" 就可以了。安裝包執行完成後會啟動你機

輸出redis cluster 主從的對應關係,如果同一個主從關係的masterslave在同一個node節點上,在輸出的對應關係末尾輸出提示

  需求:輸出redis cluster 主從的對應關係,如果同一個主從關係的master和slave在同一個node節點上,在輸出的對應關係末尾輸出提示。 為什麼會有這樣的需求呢?在重新搭建redis cluster的時候,建立叢集期間,發現: redis-trib.rb create --

jenkins 配置master slave實踐

今天在自己本機上配置了jenkins主從節點,現記錄如下: 1.瀏覽器開啟http://localhost:8080/jenkins/,登入jenkins後臺,點選系統管理,進入系統管理介面 2. 點選 “管理節點” ,進入節點管理介面 3. 點選 “新建節點” 

閱讀Hadoop 原始碼最重要的就是明白rpc機制,client與mastermasterslave的通訊

本部落格微信公共賬號:hadoop123(微訊號為:hadoop-123),分享hadoop技術內幕,hadoop最新技術進展,釋出hadoop相關職位和求職資訊,hadoop技術交流聚會、講座以及會議等。二維碼如下: 個人談談閱讀hadoop原始碼的經驗。

mysql master slave的replication

.主從mysql server的工作原理:(如圖及其過程分析) 過程:   Mysql的複製(replication)是一個非同步的複製,從一個Mysql instace(稱之為Master)複製到另一個Mysql instance(稱之Slave)。實現整個複製操作主

WebService—CXF整合Spring實現接口發布調用過程2

creat tco win [] exception onf del tac xml配置 一、CXF整合Spring實現接口發布 發布過程如下: 1、引入jar包(基於maven管理) <!-- cxf --> <dependency>

2. MySql更新語句執行過程redologbinlog

一句update的語句:Update T set C=c+1 where id = 2; 和查詢語句一樣會走一遍如下的流程:   與查詢語句不一樣的是,更新語句設計上有兩個重要的模組:redo log 和 binlog 一、重要日誌模組: redo log

Jenkins : 分佈安裝 master slave

目錄 安裝 master 安裝 slave 設定 master 與 slave 的通訊方式 新增 slave 配置 在 salve 上安裝 jre 安裝並配置 Jenkins salve Jenkins 是一個可擴充套件的持續整合引擎。主要用於持續、自動地構建、測試軟體專案。本文介紹在

python3.6 python 2.7版本安裝pycrypto過程及問題(不需要安裝vs)

方式 導入模塊 ges pac scrip http color sta 下一步 python安裝pycrypto真的是踩到很多坑,說起來一把辛酸淚,好在最後成功解決了,總結了一下遇到的問題,寫出來與大家共享 首先要明白的是crypto和pycrypto是兩個不同的模塊

Linux裝置驅動的匹配過程

一、bus_type結構體及涉及的函式: (1)bus_type結構體 struct bus_type { const char *name; const char *dev_name; struct device *dev_root; struct

我自己配置Mysql Master/Slave過程

B伺服器上的www.sns.com.err [[email protected] var]# cat www.sns.com.err 080822 15:00:01  mysqld started 080822 15:00:01 [Warning] The syntax for replicatio

iOS稽核5.2.13.2拒絕解決過程

The seller and company names associated with your app do not reflect the name of a financial institution in the app or its metadata, as required by Guidel

Qt入門之基礎篇 ( 二 ) :Qt項目建立、編譯、運行發布過程解析

qt 5 對話 讓我 進度 qmake ctr deploy 設定 設置 轉載請註明出處:CN_Simo。 題解:   本篇內容主講Qt應用從創建到發布的整個過程,旨在幫助讀者能夠快速走進Qt的世界。   本來計劃是講解Qt源碼靜態編譯,如此的話讀者可能並不能清楚地知

[C#學習筆記之異步編程模式2]BeginInvokeEndInvoke方法 (轉載)

cti otf 函數返回 編程模式 catch 數值 gin 單線程 blog 為什麽要進行異步回調?眾所周知,普通方法運行,是單線程的,如果中途有大型操作(如:讀取大文件,大批量操作數據庫,網絡傳輸等),都會導致方法阻塞,表現在界面上就是,程序卡或者死掉,界面元素不動了,

【轉】1.2 CDN的基本工作過程

過程 業務 用戶服 fit lan 進行 發的 傳統 告訴 1.2 CDN的基本工作過程 使用CDN會極大地簡化網站的系統維護工作量,網站維護人員只需將網站內容註入CDN的系統,通過CDN部署在各個物理位置的服務器進行全網分發,就可以實現跨運營商、跨地域的用戶覆蓋。由於C

EF執行SQL語句存儲過程

div spa rip mman code rom {0} base result EF雖然使用對象化的方式避免了我們寫SQL,但是對於部分SQL,例如需要復雜的查詢、執行插入和刪除等可以操作,直接執行SQL可以減少減少性能上的損失。 使用EF執行SQL可以通過Execut

1.3—一個典型的JAVA程序的編寫運行過程

java程序JAVA語言應用範圍 桌面應用編程 WEB客戶端編程 WEB服務器編程 手機編程 機器人編程 第一個JAVA程序JAVA開發環境搭建 下載:Download J2SDK (Java 2 Software Development Kit) from http://java.sun.com 安裝

ELF文件的格式加載過程

pos ram glob ios sel 執行 log iar section http://blog.csdn.net/lingfong_cool/article/details/7832896 (一) ELF 文件的格式 ELF 文件類型 (1) 可重定位文