<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Vietnamese Developers' Blog</title>
	<atom:link href="http://openandfree.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://openandfree.wordpress.com</link>
	<description>There are 10 types of people in the world: Those who know the binary system and those who don't</description>
	<lastBuildDate>Sat, 22 Nov 2008 00:47:09 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='openandfree.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://www.gravatar.com/blavatar/caa4770bbad32f97d95cfd98b414dfc1?s=96&#038;d=http://s.wordpress.com/i/buttonw-com.png</url>
		<title>Vietnamese Developers' Blog</title>
		<link>http://openandfree.wordpress.com</link>
	</image>
			<item>
		<title>Gọi repaint() nhiều lần trong JFrame và JApplet</title>
		<link>http://openandfree.wordpress.com/2008/11/22/g%e1%bb%8di-repaint-nhi%e1%bb%81u-l%e1%ba%a7n-trong-jframe-va-japplet/</link>
		<comments>http://openandfree.wordpress.com/2008/11/22/g%e1%bb%8di-repaint-nhi%e1%bb%81u-l%e1%ba%a7n-trong-jframe-va-japplet/#comments</comments>
		<pubDate>Sat, 22 Nov 2008 00:47:09 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[frame]]></category>
		<category><![CDATA[repaint()]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=78</guid>
		<description><![CDATA[Khi học Java, chúng ta thường bắt gặp những chương trình đơn giản về animation trong các sách dạy AWT và Swing, chẳng hạn như chương trình sau đây:
import java.awt.*;
import javax.swing.*;

public class Animation1 {

  public static void main( String[] args ) {

     Animation1 gui = new Animation1();
    [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=93&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p style="text-align:justify;">Khi học Java, chúng ta thường bắt gặp những chương trình đơn giản về animation trong các sách dạy AWT và Swing, chẳng hạn như chương trình sau đây:</p>
<pre>import java.awt.*;
import javax.swing.*;

public class Animation1 {

  public static void main( String[] args ) {

     Animation1 gui = new Animation1();
     gui.go();

  }

  private void go() {

     JFrame frame = new JFrame();
     frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

     panel_ = new MyPanel();
     frame.getContentPane().add( panel_, BorderLayout.CENTER );

     frame.setSize( 500, 500 );
     frame.setVisible( true );

     for( int i = 0; i &lt; 400; i++ ) {

        x_++;
        y_++;

        panel_.repaint();

        try {
          Thread.sleep( 10 );
        } catch( Exception ex ) { }

     }

  }

  class MyPanel extends JPanel {

     public void paintComponent( Graphics g ) {

         g.setColor( Color.white );
         g.fillRect( 0, 0, this.getWidth(), this.getHeight() );

         g.setColor( Color.green );
         g.fillOval( x_, y_, 40, 40 );

     }

  }

  private JPanel panel_;
  private int x_ = 0;
  private int y_ = 0;

}</pre>
<p style="text-align:justify;">Chương trình này vẽ ra một vòng tròn trên một panel, tính toán lại tọa độ của nó rồi gọi repaint() để vẽ lại vòng tròn. Phương thức repaint() yêu cầu các component trên frame tự vẽ lại.Thao tác vẽ lại liên tục với các vị trí khác nhau sẽ tạo ra cảm giác vòng tròn chạy trên panel. Câu lệnh Thread.sleep(10) làm giảm tốc độ di chuyển của vòng tròn giúp người dùng dễ theo dõi.</p>
<p style="text-align:justify;">Chúng ta thử sáng tạo thêm một chút bằng cách thêm vào frame một button dùng kể kích hoạt animation (học event handler và inner class luôn). Animation sẽ được kích hoạt khi người dùng ấn nút &#8220;Start animation&#8221;. Chương trình được cải tiến như sau:<span id="more-93"></span></p>
<pre>import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Animation2 {

    public static void main( String[] args ) {

	Animation2 gui = new Animation2();
	gui.go();

    }

    private void go() {

	JFrame frame = new JFrame();
	frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

	panel_ = new MyPanel();
	frame.getContentPane().add( panel_, BorderLayout.CENTER );

	button_ = new JButton( "start animation" );
	button_.addActionListener( new StartAnimationAction() );
	frame.getContentPane().add( button_, BorderLayout.SOUTH );

	frame.setSize( 500, 500 );
	frame.setVisible( true );

    }

    class MyPanel extends JPanel {

	public void paintComponent( Graphics g ) {

	    g.setColor( Color.white );
	    g.fillRect( 0, 0, this.getWidth(), this.getHeight() );

	    g.setColor( Color.green );
	    g.fillOval( x_, y_, 40, 40 );

	}

    }

    class StartAnimationAction implements ActionListener {

	public void actionPerformed( ActionEvent e ) {

	    for( int i = 0; i &lt; 400; i++ ) {

		x_++;
		y_++;

		panel_.repaint();

		try {
		  Thread.sleep( 10 );
		} catch( Exception ex ) { }

	    }

	}

    }

  private JPanel panel_;
  private JButton button_;
  private int x_ = 0;
  private int y_ = 0;

}</pre>
<p style="text-align:justify;">Phiên bản mới nhìn qua thì rất &#8220;đẹp&#8221;, nhưng thực ra nó không chạy đúng như chúng ta mong đợi!  Chúng ta không nhìn thấy vòng tròn di chuyển trên panel mà chỉ thấy nó nhảy  từ vị trí ban đầu đến vị trí cuối cùng, các trạng thái trung gian đã bị mất. Vậy đâu là nguyên nhân của hành vi kì lạ này?</p>
<p style="text-align:justify;">Thực ra, khi chúng ta đặt phương thức repaint() vào trong một vòng lặp, AWT sẽ trộn các lời gọi repaint() lại với nhau và chỉ có lời gọi repaint() cuối cùng được thực hiện. Bởi vậy chúng ta không thể nhìn thấy các trạng thái trung gian của vòng tròn trên panel.</p>
<p style="text-align:justify;">Vậy làm thế nào giải quyết vấn đề này? Một giải pháp là đưa các lời gọi repaint() sang một thread khác như sau:</p>
<pre>import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Animation3 {

    public static void main( String[] args ) {

	Animation3 gui = new Animation3();
	gui.go();

    }

    private void go() {

	JFrame frame = new JFrame();
	frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

	panel_ = new MyPanel();
	frame.getContentPane().add( panel_, BorderLayout.CENTER );

	button_ = new JButton( "start animation" );
	button_.addActionListener( new StartAnimationAction() );
	frame.getContentPane().add( button_, BorderLayout.SOUTH );

	frame.setSize( 500, 500 );
	frame.setVisible( true );

    }

    class MyPanel extends JPanel {

	public void paintComponent( Graphics g ) {

	    g.setColor( Color.white );
	    g.fillRect( 0, 0, this.getWidth(), this.getHeight() );

	    g.setColor( Color.green );
	    g.fillOval( x_, y_, 40, 40 );

	}

    }

    class StartAnimationAction implements ActionListener, Runnable {

	public void actionPerformed( ActionEvent e ) {

	    Thread thread = new Thread( this );
	    thread.start();

	}

	public void run() {

	    for( int i = 0; i &lt; 400; i++ ) {

		x_++;
		y_++;

		panel_.repaint();

		try {
		  Thread.sleep( 10 );
		} catch( Exception ex ) { }

	    }

	}

    }

  private JPanel panel_;
  private JButton button_;
  private int x_ = 0;
  private int y_ = 0;

}</pre>
<p>Bây giờ thì chương trình của chúng ta đã chạy ngon lành. Để giải thích cặn kẽ về vấn đề gọi repaint() nhiều lần có lẽ cần đến những hiểu biết nhất định về thread trong Java. Bởi vậy &#8220;tác giả&#8221;, với trình độ còn rất hạn chế, đành tạm thời hài lòng với giải pháp nói trên <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
<p>Tài liệu tham khảo: <a href="http://www.ryerson.ca/~dgrimsha/courses/cps840/repaint.html">The repaint() method and the GUI thread</a></p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/93/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=93&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/11/22/g%e1%bb%8di-repaint-nhi%e1%bb%81u-l%e1%ba%a7n-trong-jframe-va-japplet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
		<item>
		<title>vim plugin for NetBeans</title>
		<link>http://openandfree.wordpress.com/2008/11/20/vim-plugin-for-netbeans/</link>
		<comments>http://openandfree.wordpress.com/2008/11/20/vim-plugin-for-netbeans/#comments</comments>
		<pubDate>Thu, 20 Nov 2008 01:09:34 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[NetBeans]]></category>
		<category><![CDATA[vim plugin]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=77</guid>
		<description><![CDATA[Khi đã quen viết code trên vi/vim rồi thì chuyển sang editor khác thật khó chịu. Phiền hà nhất là các thao tác di chuyển con trỏ (phải dùng các phím mũi tên lên xuống rất mất thời gian), xóa một từ hoặc một dòng (phải giữ Backspace mỏi cả tay), nhảy giữa các từ&#8230;Tóm [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=92&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Khi đã quen viết code trên vi/vim rồi thì chuyển sang editor khác thật khó chịu. Phiền hà nhất là các thao tác di chuyển con trỏ (phải dùng các phím mũi tên lên xuống rất mất thời gian), xóa một từ hoặc một dòng (phải giữ Backspace mỏi cả tay), nhảy giữa các từ&#8230;Tóm lại với những ai đã quen lập trình trên vi/vim thì việc phải chuyển sang các IDE khác sẽ không khác gì cực hình.</p>
<p>Giải pháp cho vấn đề này là cài đặt vim plugin cho IDE đang sử dụng. Một IDE đang được sử dụng rộng rãi là NetBeans. Để cài đặt vim plugin cho NetBeans, vào link dưới đây để tải về file cài đặt:</p>
<p><a href="http://sourceforge.net/projects/viex/">http://sourceforge.net/projects/viex/</a></p>
<p>Giải nén để nhận được file start-module-myvim.nbm. Từ menu của NetBeans, chọn Tools-&gt;Plugins-&gt;Downloaded-&gt;Add Plugins rồi chọn file start-module-myvim.nbm. Chúng ta sẽ thấy biểu tượng của vim nằm bên cạnh các icon khác như trong hình dưới đây. (Chú ý hình dáng của con trỏ, chúng ta đang ở chế độ lệnh của vim).</p>
<p><img class="alignnone" src="http://farm4.static.flickr.com/3173/3044058495_7b05091433_o.jpg" alt="" width="600" height="400" /></p>
<p>Bây giờ thì chúng ta đã có thể tận hưởng sự tiện lợi trong việc viết code bằng vim kết hợp với những tính năng phức tạp cung cấp bởi NetBeans. Tuy nhiên, plugin này vẫn chưa hỗ trợ một số thao tác edit trong vim như</p>
<p>- Không tự động indent khi xuống dòng bằng phím o.</p>
<p>- Không hỗ trợ shift-o</p>
<p>- Không hỗ trợ ctrl-r (redo).</p>
<p>- Không hỗ trợ xoá nhiều từ liền nhau (d 2 w, d 3 w, d $,&#8230;)</p>
<p>Tuy nhiên với những tính năng hiện có của plugin này thì công việc lập trình trên NetBeans đã trở nên thú vị hơn rất nhiều. Hi vọng những khiếm khuyết nói trên sẽ được khắc phục trong những phiên bản sau.</p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/92/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/92/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/92/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=92&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/11/20/vim-plugin-for-netbeans/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>

		<media:content url="http://farm4.static.flickr.com/3173/3044058495_7b05091433_o.jpg" medium="image" />
	</item>
		<item>
		<title>Các mô hình phát triển phần mềm</title>
		<link>http://openandfree.wordpress.com/2008/10/26/cac-mo-hinh-phat-tri%e1%bb%83n-ph%e1%ba%a7n-m%e1%bb%81m/</link>
		<comments>http://openandfree.wordpress.com/2008/10/26/cac-mo-hinh-phat-tri%e1%bb%83n-ph%e1%ba%a7n-m%e1%bb%81m/#comments</comments>
		<pubDate>Sun, 26 Oct 2008 12:51:27 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[Công nghệ phần mềm]]></category>
		<category><![CDATA[evolutionary]]></category>
		<category><![CDATA[incremental]]></category>
		<category><![CDATA[software engineering]]></category>
		<category><![CDATA[software process models]]></category>
		<category><![CDATA[waterfall]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=63</guid>
		<description><![CDATA[Bài viết này trình bày những mô hình phát triển phần mềm cơ bản.
Một dự án phát triển phần mềm thường trải qua các hoạt động sau đây:
- Phân tích yêu cầu.
- Thiết kế và lập trình.
- Test.
- Bảo trì.
Mỗi mô hình phát triển phần mềm đưa ra một cách tổ chức sắp xếp khác [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=63&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p class="MsoNormal" style="text-align:justify;">Bài viết này trình bày những mô hình phát triển phần mềm cơ bản.</p>
<p class="MsoNormal" style="text-align:justify;">Một dự án phát triển phần mềm thường trải qua các hoạt động sau đây:</p>
<p class="MsoNormal" style="margin-left:.5in;text-indent:-.25in;text-align:justify;"><!--[if !supportLists]--><span>-<span style="font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;font-family:&quot;"> </span></span><!--[endif]-->Phân tích yêu cầu.</p>
<p class="MsoNormal" style="margin-left:.5in;text-indent:-.25in;text-align:justify;"><!--[if !supportLists]--><span>-<span style="font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;font-family:&quot;"> </span></span><!--[endif]-->Thiết kế và lập trình.</p>
<p class="MsoNormal" style="margin-left:.5in;text-indent:-.25in;text-align:justify;"><!--[if !supportLists]--><span>-<span style="font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;font-family:&quot;"> </span></span><!--[endif]-->Test.</p>
<p class="MsoNormal" style="margin-left:.5in;text-indent:-.25in;text-align:justify;"><!--[if !supportLists]--><span>-<span style="font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;font-family:&quot;"> </span></span><!--[endif]-->Bảo trì.</p>
<p class="MsoNormal" style="text-align:justify;">Mỗi mô hình phát triển phần mềm đưa ra một cách tổ chức sắp xếp khác nhau của các hoạt động này.</p>
<h3 class="MsoNormal" style="text-align:justify;"><strong>1. Mô hình thác nước (waterfall)</strong></h3>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Đây là mô hình phát triển phần mềm cổ điển nhất. Mô hình này đề nghị các hoạt động được tiến hành như các giai đoạn tách biệt, giai đoạn sau sẽ không bắt đầu chừng nào giai đoạn trước chưa hoàn thành. Sản phẩm đầu ra của giai đoạn trước trở thành đầu vào của giai đoạn sau.<img class="alignnone" src="http://farm4.static.flickr.com/3235/2974435882_02faa55749_o.jpg" alt="" width="512" height="384" /></p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><!--[if gte vml 1]&gt;                    &lt;![endif]-->Những mũi tên ngược từ dưới lên trên cho thấy những sai lầm ở giai đoạn trước có thể được phát hiện ở giai đoạn sau và đòi hỏi việc quay ngược lên để làm lại giai đoạn trước. Tuy nhiên ,hoạt động quay lui này chỉ nên được coi là các ngoại lệ mà thôi.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Mô hình thác nước có ưu điểm là dễ quản lí. Đây chính là mô hình ưa thích của các nhà quản lí dự án. Thời gian hoàn thành dự án thường được dự báo với độ chính xác hơn so với các mô hình khác. Các tài liệu đầu ra của từng giai đoạn cũng được xây dựng đầy đủ và hệ thống hơn. Tuy nhiên mô hình này có một số nhược điểm lớn là:</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Mô hình đòi hòi một bản yêu cầu (requirement) đầy đủ và chính xác từ phía khách hàng.</b> Yêu cầu này hiếm khi đạt được bởi khách hàng ít khi xác định được chính xác họ muốn gì ở ngay giai đoạn đầu của dự án, sở thích của họ cũng thay đổi khá thường xuyên. Việc làm lại các giai đoạn ban đầu để đáp ứng sự thay đổi của khách hàng thường mất rất nhiều công sức và phá vỡ cấu trúc của phần mềm.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Khách hàng cần phải kiên nhẫn. </b>Họ chỉ được tham gia vào dự án ở giai đoạn phân tích yêu cầu và test mà thôi. Ngoài ra, sản phẩm sẽ chỉ được bàn giao khi tất cả các công việc liên quan đã được hoàn thành.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Mô hình thác nước chỉ nên được sử dụng khi đội dự án đã có kinh nghiệm, yêu cầu từ khách hàng được xác  định rõ ngay từ đầu và ít có khả năng thay đổi. Hiện nay, mô hình thác nước vẫn được sử dụng rộng rãi do tính gần gũi với các mô hình phát triển trong các ngành kĩ thuật khác.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><span id="more-63"></span></p>
<h3 class="MsoNormal" style="text-align:justify;"><strong>2. Mô hình tiến hóa (Evolutionary)</strong></h3>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Mô hình tiến hóa được đưa ra nhằm giải quyết những khó khăn gây ra do yêu cầu của khách hàng không rõ ràng hoặc hay thay đổi. Ý tưởng của mô hình này là phát triển phẩn mềm qua nhiều phiên bản, mỗi phiên bản được đưa ra lấy ý kiến khách hàng, được sửa chữa, làm mịn cho đến khi đạt được phiên bản hoàn chỉnh.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><img class="alignnone" src="http://farm4.static.flickr.com/3137/2973583173_9659fbf42f_o.jpg" alt="" width="512" height="384" /></p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Mô hình tiến hóa: Các phiên bản 1, 2, 3 có thể rất khác nhau!</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><!--[if gte vml 1]&gt;  &lt;![endif]--><!--[if !vml]--><!--[endif]--></p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Hai kiểu mô hình tiến hóa cơ bản là:</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Khám phá và phát triển (exploratory development):</b> Đội dự án sẽ làm việc cùng khách hàng để khám phá các yêu cầu của họ. Dự án sẽ bắt đầu trước tiên với những yêu cầu đã rõ ràng. Các đặc tính khác sẽ được thêm vào dần dần dựa trên đề nghị của khách hàng.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Làm bản mẫu để “vứt đi” (throwaway prototyping):</b> Mô hình này được áp dụng cho giai đoạn phân tích yêu cầu. Theo đó, đội dự án sẽ làm các bản mẫu (prototype) để lấy ý kiến khách hàng nhằm kiểm chứng và làm rõ các yêu cầu chưa rõ ràng. Khi đã có một bản yêu cầu hoàn chỉnh, giai đoạn phát triển tiếp theo có thể sử dụng mô hình thác nước.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Mô hình tiến hóa cho phép khách hàng tham gia sâu hơn vào quá trình phát triển, nhờ đó sản phẩm cuối cùng thường phản ánh chính xác mong muốn của khách hàng. Tuy nhiên, mô hình này cũng có các nhược điểm là:</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Khó khăn trong việc thiết kế: </b>Việc phát triển qua nhiều phiên bản có thể phá vỡ kiến trúc tổng thể của phần mềm.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Khó khăn trong việc quản lí:</b> Các nhà quản lí thích nhìn thấy sản phẩm làm ra trong từng gian đoạn để tiện cho việc quản lí tiến độ. Ngoài ra, các tài liệu mô tả cho từng phiên bản thường không được lập đầy đủ.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">-<b> Khó khăn do khách hàng gây ra:</b> Khách hàng có thể nhầm tưởng rằng một bản mẫu có thể tốt gần như sản phẩm thật. Trong thực tế từ bản mẫu đến sản phẩm cuối cùng là một khảng cách xa. Ngoài ra khách hàng có xu hướng đưa thêm vào những yêu cầu không cần thiết.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Khó khăn về địa lý:</b> Mô hình tiến hóa đòi hỏi đội dự án phải ngồi gần khách hàng. Các dự án outsourcing khó có thể đáp ứng yêu cầu này.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Theo <a href="http://www.cs.st-andrews.ac.uk/~ifs/">Ian Sommerville</a> trong cuốn &#8220;<a href="http://www.cs.st-andrews.ac.uk/~ifs/Books/SE8/index.html">Software Engineering</a>&#8220;, mô hình tiến hóa là mô hình phù hợp nhất cho các dự án vừa và nhỏ (dưới 500 000 dòng code). Các dự án lớn và phức tạp nên sử dựng một mô hình kết hợp giữa mô hình thác nước và tiến hóa. Trong đó, các bản mẫu được dùng để làm rõ các yêu cầu của khách hàng. Các yêu cầu đã rõ ràng được tiếp tục phát triển theo mô hình thác nước. Các yêu cầu chưa rõ ràng có thể sử dụng mô hình khám phá và phát triển.</p>
<h3 class="MsoNormal" style="text-align:justify;"><strong>3. Mô hình tăng trưởng (Incremental)</strong></h3>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Mô hình tăng trưởng kết hợp những ưu điểm của mô hình thác nước và mô hình tiến hóa. Ý tưởng của mô hình này là phân chia phần mềm thành những phần tăng trưởng (được gọi là các increments) và phát triển, bàn giao chúng lần lượt cho khách hàng theo mức độ quan trọng. Phần tăng trưởng nào đến lượt được phát triển thì những yêu cầu tương ứng sẽ được phân tích. Chỉ những thay đổi từ phía khách hàng cho những phần chưa được phát triển mới được chấp nhận.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><img class="alignnone" src="http://farm4.static.flickr.com/3179/2973583211_51539da456_o.jpg" alt="" width="512" height="384" /></p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Theo Ian Sommerville, mỗi phần tăng trưởng không nên quá 20 000 dòng code và phải mang lại những lợi ích nhất định cho khách hàng. Còn theo Mike Cotterell và Bob Hughes trong “<a href="http://www.amazon.co.uk/Software-Management-Tutorial-computing-information/dp/1850321906/ref=sr_1_3/275-4622645-9975354?ie=UTF8&amp;s=books&amp;qid=1225023453&amp;sr=1-3">Software Project Management</a>”, mỗi phần tăng trưởng nên chiếm từ 1% đến 5% của toàn dự án và không nên kéo dài quá một tháng.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Mô hình tăng trưởng có những ưu điểm sau đây:</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Rút ngắn thời gian chờ đợi của khách hàng.</b> Khách hàng không phải đợi đến khi toàn bộ hệ thống hoàn thành mới được hưởng lợi. Những thành phần quan trọng nhất được bàn giao sớm và mang lại lợi ích sớm hơn cho khách hàng.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Tăng chất lượng phần mềm. </b>Thành phần quan trọng nhất của hệ thống được phát triển và đi vào hoạt động sớm nhất, bởi vậy nó được test nhiều nhất. Ngoài ra, những ý kiến của khách hàng cũng như kinh nghiệm phát triển các thành phần trước sẽ được áp dụng ngay lập tức cho các thành phần sau.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Giảm bớt những yêu cầu không cần thiết từ khách hàng.</b> Khi một tính năng chưa có mặt trong hệ thống, họ sẽ nghĩ rằng nó sẽ được tích hợp vào ở những lần bàn giao tiếp theo!</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;"><b>- Tăng năng suất lao động.</b> Nhiều lập trình viên làm việc tốt hơn trong các dự án nhỏ mà họ sớm nhìn thấy thành quả lao động của mình.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Tuy nhiên, mô hình tăng trưởng cũng có nhược điểm là không phải dự án nào cũng có thể được phân chia thành các phần tăng trưởng nhỏ có thể được phát triển và bàn giao tuần tự. Nếu làm không tốt giai đoạn lập kế hoạch và phân tách hệ thống, xung đột giữa các thành phần có thể nảy sinh.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Các biến thể của mô hình tăng trưởng là Agile Development và Extreme Programming.</p>
<p class="MsoNormal" style="margin-left:.25in;text-align:justify;">Ngoài các mô hình phát triển phần mềm cơ bản nói trên còn có các mô hình biến thể như mô hình chữ V (V-model), mô hình xoáy ốc (spiral) hay mô hình tái sử dụng CBSE (component-based software engineering).</p>
<h3 class="MsoNormal" style="text-align:justify;">Tài liệu tham khảo:</h3>
<ul>
<li>&#8220;Software Engineering&#8221; của Ian Sommerville</li>
<li>&#8220;Software Engineering: A practitioner&#8217;s Approach&#8221; của Pressman</li>
<li>&#8220;Software Project Management&#8221; của Cotterell và Hughes</li>
</ul>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/63/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/63/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/63/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=63&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/10/26/cac-mo-hinh-phat-tri%e1%bb%83n-ph%e1%ba%a7n-m%e1%bb%81m/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>

		<media:content url="http://farm4.static.flickr.com/3235/2974435882_02faa55749_o.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3137/2973583173_9659fbf42f_o.jpg" medium="image" />

		<media:content url="http://farm4.static.flickr.com/3179/2973583211_51539da456_o.jpg" medium="image" />
	</item>
		<item>
		<title>Linked List (1) &#8211; Basic</title>
		<link>http://openandfree.wordpress.com/2008/07/26/linked-list-1-basic/</link>
		<comments>http://openandfree.wordpress.com/2008/07/26/linked-list-1-basic/#comments</comments>
		<pubDate>Fri, 25 Jul 2008 18:39:19 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=51</guid>
		<description><![CDATA[Lấy từ tài liệu tuyệt vời này về linked list của trường Stanford mà tôi muốn khái lược lại bằng những đoạn mã trong đó. Danh sách liên kết (linked list) là  một kiến thức rất cơ bản của cấu trúc dữ liệu và giải thuật. Hiểu được những ưu và khuyết điểm của [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=69&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p style="text-align:justify;">Lấy từ tài liệu tuyệt vời <a href="http://cslibrary.stanford.edu/103/LinkedListBasics.pdf">này</a> về linked list của trường Stanford mà tôi muốn khái lược lại bằng những đoạn mã trong đó. Danh sách liên kết (linked list) là  một kiến thức rất cơ bản của cấu trúc dữ liệu và giải thuật. Hiểu được những ưu và khuyết điểm của nó sẽ giúp chúng ta hiểu các vấn đề về thời gian, không gian bộ nhớ và cấu trúc dữ liệu nói chung. Hơn thế nữa học về linked list là một cách rất tốt để hiểu về pointer. Những bài toán về linked list là sự kết hợp rất &#8220;đẹp&#8221; giữa giải thuật (algorithms) và các phép toán với con trỏ (pointer manipulation). Linked list là cách các lập trình viên luyện tập để thực sự hiểu về pointer và bạn sẽ thấy ngôn ngữ C &#8220;đẹp&#8221; vô cùng <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Hãy tham khảo thêm tài liệu của Stanford để hiểu kỹ thêm (Highly recommend!)</p>
<p><!--TOC--><br />
<span id="more-69"></span></p>
<h1>Linked list basic</h1>
<h2>Khai báo linked list:</h2>
<pre>struct node {
     int data;
     struct node* next;
};
struct node* head;</pre>
<h2>Hàm length() lấy số phần tử của danh sách</h2>
<pre>
/*
 Given a linked list head pointer, compute
 and return the number of nodes in the list.
*/
int Length(struct node* head) {
    struct node* current = head;
    int count = 0;
    while (current != NULL) {
        count++;
        current = current-&gt;next;
    }
    return count;
}
</pre>
<h2>Duyệt danh sách sử dụng một con trỏ local</h2>
<pre>
struct node* current = head;
while (current != NULL) {
    // do something with *current node
    current = current-&gt;next;
}
</pre>
<h1>Linked List Building</h1>
<h2>3 bước để thêm một node vào đầu linked list</h2>
<li>Cấp phát vùng nhớ cho node mới</li>
<pre>
struct node* newNode;
newNode = malloc(sizeof(struct node));
newNode-&gt;data = data_client_wants_stored;
</pre>
<li>Kết nối trường next của node mới vào đầu danh sách</li>
<pre>
newNode-&gt;next = head;</pre>
<li>Đưa con trỏ head trỏ vào node mới</li>
<pre>
head = newNode;</pre>
<h2>Hàm Push()</h2>
<p style="text-align:justify;">Có vẻ từ 3 bước trên thì việc viết hàm Push() để thêm một node vào đầu danh sách rất dễ dàng. Hãy xem hàm sau và tìm chỗ sai trước khi xem lời giải.</p>
<pre>
void WrongPush(struct node* head, int data) {
     struct node* newNode = malloc(sizeof(struct node));
     newNode-&gt;data = data;
     newNode-&gt;next = head;
     head = newNode;
}
</pre>
<p style="text-align:justify;">Thực ra sai ở dòng</p>
<pre>head = newNode; // NO this line does not work!</pre>
<p style="text-align:justify;">Hãy sửa lại hàm Push() trước khi xem tiếp.</p>
<pre>
/*
Takes a list and a data value.
Creates a new link with the given data and pushes
it onto the front of the list.
The list is not passed in by its head pointer.
Instead the list is passed in as a "reference" pointer
to the head pointer -- this allows us
to modify the caller's memory.
*/
void Push(struct node** headRef, int data) {
     struct node* newNode = malloc(sizeof(struct node));
     newNode-&gt;data = data;
     newNode-&gt;next = *headRef; // The '*' to dereferences back to the real head
     *headRef = newNode;
}
</pre>
<p style="text-align:justify;">Với C++ thì có hỗ trợ truyền tham chiếu (reference &amp;) vào hàm, nên hàm Push có thể viết như sau với C++</p>
<pre>
/*
 Push() in C++ -- we just add a '&amp;' to the right hand
 side of the head parameter type, and the compiler makes
 that parameter work by reference. So this code changes
 the caller's memory, but no extra uses of '*' are necessary --
 we just access "head" directly, and the compiler makes that
 change reference back to the caller.
*/
void Push(struct node*&amp; head, int data) {
     struct node* newNode = malloc(sizeof(struct node));
     newNode-&gt;data = data;
     newNode-&gt;next = head; // No extra use of * necessary on head -- the compiler
     head = newNode; // just takes care of it behind the scenes.
}
</pre>
<h1>Techniques</h1>
<h2>Duyệt xuống (Iterate down)</h2>
<pre>
struct node* current = head;
while (current != NULL) {
     /* Do sth here */
     current = current-&gt;next;
}
</pre>
<p style="text-align:justify;">Có thể thay vòng lặp bằng</p>
<pre>
for (current = head; current != NULL; current = current-&gt;next) {</pre>
<h2>Đổi một pointer bằng một tham chiếu tới pointer (reference pointer)</h2>
<p style="text-align:justify;">Nhiều hàm cần phải thay đổi con trỏ head mặc dù nó được truyền vào hàm số theo kiểu pass-by-value. Để làm điều này trong C thì hãy truyền một pointer đến head pointer thay vì chỉ truyền head pointer. Kiểu pointer chỉ đến một pointer khác thường được gọi là reference pointer.</p>
<p style="text-align:justify;">Hãy xem hàm ChangeToNull để cho con trỏ head trỏ vào NULL</p>
<pre>
// Change the passed in head pointer to be NULL
// Uses a reference pointer to access the caller's memory
void ChangeToNull(struct node** headRef) {
     // Takes a pointer to the value of interest
     *headRef = NULL; // use '*' to access the value of interest
}</pre>
<h2>Build list sử dụng thêm node vào đầu bằng hàm Push</h2>
<p style="text-align:justify;">Phương pháp này làm cho đoạn mã xây dựng list rất ngắn và chạy rất nhanh</p>
<pre>
struct node* AddAtHead() {
	struct node* head = NULL;
	int i;
	for (i=1; i&lt;6; i++) {
		Push(&amp;head, i);
	}
	// head == {5, 4, 3, 2, 1};
	return(head);
}</pre>
<h2>Build list sử dụng con trỏ tail</h2>
<p style="text-align:justify;">Cũng có thể thêm một node mới vào cuối cùng của list. Chúng ta sẽ cần một con trỏ tail trỏ vào node cuối cùng của list và trường next của nó sẽ trỏ vào null. Khi thêm một node mới vào </p>
<pre>
newNode-&gt;next = NULL;
tail-&gt;next = newNode;
tail = tail-&gt;next;</pre>
<p style="text-align:justify;">Tuy nhiên thì có một trường hợp đặc biệt khi phần tử mới thêm vào danh sách là phần tử đầu tiên của list. Hãy xem xét tiếp các kỹ thuật sau.</p>
<h2>Build list sử dụng con trỏ tail có xử lý trường hợp đặc biệt</h2>
<p style="text-align:justify;">Khi build một danh sách bằng cách thêm một node mới vào cuối danh sách thì điểm khó là node đầu tiên phải được thêm vào head pointer và các node khác thì được thêm vào sau node cuối cùng sử dụng tail pointer. Cách đơn giản nhất để xử lý cả điều này là viết hai đoạn code riêng rẽ xử lý chúng. Đầu tiên là đoạn mã thêm vào head pointer cho phần tử đầu tiên và tiếp đó là một vòng lặp sử dụng tail pointer để thêm tất cả các node khác. Trông nó có vẻ &#8220;xấu&#8221; nhưng đơn giản và chạy nhanh.</p>
<pre>
struct node* BuildWithSpecialCase() {
	struct node* head = NULL;
	struct node* tail;
	int i;
	// Deal with the head node here, and set the tail pointer
	Push(&amp;head, 1);
	tail = head;

	// Do all the other nodes using 'tail'
	for (i=2; inext), i); // add node at tail-&gt;next
		tail = tail-&gt;next; // advance tail to point to last node
	}
	return(head); // head == {1, 2, 3, 4, 5};
}</pre>
<h2>Build list sử dụng dummy node</h2>
<p style="text-align:justify;">Một giải pháp khác là sử dụng một dummy node tạm thời ở đầu danh sách. Dummy node sẽ đóng vai trò là phần tử đầu tiên và tất cả các node &#8220;thực sự&#8221; sẽ được thêm vào sau dummy node.</p>
<pre>
struct node* BuildWithDummyNode() {
	struct node dummy; // Dummy node is temporarily the first node
	struct node* tail = &dummy; // Start the tail at the dummy.
	// Build the list on dummy.next (aka tail-&gt;next)
	int i;
	dummy.next = NULL;
	for (i=1; inext), i);
		tail = tail-&gt;next;
	}
	// The real result list is now in dummy.next
	// dummy.next == {1, 2, 3, 4, 5};
	return(dummy.next);
}</pre>
<p style="text-align:justify;">Ở ví dụ trên ta thấy dummy node sẽ không ở trong list khi ra khỏi hàm. Nhưng có một vài cách thể hiện linked list luôn giữ lại dummy node trong suốt quá trình sử dụng nó. Với cách tiếp cận đó thì sẽ không bao giờ có một danh sách rỗng (empty list). Thay vì đó thì danh sách luôn có một dummy node ở đầu. Tuy nhiên thì tôi không khuyến khích phương pháp tiếp cận đó. Phương án sử dụng dummy node tạm thời nên được sử dụng hơn.</p>
<h2>Build list sử dụng tham chiếu cục bộ (local references)</h2>
<p style="text-align:justify;">Cuối cùng đây là một giải pháp rất &#8220;mẹo mực&#8221; mà không phải sự dụng dummy node. Đó là sử dụng một local &#8220;reference pointer&#8221; mà luôn trỏ vào pointer cuối cùng của list chứ không phải node cuối cùng.</p>
<pre>
struct node* BuildWithLocalRef() {
	struct node* head = NULL;
	struct node** lastPtrRef= &head; // Start out pointing to the head pointer
	int i;
	for (i=1; inext); // Advance to point to the
							// new last pointer
	}
	// head == {1, 2, 3, 4, 5};
	return(head);
}</pre>
<p style="text-align:justify;">Kỹ thuật này rất ngắn nhưng bên trong vòng lặp thật &#8220;kinh hoàng&#8221;.</p>
<p style="text-align:justify;">Cả hai giải pháp sử dụng temporary-dummy và reference pointer có hơi chút &#8220;không bình thường&#8221; nhưng nó rất tốt để chắc chắn rằng chúng ta hiểu về pointer bởi vì chúng sử dụng pointer theo cách &#8220;không bình thường&#8221;.</p>
<h1>Các đoạn mã ví dụ</h1>
<h2>AppendNode()</h2>
<pre>
struct node* AppendNode(struct node** headRef, int data) {
	struct node* current = *headRef;
	struct node* newNode;
	newNode = malloc(sizeof(struct node));
	newNode-&gt;data = data;
	newNode-&gt;next = NULL;

	// special case for length 0
	if (current == NULL) {
		*headRef = newNode;
	} else {
		// Locate the last node
		while (current-&gt;next != NULL) {
			current = current-&gt;next;
		}
		current-&gt;next = newNode;
	}
}</pre>
<h2>AppendNode() sử dụng Push()</h2>
<pre>
struct node* AppendNode(struct node** headRef, int data) {
	struct node* current = *headRef;
	// special case for the empty list
	if (current == NULL) {
		Push(headRef, data);
	} else {
		// Locate the last node
		while (current-&gt;next != NULL) {
			current = current-&gt;next;
		}
		// Build the node after the last node
		Push(&amp;(current-&gt;next), data);
	}
}
</pre>
<h2>CopyList()</h2>
<pre>
struct node* CopyList(struct node* head) {
	struct node* current = head; // used to iterate over the original list
	struct node* newList = NULL; // head of the new list
	struct node* tail = NULL; // kept pointing to the last node in the new list
	while (current != NULL) {
		if (newList == NULL) { // special case for the first new node
			newList = malloc(sizeof(struct node));
			newList-&gt;data = current-&gt;data;
			newList-&gt;next = NULL;
			tail = newList;
		} else {
			tail-&gt;next = malloc(sizeof(struct node));
			tail = tail-&gt;next;
			tail-&gt;data = current-&gt;data;
			tail-&gt;next = NULL;
		}
		current = current-&gt;next;
	}
	return(newList);
}</pre>
<h2>CopyList() with Push()</h2>
<pre>
// Variant of CopyList() that uses Push()
struct node* CopyList2(struct node* head) {
	struct node* current = head; // used to iterate over the original list
	struct node* newList = NULL; // head of the new list
	struct node* tail = NULL; // kept pointing to the last node in the new list
	while (current != NULL) {
		if (newList == NULL) { // special case for the first new node
			Push(&amp;newList, current-&gt;data);
			tail = newList;
		} else {
			Push(&amp;(tail-&gt;next), current-&gt;data); // add each node at the tail
			tail = tail-&gt;next; // advance the tail to the new last node
		}
		current = current-&gt;next;
	}
	return(newList);
}</pre>
<h2>CopyList() With Dummy Node</h2>
<pre>
// Dummy node variant
struct node* CopyList(struct node* head) {
	struct node* current = head; // used to iterate over the original list
	struct node* tail; // kept pointing to the last node in the new list
	struct node dummy; // build the new list off this dummy node
	dummy.next = NULL;
	tail = &dummy; // start the tail pointing at the dummy
	while (current != NULL) {
		Push(&amp;(tail-&gt;next), current-&gt;data); // add each node at the tail
		tail = tail-&gt;next; // advance the tail to the new last node
		current = current-&gt;next;
	}
	return(dummy.next);
}</pre>
<h2>CopyList() With Local References</h2>
<pre>
// Local reference variant
struct node* CopyList(struct node* head) {
	struct node* current = head; // used to iterate over the original list
	struct node* newList = NULL;
	struct node** lastPtr;
	lastPtr = &newList; // start off pointing to the head itself
	while (current != NULL) {
		Push(lastPtr, current-&gt;data); // add each node at the lastPtr
		lastPtr = &amp;((*lastPtr)-&gt;next); // advance lastPtr
		current = current-&gt;next;
	}
	return(newList);
}</pre>
<h2>CopyList() Recursive</h2>
<pre>
// Recursive variant
struct node* CopyList(struct node* head) {
	if (head == NULL) return NULL;
	else {
		struct node* newList = malloc(sizeof(struct node)); // make the one node
		newList-&gt;data = current-&gt;data;
		newList-&gt;next = CopyList(current-&gt;next); // recur for the rest
		return(newList);
	}
}</pre>
<h1>Other implementations</h1>
<p style="text-align:justify;">Hãy tham khảo thêm trong tài liệu</p>
<li>Dummy header</li>
<li>Circular</li>
<li>Tail pointer</li>
<li>Head struct</li>
<li>Doubly-linked</li>
<li>Chunk list</li>
<li>Dynamic array</li>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/69/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/69/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/69/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/69/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/69/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/69/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/69/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/69/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=69&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/07/26/linked-list-1-basic/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>
	</item>
		<item>
		<title>StringTokenizer</title>
		<link>http://openandfree.wordpress.com/2008/07/21/stringtokenizer/</link>
		<comments>http://openandfree.wordpress.com/2008/07/21/stringtokenizer/#comments</comments>
		<pubDate>Mon, 21 Jul 2008 02:47:14 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=49</guid>
		<description><![CDATA[StringTokenizer là một class thường được viết trong các dự án. Đó là một class dùng để xử lý chuỗi, chia tách chuỗi thành các chuỗi con (token) được phân cách bởi các ký tự định nghĩa trước (delimiter). Ví dụ:
&#8220;Hoang Tran:1982:Hanoi University of Technology&#8221; được phân cách bởi ký tự &#8220;:&#8221; để thu được [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=65&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p style="text-align:justify;">StringTokenizer là một class thường được viết trong các dự án. Đó là một class dùng để xử lý chuỗi, chia tách chuỗi thành các chuỗi con (token) được phân cách bởi các ký tự định nghĩa trước (delimiter). Ví dụ:</p>
<p style="text-align:justify;">&#8220;Hoang Tran:1982:Hanoi University of Technology&#8221; được phân cách bởi ký tự &#8220;:&#8221; để thu được thông tin tương ứng về tên, năm sinh và trường học.</p>
<p style="text-align:justify;">Có rất nhiều cách thể hiện lớp StringTokenizer này ở các ngôn ngữ khác nhau (Java thì đã có trong JDK của nó rồi) Bạn có thể google để tìm tham khảo các cách viết đó. Dựa trên ý tưởng của java, chúng ta sẽ xây dựng một lớp StringTokenizer hữu dụng trong C++.</p>
<p style="text-align:justify;">Trong nhiều cách viết lớp này ở trên mạng thì mình cũng nhận ra rằng cách viết ở dự án cũ là tối ưu, hay và dễ hiểu nhất <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Sử dụng deque làm storage data cho lớp StringTokenizer sẽ làm cho quá trình insert cũng như delete các beginning và end của container rất nhanh.</p>
<p style="text-align:justify;">Đây là lớp StringTokenizer đã được thêm bớt chút ít sau khi đi chôm <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<p><span id="more-65"></span></p>
<pre>
class StringTokenizer
{
  public:
    typedef std::deque::iterator iterator;

    StringTokenizer( std::string str, std::string delim = " \t\r\f\n", bool split = false )
    {
      std::string::size_type pos;
      while (( pos = str.find_first_of( delim ) ) != std::string::npos ) {
        if ( split &amp;&amp; pos == 0 ) {
          _tokens.push_back( std::string() );
        } else if ( pos &gt; 0 ) {
          _tokens.push_back( str.substr( 0, pos ) );
        }
        ++pos;

        /* Skip consecutive delimiter characters when tokenising. */

        if (( ! split ) &amp;&amp; ( ! str.empty() )) {
          for( ; delim.find( str[pos] ) != std::string::npos; ++pos ) {
            /* Empty loop. */
          }
        }
        str = str.substr( pos );
      }

      if (( split ) || ( ! str.empty() )) {
        _tokens.push_back( str );
      }
    }

    StringTokenizer( const StringTokenizer &amp;rhs )
    {
      *this = rhs;
    }

    virtual ~StringTokenizer()
    {
    }

    iterator begin()
    {
      return _tokens.begin();
    }

    iterator end()
    {
      return _tokens.end();
    }

    StringTokenizer&amp; operator=( const StringTokenizer &amp;rhs )
    {
      if ( &amp;rhs != this ) {
        _tokens = rhs._tokens;
      }
      return *this;
    }

    const std::string&amp; operator [] ( int index ) {
      return _tokens[ index ];
    }

    int size() const
    {
      return _tokens.size();
    }
    bool empty() const
    {
      return size() == 0;
    }
    bool has_more_tokens()
    {
      return (_tokens.size() &gt; 0);
    }

    std::string next_token()
    {
      std::string tok = _tokens.front();
      _tokens.pop_front();
      return tok;
    }

  private:
    std::deque _tokens;

};
</pre>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/65/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/65/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/65/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=65&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/07/21/stringtokenizer/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>
	</item>
		<item>
		<title>Viết daemon trên Linux (2)</title>
		<link>http://openandfree.wordpress.com/2008/07/03/vi%e1%ba%bft-daemon-tren-linux-2/</link>
		<comments>http://openandfree.wordpress.com/2008/07/03/vi%e1%ba%bft-daemon-tren-linux-2/#comments</comments>
		<pubDate>Thu, 03 Jul 2008 06:36:56 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[daemon]]></category>
		<category><![CDATA[signal]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=48</guid>
		<description><![CDATA[Nguồn: http://www.enderunix.org/docs/eng/daemon.php
Bài viết này bổ xung một số vấn đề chưa được trình bày trong bài viết “Viết daemon trên Linux (1)” của anh Hoàng.
1- Logging
Chúng ta có thể lựa chọn một trong hai cách ghi log như sau:
- Tự viết hàm ghi log:
Một hàm ghi log có dạng như sau:

void log_message( const char *fname, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=64&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Nguồn: <a href="http://www.enderunix.org/docs/eng/daemon.php">http://www.enderunix.org/docs/eng/daemon.php</a><br />
Bài viết này bổ xung một số vấn đề chưa được trình bày trong bài viết <a href="http://www.openandfree.org/blog/?p=45">“Viết daemon trên Linux (1)” </a>của anh Hoàng.</p>
<p><strong>1- Logging</strong><br />
Chúng ta có thể lựa chọn một trong hai cách ghi log như sau:<br />
- Tự viết hàm ghi log:<br />
Một hàm ghi log có dạng như sau:</p>
<pre>
void log_message( const char *fname, const char *msg )
{
  FILE *logfile = fopen( fname, “a” );

  if( ! logfile )
    return;

  fprintf( logfile, “%s\n”, msg );

  fclose( logfile );

}
</pre>
<p>- Dùng các hàm có sẵn trong thư viện: Standard C library có các hàm syslog(), openlog(), closelog() phục vụ việc ghi log.</p>
<p><strong>2- Cơ chế loại trừ lẫn nhau</strong><br />
Tại một thời điểm thường chỉ có một thực thể của daemon đang chạy. Một thực thể của daemon sẽ cố gắng khóa một file (lock file). Nếu khóa thành công nghĩa là chưa có thực thể nào khác của daemon đó đang chạy. Khi đó, pid của thực thể sẽ được ghi vào lock file. Chúng ta sử dụng hàm chuẩn lockf() như sau:</p>
<pre>
lfp = open( "exampled.lock", O_RDWR | O_CREAT, 0640 );

if ( lfp &lt; 0 ) /* không mở được lock file */
  exit( EXIT_FAILURE );

if ( lockf( lfp, F_TLOCK, 0 ) &lt; 0 )
  /* không khóa được lock file, một thực thể khác đang chạy  */
  exit( EXIT_SUCCESS );

/* chưa có thực thể nào đang chạy, ghi pid vào lock file */
sprintf( str,"%d\n", getpid() );
write( lfp, str, strlen(str) );
</pre>
<p><strong>3- Bắt các tín hiệu gửi đến</strong><br />
Sau các bước chuẩn bị nói trên, một daemon sẽ bắt đầu lắng nghe tín hiệu gửi đến từ người dùng hoặc từ các tiến trình khác. Chúng ta viết một hàm xử lí tín hiệu, sau đó gán hàm đó với các tín hiệu cụ thể nhờ hàm chuẩn signal():<span id="more-64"></span></p>
<pre>
void signal_handler( const int sig ) /* signal handler function */
{
  switch( sig ) {

    case SIGHUP:
      log_message( LOG_FILE, "hangup signal catched" );
      break;

    case SIGTERM:
      log_message( LOG_FILE, "terminate signal catched" );
      exit( EXIT_SUCCESS );

  }

}

signal( SIGHUP, signal_handler ); /* hangup signal */
signal( SIGTERM, signal_handler ); /* software termination signal from kill */
</pre>
<p><strong>4- Chương trình hoàn chỉnh</strong><br />
Chương trình dưới đây là sự phát triển của chương trình trong bài viết trước</p>
<pre>
/*
Dịch chương trình: cc –o example example.c
Chạy chương trình: ./example
Test daemon: ps –ef | grep example
Test log: tail –f example.log
Test signal: kill –HUP `cat example.lock`
Terminate daemon: kill `cat example.lock`
*/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include 

const char *LOG_FILE  = “example.log”;
const char *LOCK_FILE = “example.lock”;

void log_message( const char *fname, const char *msg )
{
  FILE *logfile = fopen( fname, “a” );

  if( ! logfile )
    return;

  fprintf( logfile, “%s\n”, msg );

  fclose( logfile );

}

void signal_handler( const int sig ) /* signal handler function */
{
  switch( sig ) {

    case SIGHUP:
      log_message( LOG_FILE, "hangup signal catched" );
      break;

    case SIGTERM:
      log_message( LOG_FILE, "terminate signal catched" );
      exit( EXIT_SUCCESS );

  }

}

int main(void) {

  /* Our process ID and Session ID */
  pid_t pid, sid;

  /* Fork off the parent process */
  pid = fork();
  if (pid  0) {
    exit( EXIT_SUCCESS );
  }

  /* Change the file mode mask */
  umask(0);

  /* Create a new SID for the child process */
  sid = setsid();
  if (sid &lt; 0) {
    /* Log the failure */
    exit( EXIT_FAILURE );
  }

  /* Change the current working directory */
  if ( (chdir("/")) &lt; 0 ) {
    /* Log the failure */
    exit(EXIT_FAILURE);
  }

  /* Close out the standard file descriptors */
  close(STDIN_FILENO);
  close(STDOUT_FILENO);
  close(STDERR_FILENO);

  /* Daemon-specific initialization goes here */
  lfp = open( LOCK_FILE, O_RDWR|O_CREAT, 0640 );

  if (lfp &lt; 0) /* cannot open lock file*/
    exit( EXIT_FAILURE );

  if ( lockf(lfp,F_TLOCK,0) &lt; 0 ) /* cannot lock */
    exit( EXIT_SUCCESS );

  /* first instance continues */
  sprintf( str, "%d\n", getpid() );
  write( lfp, str, strlen(str) ); /* record pid to lockfile */

  signal( SIGCHLD, SIG_IGN ); /* ignore child */
  signal( SIGTSTP, SIG_IGN ); /* ignore tty signals */
  signal( SIGTTOU, SIG_IGN );
  signal( SIGTTIN, SIG_IGN );
  signal( SIGHUP, signal_handler ); /* catch hangup signal */
  signal( SIGTERM, signal_handler); /* catch kill signal */

  /* The Big Loop */
  while (1) {
    /* Do some task here ... */
    sleep(30); /* wait 30 seconds */
  }

  exit(EXIT_SUCCESS);

}</pre>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/64/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/64/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/64/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=64&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/07/03/vi%e1%ba%bft-daemon-tren-linux-2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
		<item>
		<title>Lấy xâu con trong shell script</title>
		<link>http://openandfree.wordpress.com/2008/05/19/l%e1%ba%a5y-xau-con-trong-shell-script/</link>
		<comments>http://openandfree.wordpress.com/2008/05/19/l%e1%ba%a5y-xau-con-trong-shell-script/#comments</comments>
		<pubDate>Mon, 19 May 2008 04:50:49 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[cut]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[substring]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=47</guid>
		<description><![CDATA[Hôm trước nhân lúc nhàn rỗi mình có giúp một “đồng nghiệp” viết một script nhỏ bằng ZShell phục vụ cho việc testing. Do công việc hàng ngày chỉ là viết các script đơn giản nên mình đã khá lúng túng trước yêu cầu phải lấy ra được một xâu con (substring) từ một xâu [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=52&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Hôm trước nhân lúc nhàn rỗi mình có giúp một “đồng nghiệp” viết một script nhỏ bằng ZShell phục vụ cho việc testing. Do công việc hàng ngày chỉ là viết các script đơn giản nên mình đã khá lúng túng trước yêu cầu phải lấy ra được một xâu con (substring) từ một xâu khác. Lên Google search thì thấy cũng có khá nhiều người gặp khó khăn trước vấn đề này và hóa ra xử lí xâu trong shell cũng là một vấn đề khá thú vị. Bài viết này tổng kết những phương pháp để lấy substring trong shell mà tác giả lượm lặt trên mạng. Chú ý rằng những phương pháp này có thể không áp dụng được với một số loại shell nhất định.</p>
<p>Những kĩ thuật dưới đây được áp dụng để giải quyết bài toán cụ thể là lấy ra xâu “substring” từ xâu “this is a substring test”. Bài toán này có ý nghĩa trong trường hợp substring là một biến có thể nhận nhiều giá trị khác nhau.</p>
<p>1- Sử dụng cú pháp substring=${string:starting_position:length}</p>
<pre>string=”this is a substring test”
substring=${string:10:9}
echo $substring</pre>
<p><strong>substring</strong></p>
<p>Chú ý rằng vị trí đầu tiên trong một xâu là 0. Nhược điểm của cách này là chỉ áp dụng khi xâu con cần lấy ra có độ dài cố định. Hơn nữa cách này không áp dụng được cho zshell, một loại shell được dùng phổ biến trên các hệ thống UNIX.</p>
<p>2- Sử dụng lệnh cut<br />
Lệnh cut dùng để lấy ra một số kí tự trong một xâu. Dùng man cut để xem chi tiết về lệnh này. Trong bài toán cụ thể của chúng ta, trong xâu string thì xâu con “substring” bắt đầu từ vị trí 11 và kết thúc ở vị trí thứ 19. Bởi vậy có thể dùng lệnh sau đây để cắt ra “substring”</p>
<pre>string=”this is a substring test”
echo $string | cut –c11-19</pre>
<p><strong>substring</strong></p>
<p>Nhược điểm của cách làm này là chỉ áp dụng với xâu con có độ dài cố định. Chúng ta có thể sử dụng lệnh cut với các tham số sau đây đề khắc phục nhược điểm đó</p>
<pre>string=”this is a substring test”
echo $string | cut –d’ ‘ –f4</pre>
<p><strong>substring</strong></p>
<p>Lệnh trên lấy ra trường thứ 4 (f = field) của xâu string, mỗi trường cách nhau bởi kí tự space (d=delimeter).</p>
<p>3- Sử dụng các toán tử #, ##, % và %%</p>
<p>Toán tử # nghĩa là xóa bắt đầu từ bên trái xâu đầu tiên thỏa mãn mẫu (pattern) theo sau dấu #</p>
<p><span id="more-52"></span></p>
<pre>string=”this is a substring test”

# mẫu “* “ sau dấu #, xóa từ bên trái xâu đầu tiên có dạng một xâu hay một kí tự bất kì
#theo sau bởi kí tự space, tức là xâu “this “
#(chú ý “*” đại diện cho một xâu hay kí tự bất kí)
substring=${string#* }

echo $substring</pre>
<p><strong>is a substring test</strong></p>
<p>Toán tử ## nghĩa là xóa bắt đầu từ bên trái xâu cuối cùng thỏa mãn mẫu (pattern) theo sau dấu ##</p>
<pre>string=”this is a substring test”

# mẫu “* “ sau dấu ##, xóa từ bên trái xâu cuối cùng có dạng một xâu hay kí tự bất kì
#theo sau bởi kí tự space, tức là xâu “this is a substring “
substring=${string##* }

echo $substring</pre>
<p><strong>test</strong></p>
<p>Toán tử % nghĩa là xóa từ bên phải xâu đầu tiên thỏa mãn mẫu (pattern) theo sau dấu %.</p>
<pre>string=”this is a substring test”

# mẫu “ *” sau dấu %, xóa từ bên phải xâu đầu tiên có dạng kí tự space
#theo sau bởi một xâu hay kí tự bất kí, tức là xâu “ test”.
substring=${string% *}

echo $substring</pre>
<p><strong>this is a substring</strong></p>
<p>Toán tử %% nghĩa là xóa từ bên phải xâu đầu tiên thỏa mãn mấu (pattern) theo sau dấu %%.</p>
<pre>string=”this is a substring test”

#mẫu “ *” sau dấu %%, xóa từ bên phải xâu cuối cùng có dạng kí tự space
#theo sau bởi một xâu hay kí tự bất kì, tức là xâu “ is a substring  test”
substring=${string%% *}

echo $substring</pre>
<p><strong>this</strong></p>
<p>Trở lại bài toán ban đầu, để lấy ra xâu “substring” chúng ta cần xóa đi hai xâu “this is a “ và “ test”. Việc xóa đi xâu “ test” đã được nói đến ở trên:</p>
<pre>substring=${string% *}</pre>
<p>Có nhiều cách để xóa nốt xâu “this is a “. Xâu này có đặc điểm là xâu đầu tiên có dạng một xâu bất kì theo sau bởi kí tự ‘a’ theo sau bởi kí tự space. Bởi vậy có thể viết</p>
<pre>substring=${substring#*a }
echo $substring</pre>
<p><strong>substring</strong></p>
<p>Đơn giản hơn, đó chính là xâu có dạng “this is a “!!!. Bởi vậy chúng ta có cách thứ hai sau đây</p>
<pre>substring=${substring#this is a }
echo $substring</pre>
<p><strong>substring </strong></p>
<p>Ngoài những cách nói trên thì sed và awk cũng là những công cụ rất mạnh để xử lí xâu. awk có hẳn một hàm substr để lấy ra các xâu con từ một xâu. Tác giả đã có một bài viết về sed và hi vọng sẽ sớm có một bài giới thiệu awk trong tương lai gần.</p>
<p>Một trang web thú vị tổng hợp các cú pháp của lập trình bash shell là: <a href="http://www.arachnoid.com/linux/shell_programming.html">http://www.arachnoid.com/linux/shell_programming.html</a><br />
Cái thú vị nhất ở trang này là lời khẳng định sau đây: &#8220;<strong>I want to say at the outset that shell programming is an art, not a science. That means there is always some other way to do the same thing.</strong><em>&#8220;</em></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/52/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/52/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/52/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/52/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/52/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/52/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/52/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/52/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=52&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/05/19/l%e1%ba%a5y-xau-con-trong-shell-script/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
		<item>
		<title>[Basic] Pointer</title>
		<link>http://openandfree.wordpress.com/2008/04/02/basic-pointer/</link>
		<comments>http://openandfree.wordpress.com/2008/04/02/basic-pointer/#comments</comments>
		<pubDate>Wed, 02 Apr 2008 15:47:07 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=46</guid>
		<description><![CDATA[Phần này sẽ khái lược rất nhanh một vài điểm về các quy tắc sử dụng con trỏ. Để tham khảo toàn diện về con trỏ và bộ nhớ hãy xem tài liệu Pointers and Memory http://cslibrary.stanford.edu/102/).
-         Pointer/Pointee: Một con trỏ “pointer” sẽ lưu một reference đến [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=51&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Phần này sẽ khái lược rất nhanh một vài điểm về các quy tắc sử dụng con trỏ. Để tham khảo toàn diện về con trỏ và bộ nhớ hãy xem tài liệu Pointers and Memory <a href="http://cslibrary.stanford.edu/102/">http://cslibrary.stanford.edu/102/</a>).</span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">Pointer/Pointee: Một con trỏ “pointer” sẽ lưu một reference đến một biến khác được biết như là pointee của nó. Con trỏ có thể được thiết lập giá trị NULL có nghĩa là nó refer đến một pointee nào. (Trong C và C++, giá trị NULL có thể được sử dụng như là giá trị boolean false).</span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">Dereference: Toán tử dereference trên con trỏ cho phép truy nhập vào pointee của nó. Một pointer chỉ có thể bị dereference sau khi nó được thiết lập trỏ đến một pointee cụ thể. Một pointer mà không có pointee thì là bad pointer và không thể bị dereference. </span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">Bad pointer: Một pointer mà không được trỏ vào một pointee thì là “bad” và không thể dereference. Trong C và C++, việc dereference một bad pointer đôi khi gây xung đột ngay lập tức và làm hỏng bộ nhớ của chương trình đang chạy, gây nên &#8220;không biết đường nào mà lần&#8221;. Kiểu lỗi này rất khó để theo dõi. Trong C và C++, tất cả các pointer bắt đầu bằng bad values (những giá trị ngẫu nhiên), do đó rất dễ tình cờ sử dụng bad pointer. Những đoạn mã đúng sẽ thiết lập mỗi pointer có một good value trước khi sử dụng chúng. Chính vì vậy sử dụng bad pointer là một lỗi rất phổ biến trong C/C++. Với Java và các ngôn ngữ khác, các pointers được tự động bắt đầu với giá trị NULL, do đó quá trình dereference sẽ được dễ dàng detect nên các chương trình Java dễ gỡ lỗi này hơn nhiều. </span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">Pointer assignment: Một phép gán giữa hai con trỏ như p = q; sẽ làm cho hai pointer trỏ vào cùng một pointee. Nó sẽ không copy vùng nhớ của pointee. Sau phép gán thì cả hai pointer sẽ chỉ vào cùng một vùng nhớ của pointee. </span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">malloc(): malloc() là một hàm hệ thống mà cấp pháp một vùng nhớ trong “heap” và trả về con trỏ tới vùng nhớ mới đó. Prototype của malloc() và các hàm khác ở trong stdlib.h. Tham số của malloc() là một số nguyên là kích thước của vùng nhớ cần cấp phát tính theo bytes. Không giống như các biến cục bộ (“stack”), vùng nhớ heap không tự động giải phóng khi hàm tạo thoát ra. malloc() sẽ trả về NULL nếu nó không thế đáp ứng được yêu cầu cấp phát. Bạn nên kiểm tra trường hợp NULL với assert() nếu bạn mong nó an toàn. Hầu hết các hệ điều hành tiên tiến sẽ ném ra một exception hoặc làm việc bắt lỗi tự động trong việc cấp phát bộ nhớ của chúng, do đó không nhất thiết là trong đoạn mã của bạn phải kiểm tra việc cấp phát bộ nhớ thất bại. </span></p>
<p class="MsoNormal" style="margin-left:0.5in;text-align:justify;text-indent:-0.25in;"><!--[if !supportLists]--><span style="font-size:10pt;font-family:Verdana;"><span>-<span style="font-family:'Times New Roman';font-style:normal;font-variant:normal;font-weight:normal;font-size:7pt;line-height:normal;">         </span></span></span><!--[endif]--><span style="font-size:10pt;font-family:Verdana;">free(): free() thì ngược với malloc(). Gọi hàm free() trên vùng nhớ trên heap để chỉ ra rằng hệ thống đã thực hiện xong và giải phóng vùng nhớ đó. Tham số của free là một con trỏ tới vùng nhớ trên heap – con trỏ mà chúng ta đã có được thông qua lời gọi tới hàm malloc().</span></p>
<p class="MsoNormal" style="text-align:center;"><span style="text-align:center; display: block;"><a href="http://openandfree.wordpress.com/2008/04/02/basic-pointer/"><img src="http://img.youtube.com/vi/f-pJlnpkLp0/2.jpg" alt="" /></a></span></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/51/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/51/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/51/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=51&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/04/02/basic-pointer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>

		<media:content url="http://img.youtube.com/vi/f-pJlnpkLp0/2.jpg" medium="image" />
	</item>
		<item>
		<title>Viết daemon trên Linux (1)</title>
		<link>http://openandfree.wordpress.com/2008/02/25/vi%e1%ba%bft-daemon-tren-linux/</link>
		<comments>http://openandfree.wordpress.com/2008/02/25/vi%e1%ba%bft-daemon-tren-linux/#comments</comments>
		<pubDate>Mon, 25 Feb 2008 02:15:38 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[daemon]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=45</guid>
		<description><![CDATA[
1. Daemon là gì?
Một daemon (hay service) là một background process được thiết kế để chạy độc lập, rất ít hoặc không có sự can thiệp của user. Daemon http của Apache web server là một ví dụ về daemon. Nó chạy ở dưới background, lắng nghe một số port xác định và cung cấp [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=50&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><!--TOC--></p>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">1. Daemon là gì?</span></strong></h1>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Một daemon (hay service) là một background process được thiết kế để chạy độc lập, rất ít hoặc không có sự can thiệp của user. Daemon http của Apache web server là một ví dụ về daemon. Nó chạy ở dưới background, lắng nghe một số port xác định và cung cấp các pages hoặc processes scripts dựa vào kiểu request của user.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Để tạo một daemon trong Linux, chúng ta cần phải thực hiện một số bước theo thứ tự. Hiểu sâu bên trong cách một daemon hoạt động còn giúp chúng ta hiểu các hàm system call của kernel của Linux. Thực tế thì trong kernel module, có nhiều daemon quản lý các hardware device như là các mạch điều khiển ngoài, printer và PDAs. Chúng là một trong những khối cơ bản trên Linux mà nó cung cấp sự mềm dẻo và sức mạnh tuyệt vời.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Thông qua tài liệu này, chúng ta sẽ thể hiện một daemon rất đơn giản được viết bằng C. Như bạn có thể theo dõi từng bước, các đoạn mã sẽ được thêm vào chỉ ra thứ tự thực hiện các bước để thiết lập daemon và chạy nó.</span></p>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">2. Khởi đầu</span></strong></h1>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Đầu tiên, bạn cần phải có những package sau được cài đặt trên Linux để phát triển các daemons: </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">- GCC 3.2.2 or higher</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">- Linux Development headers and libraries</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Nếu hệ thống của bạn không có những package này bạn cần cài đặt nó. Để tìm version mà GCC bạn đang cài đặt, sử dụng lệnh:</span></p>
<pre>gcc –-version</pre>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">3. Thiết kế</span></strong></h1>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">3.1 Nó sẽ làm gì?</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Một daemon sẽ làm một việc nào đó và cần làm nó tốt. Nó có thể phức tạp như phải quản lý hàng trăm mailbox với rất nhiều domains, hoặc là đơn giản như việc viết một report và gọi sendmail để gửi report đó đi. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Trong bất kỳ trường hợp nào, bạn cần phải lên kế hoạch xem daemon sẽ làm gì. Nếu nó cần tương tác với các daemon khác (có thể bạn chưa viết) thì đó sẽ là điều cần xem xét. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">3.2 Tương tác như thế nào</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Daemon không nên trao đổi trực tiếp với user thông qua terminal. Thực tế, một daemon hoàn toàn không nên tương tác trực tiếp với user. Tất cả sự trao đổi nên thông qua một dạng giạo diện mà nó có thể phức tạp như một GTK+ GUI hay đơn giản như là một tập các tín hiệu. </span></p>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">4. Cấu trúc cơ bản của daemon</span></strong></h1>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Khi một daemon khởi động, nó phải thực hiện một vài công việc ở mức thấp (low-level) để sẵn sàng thực hiện các công việc thực sự. Nó sẽ cần thực hiện các bước sau: </span></p>
<p><span id="more-50"></span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">• Fork off the parent process</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">• Change file mode mask (umask)</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">• Open logs for writing</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">• Create a unique Session ID (SID)</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">• Change the current working directory to a safe place</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">• Close standard file descriptors</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">• Enter actual daemon code</span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;"><br />
4.1 Forking The Parent Process</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Daemon được khởi động hoặc bởi bản thân hệ thống hoặc bởi user command hay script. Khi nó bắt đầu chạy, thì nó được cấp một process và giống như những file có thể chạy khác trên hệ thống. Để cho nó thực sự dưới background, một tiến trình con cần phải được tạo ra tại nơi mà đoạn mã thực sự được thực thi. Quá trình này được biết như là forking và sử dụng hàm fork(). Hãy tham khảo blog trước về fork(). </span></p>
<pre>
pid_t pid;

/* Fork off the parent process */
pid = fork();
if(pid  0) {
  exit(EXIT_SUCCESS);
}
</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Chú ý rằng chúng ta kiểm tra lỗi ngay sau lệnh fork(). Khi viết một daemon, bạn sẽ phải viết những đoạn mã được bảo vệ bao nhiêu càng tốt bấy nhiêu. Thực tế thì một điều tốt là một phần trong toàn bộ các đoạn mã của daemon không có tác dụng gì<span> </span>ngoài chức năng kiểm tra lỗi. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Hàm fork() trả về hoặc là process id (PID) của tiến trình con (khác 0) hoặc là -1 nếu lỗi. Nếu process không thể fork một child process thì daemon nên tạm dừng tại đây. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Nếu fork() thành công, thì parent process phải thoát êm đẹp. Điều này khá kỳ lạ với những người không nhìn thấy nó, nhưng bởi vì forking thì child process sẽ tiếp tục thực thi từ đây trong đoạn mã. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">4.2 Thay đổi mặt lạ file (Umask)</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Để viết vào bất kỳ file nào (bao gồm file logs) được tạo ra bởi daemon, thì mặt lạ file (umask) phải được thay đổi để đảm bảo rằng chúng có thể được viết hay đọc chính xác. Điều này tương tự với việc chạy umask từ command line, nhưng ta thực hiện nó bằng hàm umask() ở đây: </span></p>
<pre>
pid_t pid, sid;

/* Fork off the parent process */
pid = fork();
if (pid  0) {
  exit(EXIT_SUCCESS);
}

/* Change the file mode mask */
umask(0);
</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Bằng việc thiết lập umask bằng 0, chúng ta có toàn quyền truy nhập vào những file được tạo ra bởi daemon. Thậm chí nếu bạn không có kế hoạch sử dụng những file này thì nó vẫn là ý kiến hay khi thiết lập umask ở đây, trong trường hợp bạn đang truy nhập những file khác trong hệ thống file. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">4.3 Mở logs để ghi</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Phần này là tùy chọn, nhưng khuyến nghị rằng bạn nên mở một log file ở đâu đó trong hệ thống để theo dõi hệ thông. Nó có thể ở nơi mà bạn có thể nhìn vào các thông tin gỡ lỗi cho daemon của bạn. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">4.4 Khởi tạo Unique Session ID (SID)</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Từ đây, child process phải thiết lập một unique SID từ kernel để hoạt động. Trong khi đó, child process phải trở thành một “đứa trẻ mồ côi” (parent process died) trong hệ thống. Kiểu pid_t được khai báo trong phần trước cũng được sử dụng để tạo một SID mới cho child process. </span></p>
<pre>
pid_t pid, sid;

/* Fork off the parent process */
pid = fork();
if (pid  0) {
  exit(EXIT_SUCCESS);
}

/* Change the file mode mask */
umask(0);

/* Open any logs here */

/* Create a new SID for the child process */
sid = setsid();
if (sid &lt; 0) {
  /* Log any failure */
  exit(EXIT_FAILURE);
}</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Một lần nữa, hàm setsid() có cùng giá trị trả về như hàm fork(). Chúng ta có thể áp dụng cùng một cách thức kiểm tra lỗi ở đây cho hàm khởi tạo SID cho child process. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">4.5 Thay đổi thư mục làm việc</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Thư mục làm việc hiện tại nên được chuyển tới những nơi cố định. Bởi vì rất nhiều bản phân phối Linux không hoàn toàn tuân theo chuẩn Linux Filesystem Hierarchy, thư mục root (/) là thư mục duy nhất được đảm bảo theo chuẩn. Chúng ta có thể làm việc đó bằng cách sử dụng hàm chdir(): </span></p>
<pre>
pid_t pid, sid;

/* Fork off the parent process */
pid = fork();
if (pid  0) {
 exit(EXIT_SUCCESS);
}

/* Change the file mode mask */
umask(0);

/* Open any logs here */

/* Create a new SID for the child process */
sid = setsid();
if (sid &lt; 0) {
 /* Log any failure here */
 exit(EXIT_FAILURE);
}

/* Change the current working directory */
if ((chdir("/")) &lt; 0) {
 /* Log any failure here */
 exit(EXIT_FAILURE);
}</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Một lần nữa bạn lại có thể nhìn thấy những đoạn mã bảo vệ. Hàm chdir() trả về -1 nếu lỗi, do đó để chắc chắn chúng ta cần kiểm tra giá trị đó sau khi chuyển đến thư mục root. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">4.6 Đóng những mô tả file chuẩn</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Một trong những bước cuối cùng trong việc thiết lập một daemon là đóng những mô tả file chuẩn (stdin, stdout, stderr). Bởi vì một daemon phải không được nhìn thấy terminal, những mô tả phải này trở nên thừa thãi và có thể là những mối nguy hiểm về bảo mật. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Hàm close() có thể thực hiện việc này: </span></p>
<pre>pid_t pid, sid;

/* Fork off the parent process */
pid = fork();
if (pid  0) {
 exit(EXIT_SUCCESS);
}

/* Change the file mode mask */
umask(0);

/* Open any logs here */

/* Create a new SID for the child process */
sid = setsid();
if (sid &lt; 0) {
  /* Log any failure here */
  exit(EXIT_FAILURE);
}

/* Change the current working directory */
if ((chdir("/")) &lt; 0) {
  /* Log any failure here */
  exit(EXIT_FAILURE);
}

/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Đó là một ý tưởng tốt khi gắn những hằng tự định nghĩa cho các mô tả file, để tăng tính portable giữa các hệ thống khác nhau. </span></p>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">5. Viết mã cho daemon</span></strong></h1>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">5.1 Khởi tạo</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Tại thời điểm này, về cơ bản chúng đã nói cho Linux rằng đó là một daemon, do đó bây giờ là lúc viết những đoạn mã thực sự cho daemon. Khởi tạo (initialization) là bước đầu tiên. Vì ở đây có rất nhiều hàm khác nhau có thể gọi để thiết lập các nhiệm vụ cho daemon nên tôi sẽ không đi xa hơn ở đây. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Nhưng điểm lớn nhất ở đây là khi khởi tạo bất kỳ thứ gì cho daemon, cần phải áp dụng quy tắc bảo vệ cho daemon giống như ở phần trước. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Hãy càng chi tiết càng tốt khi viết vào syslog hay những file logs của bạn. Việc gỡ lỗi daemon có thể khá khó khi không có đủ thông tin cần thiết về trạng thái của daemon. </span></p>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">5.2 Vòng lặp</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Đoạn mã chính của daemon nằm trong vòng lặp vô tận. Về mặt kỹ thuật nó không phải là một vòng lặp vô tận nhưng nó được cấu tạo như sau: </span></p>
<pre>pid_t pid, sid;

/* Fork off the parent process */
pid = fork();
if (pid  0) {
  exit(EXIT_SUCCESS);
}

/* Change the file mode mask */
umask(0);

/* Open any logs here */

/* Create a new SID for the child process */
sid = setsid();
if (sid &lt; 0) {
  /* Log any failures here */
  exit(EXIT_FAILURE);
}

/* Change the current working directory */
if ((chdir("/")) &lt; 0) {
  /* Log any failures here */
  exit(EXIT_FAILURE);
}

/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

/* Daemon-specific initialization goes here */

/* The Big Loop */
while (1) {
  /* Do some task here ... */
  sleep(30); /* wait 30 seconds */
}</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Vòng lặp tiêu biểu thường là một vòng lặp while mà có một điều kiện kết thúc vô tận với lời gọi tới hàm sleep bên trong để làm cho nó chạy với những khoảng cụ thể. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Hãy nghĩ về nó như nhịp đập tim: khi tim bạn đập, nó thực hiện một vài tác vụ, sau đó chờ đợi cho đến nhịp đập tiếp theo diễn ra. Rất nhiều daemons thực hiện theo phương thức này.</span></p>
<h1><strong><span style="font-size:10pt;font-family:Verdana;">6. Tổng hợp</span></strong></h1>
<h2><strong><span style="font-size:10pt;font-family:Verdana;">6.1 Ví dụ hoàn chỉnh</span></strong></h2>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Ví dụ dưới đây là một daemon ví dụ hoàn chỉnh mà chỉ ra tất cả các bước cần thiết cho việc thiết lập và thực thi daemon. Để chạy nó, đơn giản chỉ cần biên dịch sử dụng gcc và thực thi từ command line. Để kết thúc nó, hay sử dụng lệnh kill sau khi tìm thấy PID của nó. </span></p>
<pre>
#include
#include
#include
#include
#include
#include
#include
#include
#include 

int main(void) {

	/* Our process ID and Session ID */
	pid_t pid, sid;

	/* Fork off the parent process */
	pid = fork();
	if (pid  0) {
	  exit(EXIT_SUCCESS);
	}

	/* Change the file mode mask */
	umask(0);

	/* Open any logs here */

	/* Create a new SID for the child process */
	sid = setsid();
	if (sid &lt; 0) {
	  /* Log the failure */
	  exit(EXIT_FAILURE);
	}

	/* Change the current working directory */
	if ((chdir("/")) &lt; 0) {
	  /* Log the failure */
	  exit(EXIT_FAILURE);
	}

	/* Close out the standard file descriptors */
	close(STDIN_FILENO);
	close(STDOUT_FILENO);
	close(STDERR_FILENO);

	/* Daemon-specific initialization goes here */

	/* The Big Loop */
	while (1) {
	  /* Do some task here ... */
	  sleep(30); /* wait 30 seconds */
	}

	exit(EXIT_SUCCESS);
}</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Từ đây, bạn có thể sử dụng bộ khung này để viết daemons của chính bạn. Hãy chắc chắn thêm vào quá trình log của bạn (hoặc sử dụng chức năng của syslog). Cuối cùng hãy chú viết những đoạn mã an toàn bằng việc kiểm tra lỗi trả về thường xuyên.</span></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/50/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/50/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/50/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=50&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/02/25/vi%e1%ba%bft-daemon-tren-linux/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f6f88159a87ae5eb23d369e775bf0e4a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hoangtran</media:title>
		</media:content>
	</item>
		<item>
		<title>Các kiểu khởi tạo biến</title>
		<link>http://openandfree.wordpress.com/2008/02/23/cac-ki%e1%bb%83u-kh%e1%bb%9fi-t%e1%ba%a1o-bi%e1%ba%bfn/</link>
		<comments>http://openandfree.wordpress.com/2008/02/23/cac-ki%e1%bb%83u-kh%e1%bb%9fi-t%e1%ba%a1o-bi%e1%ba%bfn/#comments</comments>
		<pubDate>Sat, 23 Feb 2008 07:48:43 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[constructor]]></category>
		<category><![CDATA[initialization]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=44</guid>
		<description><![CDATA[Một bài viết nhỏ để warm-up cái blog sau những ngày đông giá lạnh  
Nguồn: http://www.gotw.ca/gotw/001.htm
Câu hỏi: Cho biết sự khác nhau của những dòng khởi tạo biến sau đây

SomeType t;
SomeType t();
SomeType t( u );
SomeType t = u;
Trả lời:

SomeType t;
Biến t được khởi tạo bởi default constructor SomeType::SomeType()

SomeType t();
Một dòng lệnh dễ gây nhầm [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=49&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p align="justify">Một bài viết nhỏ để warm-up cái blog sau những ngày đông giá lạnh <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<p align="justify">Nguồn: <a href="http://www.gotw.ca/gotw/001.htm">http://www.gotw.ca/gotw/001.htm</a></p>
<p align="justify"><strong>Câu hỏi</strong>: Cho biết sự khác nhau của những dòng khởi tạo biến sau đây</p>
<pre>
SomeType t;
SomeType t();
SomeType t( u );
SomeType t = u;</pre>
<p align="justify"><strong>Trả lời</strong>:</p>
<pre>
SomeType t;</pre>
<p align="justify">Biến t được khởi tạo bởi default constructor SomeType::SomeType()</p>
<pre>
SomeType t();</pre>
<p align="justify">Một dòng lệnh dễ gây nhầm lẫn! Thực ra đây là dòng khai báo một HÀM không có tham số và trả về một giá trị kiểu SomeType.</p>
<pre>
SomeType t( u );</pre>
<p align="justify">Đây là dòng lệnh khởi tạo biến trực tiếp (direct initialization). Biến t được khởi tạo nhờ constructor SomeType::SomeType( u );</p>
<pre>
SomeType t = u;</pre>
<p align="justify">Nhiều người nhầm lẫn rằng lệnh này gọi đến toán tử gán (assignment operator). Thực ra đây là một khởi tạo sao chép, trong đó t được khởi tạo nhờ copy constructor của lớp SomeType. Nếu u không thuộc kiểu SomeType thì constructor SomeType::SomeType( u ) sẽ được gọi để tạo ra một đối tượng tạm thời kiểu SomeType từ u, sau đó đối tượng này sẽ được sao chép sang t bởi copy constructor.</p>
<p><span id="more-49"></span></p>
<p align="justify">Chúng ta có thể dễ dàng kiểm chứng sự khác nhau nói trên nhờ đoạn mã sau đây:</p>
<pre>
#include 

class SomeType
{
  pubic:
    //Default constructor
    SomeType() {
      std::cout  &lt;&lt; “Default constructor called” &lt;&lt; std::endl;
    }

    //Another constructor
    SomeType( AnotherType u ) {
      std::cout &lt;&lt; “Another constructor called” &lt;&lt; std::endl;
    }

    //Copy constructor
    SomeType( const SomeType &amp;rhs ) {
      std::cout &lt;&lt; “Copy constructor called” &lt;&lt; std::endl;
    }

    //Assignment operator
    SomeType&amp; operator=( const SomeType &amp;rhs ) {
      std::cout &lt;&lt; “Assignment operator called” &lt;&lt; std::endl;
    }

};

int main()
{
  SomeType t;
 //SomeType t();
 //SomeType t( u );
 //SomeType t = u;
 return EXIT_SUCCESS;
}</pre>
<p align="justify"><strong>Chú ý</strong>: Một số dự án khuyến khích nhân viên sử dụng kiểu khởi tạo SomeType t = u để tránh nhầm lẫn với các khai báo hàm.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/49/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/49/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/49/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=49&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/02/23/cac-ki%e1%bb%83u-kh%e1%bb%9fi-t%e1%ba%a1o-bi%e1%ba%bfn/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/0b7240ced9e7e663cff734d741f37158?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">kiennguyen</media:title>
		</media:content>
	</item>
	</channel>
</rss>