<?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:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Vietnamese Developers' Blog &#187; Unix/Linux/BSD</title>
	<atom:link href="http://openandfree.wordpress.com/category/unixlinuxbsd/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 &#187; Unix/Linux/BSD</title>
		<link>http://openandfree.wordpress.com</link>
	</image>
			<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>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>Unix programming with standard I/O (2)</title>
		<link>http://openandfree.wordpress.com/2008/01/22/unix-programming-with-standard-io-2/</link>
		<comments>http://openandfree.wordpress.com/2008/01/22/unix-programming-with-standard-io-2/#comments</comments>
		<pubDate>Tue, 22 Jan 2008 12:46:22 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[cat]]></category>
		<category><![CDATA[standard I/O]]></category>
		<category><![CDATA[system]]></category>
		<category><![CDATA[terminal]]></category>
		<category><![CDATA[unix programming]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=43</guid>
		<description><![CDATA[Phần 2: Chương trình hiển thị nội dung file theo từng trang màn hình
1.	Đặt vấn đề
 Khi xem nội dung một file dài, chúng ta thường muốn nội dung file đó được hiển thị lần lượt theo từng trang màn hình. Hai lệnh phổ biến để xem nội dung file là cat và more không [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=48&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><h3>Phần 2: Chương trình hiển thị nội dung file theo từng trang màn hình</h3>
<p align="justify"><strong>1.	Đặt vấn đề</strong></p>
<p align="justify"> Khi xem nội dung một file dài, chúng ta thường muốn nội dung file đó được hiển thị lần lượt theo từng trang màn hình. Hai lệnh phổ biến để xem nội dung file là cat và more không đáp ứng được nhu cầu này (chúng không có khả năng này, hoặc có nhưng không tiện dụng, “tác giả” cũng không biết rõ về tất cả các khả năng của hai lệnh này). Bởi vậy, chúng ta sẽ phát triển một chương trình tên là p làm nhiệm vụ in ra nội dung một file theo từng trang màn hình (screenful-at-a-time). Chương trình sẽ đợi người dùng ấn một phím để chuyển sang hiển thị trang tiếp theo. Giống như vis, p nhận dữ liệu vào từ cả file lẫn standard input. Ví dụ:</p>
<p align="justify">p nhận dữ liệu vào từ file</p>
<pre>
$ p vis.c</pre>
<p align="justify">p nhận dữ liệu vào từ standard input</p>
<pre>
$ grep  ‘#define’  *.[ch]  |  p</pre>
<p align="justify">Ở phiên bản đầu tiên, p sẽ hiển thị nội dung file theo từng khối, mỗi khối 22 dòng (phần lớn các terminal gồm 24 dòng văn bản). Một cách đơn giản để nhắc người dùng ấn một phím để tiếp tục là không in ra kí tự new line nằm cuối dòng thứ 22. Khi đó, con trỏ sẽ nằm ở cuối dòng thứ 22 thay vì đầu dòng thứ 23. Khi người dùng ấn phím enter, kí tự new line còn thiếu của dòng 22 sẽ được thêm vào, nhờ đó dòng tiếp theo (dòng 23) sẽ được in ra ở đúng vị trí của nó. Nếu người dùng ấn ctrl-d hay q thay vì enter, p sẽ kết thúc. Chúng ta sẽ không quan tâm đến các dòng quá dài. Ngoài ra, khi hiển thị nhiều file cùng lúc thì nội dung các file sẽ được in ra liên tục mà không có sự phân cách gì cả. Tức là, đầu ra của hai lệnh sau đây là như nhau</p>
<pre>$ p file1 file2 …</pre>
<p align="justify">và</p>
<pre>$ cat file1 file2 …  |  p</pre>
<p align="justify">Chú ý rằng lệnh cat in ra nội dung các file một cách liên tục mà không có sự phân cách gì cả. Nếu muốn nội dung các file được phân cách bởi tên file, chúng ta có thể dùng vòng lặp sau đây (ôn lại lập trình shell luôn <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> )</p>
<pre>$ for i in filenames
&gt; do
&gt;      echo $i:
&gt;      cat $i
&gt; done  |  p</pre>
<p align="justify">Có rất nhiều tính năng có thể được đưa vào chương trình p. Quan điểm của chúng ta là: <strong>Trước hết tạo ra một phiên bản đơn giản, sau đó dần dần thêm vào các tính năng phức tạp hơn khi cần thiết. Những tính năng được thêm vào phải là những cái mà người dùng thực sự muốn, chứ không phải những cái mà chúng ta nghĩ rằng họ muốn</strong>.</p>
<p align="justify"><strong>2. Phiên bản đầu tiên của p</strong></p>
<p align="justify">Cấu trúc của p cũng tương tự như vis: Hàm main duyệt qua các file đầu vào và gọi một hàm tên là print để xử lí từng file. Dưới đây là hàm main</p>
<p><span id="more-48"></span></p>
<pre>
/* p: print input in chunks (version 1) */

#include 

#define PAGESIZE 22

char *progname; /* program name for error message */

main( int argc, char *argv[ ] )

{

  int i;

  FILE *fp, *efopen( char*, char* );

  void print( FILE*, int );

  progname = argv[ 0 ];

  if( argc == 1 )

    print( stdin, PAGESIZE );

  else

    for( i = 1; i &lt; argc; i++ ) {

      fp = efopen( argv[ i ], “r” );

      print( fp, PAGESIZE );

      fclose( fp );

    }

  exit( 0 );

}</pre>
<p align="justify">Hàm efopen đóng gói các xử lí quen thuộc: Mở một file, nếu không thành công thì in ra thông báo lỗi và dừng chương trình. Thông báo lỗi bao gồm tên chương trình chứa trong một biến external tên là progname được khởi tạo giá trị trong hàm main.</p>
<pre>
/* open file, die if cannot */

FILE *efopen( char *file, char *mode )

{

  FILE *fp;

  extern char *progname;

  if( ( fp = fopen( file, “r” ) ) != NULL )

    return fp;

  fprintf( stderr, “%s: cannot open file %s mode %s\n”, progname, file, mode );

  exit( 1 );

}</pre>
<p align="justify">Có nhiều phương án khác để thiết kế hàm efopen. Cách thứ nhất là khi gặp lỗi mở file, chúng ta in ra thông báo lỗi và trả về con trỏ NULL. Phương án này cho phép người dùng tự quyết định có tiếp tục xử lí hay dừng chương trình. Cách thứ hai là đưa vào một tham số thứ ba cho efopen, chỉ định hàm sẽ dừng chương trình hay trả về NULL trong trường hợp không thể mở file. Trong ngữ cảnh cụ thể của chương trình p, không có lí do gì để chương trình tiếp tục chạy khi không thể mở một file đầu vào. Bởi vậy, thiết kế hiện thời của hàm print là chấp nhận được. Phần xử lí quan trọng nhất của p được thực hiện bởi hàm print.</p>
<pre>
/* print fp in pagesize chunks */

void print( FILE *fp, int pagesize )

{

  char ttyin();

  static int lines = 0; /* Số dòng đã được in ra trên một trang màn hình */

  char buf[ BUFSIZ ];

  while( fgets( buf, sizeof buf, fp ) != NULL ) {

  if( ++lines &lt; pagesize ) {

      fputs( buf, stdout );

    } else {

      buf[ strlen( buf ) – 1 ] = ‘’;

      fputs( buf, stdout );

      fflush( stdout );

      ttyin();

      lines = 0;

    }

  }

}</pre>
<p align="justify">BUFSIZ là một hằng số được định nghĩa trong stdio.h. Hàm fgets( buf, size, fp ) đọc dòng văn bản tiếp theo từ fp vào buf và thêm kí tự ‘’ vào cuối  dòng đó. Dòng được đọc bao gồm cả kí tự new line ở cuối. Ngoài ra, độ dài của nó không được vượt quá size – 1 kí tự. <strong>Thiết kế của hàm fgets có hai điểm chưa tốt. Thứ nhất, nó nên trả về số kí tự vừa được đọc thay vì giá trị buf. Thứ hai, nó không hề đưa ra cảnh báo khi phải xử lí một dòng văn bản dài quá size – 1 kí tự. Mặc dù không có kí tự nào bị mất, chúng ta sẽ phải kiểm tra nội dung của buf để biết thực sự fgets xử lí các dòng quá dài như thế nào!!!</strong> Hàm strlen trả về độ dài của xâu, không tính kí tự ‘’ ở cuối. <strong>Hãy chú ý cách sử dụng strlen để loại bỏ kí tự cuối cùng khỏi một xâu</strong>, trong trường hợp này là kí tự new line. Việc đọc vào phím được ấn được thực hiện bởi hàm ttyin. Hàm ttyin bắt buộc phải đọc dữ liệu từ bàn phím (/dev/tty) chứ không phải từ standard input để đề phòng trường hợp standard input bị định hướng lại thành một file hay một pipeline. Chúng ta thiết kế hàm ttyin sao cho nó trả về kí tự đầu tiên trong chuỗi các kí tự được ấn bởi người dùng, tuy nhiên giá trị này chưa được sử dụng trong phiên bản đầu tiên của p.</p>
<pre>
/* process response from /dev/tty (version 1) */

char ttyin()

{

  char buf[ BUFSIZ ];

  FILE *efopen( char*, char* );

  static FILE *tty = NULL;

  if( tty == NULL )

    tty = efopen( “/dev/tty”, “r” );

  if( ( fgets( buf, BUFSIZ, tty ) == NULL ) || ( buf[ 0 ] == ‘q’ ) )

    exit( 0 );

  else

    return buf[ 0 ];

}</pre>
<p align="justify"><strong>3. Phát triển chương trình</strong></p>
<p align="justify">Phiên bản thứ nhất của p làm duy nhất một việc: In ra nội dung file theo từng trang 22 dòng và đợi phản ứng từ người dùng. Có rất nhiều tính năng mới có thể được thêm vào p mà không đòi hỏi nhiều công sức. Tuy nhiên, thực tế cho thấy những tính năng đó hiếm khi được sử dụng!!! Sự mở rộng đơn giản đầu tiên của p là cho phép người dùng nhập vào số dòng của file sẽ được in ra trên một trang màn hình. Lệnh sau đây</p>
<pre>$ p –n file</pre>
<p align="justify">sẽ in ra nội dung của file theo từng trang, mỗi trang n dòng. Tương tự như chương trình vis, chúng ta thêm đoạn mã sau đây vào hàm main.</p>
<pre>
/* p: print input in chunks (version 2) */

…

progname = argv[ 0 ];

if( ( argc &gt; 1 ) &amp;&amp; ( argv[ 1 ][ 0 ] == ‘-‘ ) ) {

  pagesize = atoi( &amp;argv[ 1 ][ 1 ] );

  argc--;

  argv++;

}

if( argc == 1 )

…</pre>
<p align="justify">Một tính năng mở rộng nữa của p là tạm thời dừng hiển thị nội dung file để thực thi các lệnh khác. <strong>Giống như trong ed và nhiều chương trình khác của Unix, khi người dùng gõ vào một dòng văn bản bắt đầu bởi dấu chấm than (exclaimation mark) thì phần còn lại của dòng đó sẽ được coi như một câu lệnh và sẽ được chuyển tới shell để thực hiện</strong>. Tính năng này được cài đặt khá đơn giản nhờ hàm system. Chúng ta có phiên bản mới của hàm ttyin như sau.</p>
<pre>
/* process response from /dev/tty (version 2) */

char ttyin()

{

  char buf[ BUFSIZ ];

  FILE *efopen( char*, char* );

  static FILE *tty = NULL;

  if( tty == NULL )

    tty = efopen( “/dev/tty”, “r” );

  for( ; ; ) {

    if( ( fgets( buf, BUFSIZ, fp ) == NULL ) || ( buf[ 0 ] == ‘q’ ) ) {

      exit( 0 );

    } else if( buf[ 0 ] == ‘!’ ) {

      system( buf + 1 ); /* BUG here!!! */

      printf( “!\n” );

    } else {

      return buf[ 0 ];

    }

  }

}</pre>
<p align="justify">Tuy nhiên, phiên bản này của ttyin có mỗi lỗi rất tinh vi. <strong>Các lệnh chạy bởi hàm system kế thừa standard input từ p</strong>. Khi p đọc dữ liệu vào từ một pineline hay một file, do standard input bị định hướng lại,  thì lệnh chạy bởi system cũng sẽ nhận dữ liệu từ chính standard input đó chứ không nhận từ người dùng thông qua terminal nữa. Trong trường hợp dưới đây, khi người dùng nhập vào dòng văn bản !ed, nhẽ ra chương trình ed sẽ được gọi và sẽ chờ người dùng nhập dữ liệu vào từ bàn phím. Tuy nhiên do standard input đã bị định hướng lại sang pinepline nên ed lại đọc dữ liệu từ chính file /etc/passwd và sau đó kết thúc ngay lập tức.</p>
<pre>$ cat /etc/passwd | p -1

root:3D.fHR5KoB.3s:0:1:S.User:/:!ed

?

!</pre>
<p align="justify">Lỗi này sẽ được giải quyết trong phần sau. Ở thời điểm này chúng ta chỉ cần nhớ rằng <strong>sử dụng hàm system có thể gây ra lỗi</strong>.</p>
<p align="justify">Chúng ta vừa phát triển hai chương trình vis và p, có thể coi là các biến thể của lệnh cat. Liệu hai chương trình này có nên được tích hợp vào cat dưới dạng các tham số tùy chọn –v và –p hay không? <strong>Chúng ta thường xuyên phải đứng trước hai sự lựa chọn: Viết một chương trình hoàn toàn mới hay thêm tính năng vào một chương trình cũ? Nguyên tắc cơ bản ở đây là: Mỗi chương trình chỉ nên làm một công việc cụ thể mà thôi</strong>. Một chương trình làm quá nhiều việc sẽ rất cồng kềnh, chạy chậm, khó bảo trì và khó sử dụng. Rất nhiều tính năng không được sử dụng đến bởi người dùng không thể nhớ được chúng!!! Chúng ta không nên nhập vis và cat vào làm một. cat thuần túy sao chép dữ liệu từ “input” sang “output” mà không xử lí gì cả. Trong khi đó, vis thực hiện chuyển đổi (transform) từ “input” sang “output”. Nhập vis và cat làm một sẽ tạo ra một chương trình làm hai việc khác nhau. Chúng ta cũng không nên gộp p và cat làm một. cat được dùng để in dữ liệu văn bản nhanh và hiệu quả. Trong khi đó, p được dùng để “duyệt” (browse) dữ liệu văn bản theo cách tiện lợi nhất đối với người dùng. Để ba chương trình cat, vis và p tồn tại riêng rẽ là một quyết định thiết kế hợp lí.</p>
<p align="justify"><strong>4. Một số hướng phát triển chương trình</strong></p>
<p>- p sẽ chạy như thế nào nếu giá trị pagesize nhỏ hơn 0?<br />
- Những tính năng nào có thể được thêm vào p? Bổ sung khả năng tìm kiếm một dòng theo vị trí hay nội dung theo cả hai chiều xuôi (fordward) và ngược (backward). Bổ sung khả năng in ra nội dung file theo từng khối nhỏ hơn pagesize.<br />
- Sử dụng hàm exec để sửa lỗi gây ra bởi hàm system.<br />
- Phiên bản hiện thời của p sẽ “im lặng” đợi người dùng nhập dữ liệu vào từ bàn phím. Sử dụng hàm isatty để khắc phục nhược điểm này.</p>
<p align="justify"><strong>Phần tiếp theo: Chương trình lựa chọn tham số PICK và chương trình chấm dứt các tiến trình ZAP</strong></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/48/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/48/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/48/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=48&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/01/22/unix-programming-with-standard-io-2/feed/</wfw:commentRss>
		<slash:comments>7</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>Unix programming with standard I/O (1)</title>
		<link>http://openandfree.wordpress.com/2008/01/17/unix-programming-with-standard-io/</link>
		<comments>http://openandfree.wordpress.com/2008/01/17/unix-programming-with-standard-io/#comments</comments>
		<pubDate>Thu, 17 Jan 2008 10:49:20 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[standard I/O]]></category>
		<category><![CDATA[unix programming]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=42</guid>
		<description><![CDATA[Lời tựa
Dạo này đang thời kì nông nhàn nên quay lại với blog. Gần một tháng nay không có bài viết mới nào, có lẽ do các blogger đều đang bận bịu với những kế hoạch riêng của mình. Dạo này không thích những thứ loằng ngoằng phức tạp nữa, cũng chả thích mình là [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=47&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p align="justify"><strong>Lời tựa</strong></p>
<p align="justify">Dạo này đang thời kì nông nhàn nên quay lại với blog. Gần một tháng nay không có bài viết mới nào, có lẽ do các blogger đều đang bận bịu với những kế hoạch riêng của mình. Dạo này không thích những thứ loằng ngoằng phức tạp nữa, cũng chả thích mình là guru nữa, đau đầu lắm <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> . Bây giờ chỉ thích những thứ đơn giản nhưng đẹp đẽ. Ngẫm ra làm phần mềm cũng chẳng cần đến những thứ cao siêu hay phức tạp, vấn đề chỉ là kết hợp những thứ thật đơn giản thành một hệ thống lớn làm việc được mà thôi.</p>
<p align="justify">Đấy là lí do mà dạo này “tác giả” quay sang đọc cuốn “The Unix programming environment” của Brian W.Kernighan và Rob Pike. Công nhận hai bác này viết sách cực hay. Cuốn sách không chỉ trình bày các công cụ hay ngôn ngữ mà quan trọng hơn là nêu bật lên được triết lí của Unix: Viết các chương trình đơn giản, mỗi chương trình chỉ làm một nhiệm vụ nhưng có khả năng tương tác tốt với nhau để thực hiện những nhiệm vụ phức tạp hơn.</p>
<p align="justify">“Tác giả” bài viết này, trong thời gian tạm thời chưa bị phân tâm bởi việc fix bug nhàm chán thường nhật, muốn trích ra đây một số phần của cuốn sách, vừa để giải trí, vừa để ôn lại C và Unix, cũng vừa là để học tập tư duy lập trình trên Unix. Đồng thời, việc làm này cũng khôi phục lại phần nào tình cảm của “tác giả” đối với công việc lập trình, vốn đã bị phai nhạt khá nhiều sau một thời gian làm việc trong một dự án chán ngắt và không hề thể hiện được một chút gì gọi là “vẻ đẹp” của lập trình Unix.</p>
<p align="justify">Tác giả cũng khuyến cáo các độc giả (nếu có <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> ) nên tìm đọc bản tiếng Anh của cuốn sách (tác giả cũng không có bản mềm, chỉ có bản cứng), bởi trình độ kĩ thuật và tiếng Anh của tác giả đều chỉ ở mức sơ cấp.</p>
<p align="justify">Loạt bài viết này trích ra từ các chương 6, 7, 8, 9 của cuốn “The Unix programming environment”. Các chương này trình bày quá trình phát triển một số chương trình đơn giản, qua đó làm nổi bật lên các kĩ thuật và các triết lí lập trình trên Unix. Các chương trình này, theo tác giả cuốn sách, đều là các chương trình nhỏ và không có mặt trong 7th Edition của Unix. Nếu hệ điều hành hiện thời của bạn cũng không có chúng, bạn sẽ thấy chúng rất có ích. Trong trường hợp ngược lại, việc so sánh thiết kế của những chương trình sẵn có với những chương trình trong sách cũng mang lại nhiều điều bổ ích.</p>
<p align="justify">Chúng ta cùng bắt đầu với các chương trình nhập xuất dữ liệu (I/O).</p>
<h4 align="justify">Phần 1 Chương trình in ra các kí tự đặc biệt: VIS</h4>
<p align="justify"><strong>1. Standard input and output: vis</strong></p>
<p align="justify">Rất nhiều chương trình đọc dữ liệu từ một đầu vào và ghi dữ liệu ra một đầu ra. Khi đó việc sử dụng các thao tác vào ra (I/O) đối với đầu vào chuẩn (standard input) và đầu ra chuẩn (standard output) là đủ đáp ứng yêu cầu của chương trình.<br />
Chúng ta hãy bắt đầu xây dựng một chương trình đơn giản tên là vis (viết tắt của visible). vis sao chép dữ liệu từ standard input sang standard output, đồng thời hiển thị tất cả các kí tự vốn không thể in được (non-printing character) dưới dạng \nnn, trong đó nnn là giá trị octal của kí tự đó. Ví dụ: Chúng ta chuẩn bị file đầu vào x như sau:</p>
<pre>
$ cat x
abc
def
</pre>
<p align="justify">Sử dụng vis để hiện thị x, kí tự ctrl-a sẽ được hiển thị là 01, kí tự ctrl-b sẽ được hiển thị là 02.</p>
<pre>$ vis x
abc01
def02</pre>
<p align="justify">Chương trình vis sẽ có ích trong việc phát hiện các kí tự lạ được ghi vào file vì một lí do nào đó. Chú ý rằng phiên bản đầu tiên của vis sẽ chỉ nhận dữ liệu vào từ standard input chứ chưa có khả năng đọc file. Khi cần in ra nội dung của nhiều file, chúng ta có thể nhờ đến lệnh cat như sau:</p>
<pre>$ cat file1 file2 file3 … | vis</pre>
<p align="justify">Chương trình vis của chúng ta sẽ có chức năng giống với lệnh sed sau đây:</p>
<pre>$ sed –n l x
abc1
def2</pre>
<p align="justify">Tuy nhiên trong một số trường hợp sed chỉ làm việc được với các file văn bản (text), bởi vậy việc viết mới chương trình vis vẫn là cần thiết.</p>
<p align="justify">Trong phiên bản đầu tiên của vis, chúng ta sử dụng hai hàm getchar và putchar. Hàm getchar đọc vào kí tự tiếp theo từ standard input (mặc định là terminal, cũng có thể là một file hay một pipeline, các chương trình không biết điều đó). Hàm putchar ghi một kí tự ra standard output, mặc định cũng là terminal.</p>
<p align="justify">Dưới đây là phiên bản đầu tiên của vis</p>
<p><span id="more-47"></span></p>
<pre>
/* vis: make funny characters visible (version 1) */
#include
#include 

int main()
{
  int c;
  while( ( c = getchar() ) != EOF ) {

    if( isascii( c ) &amp;&amp;
        ( isprint( c ) || c == ‘\n’ || c == ‘\t’ || c == ‘ ‘ ) )
      putchar( c );
    else
      printf( “\\%03o”, c );
  }
  exit( 0 );
}</pre>
<p align="justify">Chú ý: EOF không phải một byte đọc được từ standard input. Đó là một giá trị được khai báo sao cho khác với tất cả các giá trị có thể chứa trong một byte nhằm đảm bảo sự phân biệt với các giá trị thật. Bởi vậy, biến c cần được khai báo có kiểu int thay vì char để đủ lớn để chứa được giá trị EOF.</p>
<p align="justify">Dịch và chạy chương trình như sau</p>
<pre>
$ gcc vis.c –o vis
$ ./vis
hello world
hello world07
ctrl-d
$</pre>
<p align="justify"><strong>2. Truyền tham số cho trương trình: vis phiên bản 2</strong></p>
<p align="justify">Khi chạy một chương trình C, các tham số dòng lệnh (command-line argument) được truyền vào hàm main thông qua hai tham số argc và argv. argc là số tham số dòng lệnh và argv là danh sách các tham số. argc luôn lớn hơn 0 vì theo quy ước tham số thứ nhất argv[ 0 ] là tên chương trình. Các tham số thực sự có ý nghĩa là argv[ 1 ] đến argv[ argc – 1 ]. Chú ý rằng các thao tác định hướng lại standard input và standard output bởi &gt; và &lt; được thực hiện bởi shell, do đó không được tính là các tham số dòng lệnh.</p>
<p align="justify">Để minh họa việc xử lí tham số dòng lệnh, chúng ta phát triển chương trình vis bằng cách thêm vào một tham số tùy chọn –s (viết tắt của strip). Khi chạy vis –s, các kí tự lạ trong file đầu vào sẽ bị loại bỏ. Tính năng này rất có ích cho việc “làm sạch” các file đến từ các hệ điều hành khác, chẳng hạn các hệ điều hành sử dụng CRLF làm kí tự xuống dòng thay vì kí tự new line. Dưới đây là mã nguồn của phiên bản mới của vis</p>
<pre>
/* vis: make funny characters visible (version 2) */
#include
#include 

int main( int argc, char *argv[ ] )
{
  int c, strip = 0;

  if( ( argc &gt; 1 ) &amp;&amp; ( strcmp( argv[ 1 ], “-s” ) == 0 )
    strip = 1;

  while( ( c = getchar() ) != EOF ) {

    if( isascii( c ) &amp;&amp;
        ( isprint( c ) || c == ‘\n’ || c == ‘\t’ || c == ‘ ‘ ) )
      putchar( c );
    else if( ! strip )
      printf( “\\%03o”, c );
  }
  exit( 0 );
}</pre>
<p align="justify"><strong>3. Truy nhập file: vis phiên bản 3<br />
</strong>Phiên bản 1 và 2 của vis chỉ đọc dữ liệu vào từ standard input. Bây giờ chúng ta sẽ phát triển vis để có thể đọc dữ liệu vào trực tiếp từ các file. Khi chạy lệnh</p>
<pre>
$ vis file1 file2 …</pre>
<p align="justify">vis sẽ đọc vào nội dung của từng file và xử lí thay vì đọc dữ liệu từ standard input. Tất nhiên khi không có tên file nào được truyền vào thì vis vẫn đọc dữ liệu từ standard input. Các hàm thường được sử dụng để xử lí file là: fopen, getc, putc.<br />
Một số điểm cần lưu ý: Khi chạy một chương trình, sẽ có ba file được mở sẵn với các con trỏ file là stdin, stdout và stderr. Chúng có thể được sử dụng ở bất cứ đâu mà một biến kiểu FILE* có thể có mặt. Hàm getchar() tương đương với getc( stdin ) và hàm putchar( c ) tương đương với putc( c, stdout ).<br />
Dưới đây là phiên bản 3 của vis. Nếu có mặt các tham số dòng lệnh là các tên file, chúng sẽ được xử lí lần lượt. Nếu không có mặt tham số dòng lệnh nào, vis vẫn đọc dữ liệu từ standard input.</p>
<pre>
/* vis: make funny characters visible (version 3) */
#include
#include 

int strip = 0;

int main( int argc, char *argv[ ] )
{

  int i;
  FILE *fp;
  void vis( FILE* );

  while( ( argc &gt; 1 ) &amp;&amp; ( argv[1 ][ 0 ] == ‘-‘ ) ) {

    switch( argv[ 1 ][ 1 ] ) {

      case ‘s’: /* -s: strip funny chars */
        strip = 1;
        break;

      default:
        fprintf( stderr, “%s: unknown arg %s\n”, argv[ 0 ], argv[ 1 ] );
        exit( 1 );
    }

    argc--;
    argv++;
  }

  if( argc == 1 )
    vis( stdin );
  else
    for( i = 1; i &lt; argc; i++ ) {

      if( ( fp = fopen( argv[ i ], “r” ) ) == NULL ) {
        fprintf( stderr, “%s: cannot open %s\n”, argv[ 0 ], argv[ i ] );
        exit( 1 );
      } else {
        vis( fp );
        fclose( fp );
      }
    }
  exit( 0 );
}

/* make chars visible in FILE *fp */
void vis( FILE *fp )
{
  int c;

  while( ( c = getc( fp ) ) != EOF ) {

    if( isascii( c ) &amp;&amp;
        ( isprint( c ) || c == ‘\n’ || c == ‘\t’ || c == ‘ ‘ ) )
      putchar( c );
    else if( ! strip )
      printf( “\\%03o”, c );
  }
}</pre>
<p align="justify">Chúng ta dùng một vòng lặp while để xử lí các tham số dòng lệnh với giả thiết rằng các tham số tùy chọn (optional argument, ví dụ -s) xuất hiện trước. Mặc dù phiên bản hiện thời của vis chỉ có một tham số tùy chọn là –s nhưng vòng lặp while ở trên có khả năng xử lí được nhiều tham số tùy chọn. Một số hệ điều hành cung cấp hàm getopt để xử lí các tham số dòng lệnh. Hãy tham khảo hàm này trước khi tự viết.<br />
Một điều cần chú ý là vis sẽ kết thúc khi không thể mở được một trong các file đầu vào. Thiết kế này là chấp nhận được bởi vis chủ yếu được sử dụng một cách tương tác và với chỉ một file đầu vào.</p>
<p align="justify"><strong>4. Một số hướng phát triển của vis</strong></p>
<p align="justify">- Phát triển vis sao cho các kí tự tab, backslash, backspace, formfeed…được in ra dưới dạng biểu diễn của ngôn ngữ C như \t, \\, \b, \f và các kí tự blank và xuống dòng cũng được đánh dấu. So sánh chương trình với lệnh</p>
<pre>
$ sed –n l</pre>
<p align="justify">- Phát triển vis sao cho các dòng quá dài bị cắt đi, chỉ được hiển thị với một độ dài hợp lí.</p>
<p align="justify">- Phát triển vis sao cho khi chạy chương trình với tham số -sn như sau</p>
<pre>
$ vis –sn</pre>
<p align="justify">sẽ chỉ in ra các xâu được tạo thành bởi n hay nhiều hơn các kí tự có thể in được (printable character). Nghĩa là các kí tự không in được và các xâu gồm các kí tự in được nhưng quá ngắn đều bị bỏ qua. Tính năng này rất hữu ích trong việc trích ra các phần văn bản từ các file không phải văn bản (non-text file), ví dụ các chương trình chạy (executable program). Một số hệ điều hành cung cấp chương trình strings thực hiện nhiệm vụ này. Suy nghĩ xem có cần thiết viết một chương trình hoàn toàn mới thay vì sửa đổi vis hay không?</p>
<p align="justify">- Viết một chương trình có tên printable in ra tên các file chỉ chứa các kí tự in được. Tên các file được đưa vào bằng các tham số dòng lệnh. Chương trình này có ích khi cần in nhiều file cùng lúc như sau</p>
<pre>
$ pr `printable *` | lpr</pre>
<p align="justify">Dùng lệnh man để xem chức năng của hai lệnh pr và lpr</p>
<p align="justify"><strong>Phần tiếp theo: Chương trình hiển thị nội dung file theo từng trang màn hình: P</strong></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/47/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/47/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/47/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=47&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2008/01/17/unix-programming-with-standard-io/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>The stream editor sed</title>
		<link>http://openandfree.wordpress.com/2007/12/22/the-stream-editor-sed/</link>
		<comments>http://openandfree.wordpress.com/2007/12/22/the-stream-editor-sed/#comments</comments>
		<pubDate>Sat, 22 Dec 2007 06:29:37 +0000</pubDate>
		<dc:creator>kiennguyen</dc:creator>
				<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[sed]]></category>
		<category><![CDATA[stream editor]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=41</guid>
		<description><![CDATA[Nguồn: Tổng hợp từ cuốn sách “The Unix Programming Environment” của Brian W. Kernighan và Rob Pike.
Bài viết này giới thiệu những chức năng cơ bản nhất của sed, một chương trình xử lí văn bản cực mạnh trong Unix. Bài viết có sử dụng (mà không giải thích chi tiết) một số regular expression [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=46&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p align="justify">Nguồn: Tổng hợp từ cuốn sách <strong><a href="http://cm.bell-labs.com/cm/cs/upe/">“The Unix Programming Environment”</a></strong> của <strong><a href="http://cm.bell-labs.com/who/bwk/">Brian W. Kernighan</a></strong> và <strong><a href="http://herpolhode.com/rob/">Rob Pike</a></strong>.</p>
<p align="justify">Bài viết này giới thiệu những chức năng cơ bản nhất của sed, một chương trình xử lí văn bản cực mạnh trong Unix. Bài viết có sử dụng (mà không giải thích chi tiết) một số regular expression (cơ bản) và một số lệnh Unix (cũng cơ bản <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' />  )</p>
<p align="justify"><strong>Cú pháp chung của sed</strong></p>
<p align="justify">Cú pháp chung của một lệnh sed là</p>
<pre>sed 'danh sách các lệnh'  'danh sách các file đầu vào'</pre>
<p align="justify">sed sẽ đọc từng dòng của các file đầu vào và thực hiện từng lệnh trong danh sách các lệnh đối với các dòng đó. Sau đó, sed ghi dữ liệu ra vào thiết bị đầu ra chuẩn.</p>
<p align="justify">Chẳng hạn, chúng ta có thể thay tất cả các từ UNIX trong một file thành UNIX(TM) bằng câu lệnh sau:</p>
<pre>
sed 's/UNIX/UNIX(TM)/g' filename</pre>
<p align="justify">Khi thực hiện lệnh này, sed sẽ:</p>
<p align="justify">- Đọc vào từng dòng của filename</p>
<p align="justify">- Đối với mỗi dòng đọc vào, sed sẽ thực hiện lệnh s/UNIX/UNIX(TM)/g, tức là thay thế (s = substitute) tất cả (g = global) các từ UNIX bởi UNIX(TM).</p>
<p align="justify">s (substitute) là một lệnh của ed, chương trình tiền thân của sed.</p>
<p align="justify">Chú ý: sed không thay đổi nội dung của file đầu vào.</p>
<p align="justify">Chúng ta luôn sử dụng dấu nháy đơn (single quote) để bao quanh các lệnh của sed nhằm tránh trường hợp các kí tự đặc biệt bị dịch thành ý nghĩa khác bởi shell.</p>
<p align="justify">Sau đây là 1 số lệnh sed thú vị!!!</p>
<p><span id="more-46"></span></p>
<p align="justify">Cắt file thành các dòng, mỗi dòng một từ</p>
<pre>
sed 's/[ ][ ]*/\ (enter)
&gt; /g' filename</pre>
<p align="justify">Chú ý: Dấu &gt; là dấu nhắc của shell, không phải gõ vào <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' />  Bản chất của lệnh này là thay thế một hoặc nhiều kí tự blank bởi một kí tự xuống dòng. Tương tự, chúng ta có thể dãn khoảng cách giữa các dòng thêm một dòng trống bằng lệnh</p>
<pre>
sed 's/$/\ (enter)
&gt; /g' filename</pre>
<p align="justify">Bản chất của lệnh này là thay thế các kí tự kết thúc mỗi dòng ($) bởi một kí tự xuống dòng. Chú ý rằng lệnh này thao tác trên cả các dòng trống. Để lệnh chỉ thao tác trên các dòng khác trống (tức là mỗi lần thực hiện lệnh chỉ một dòng trống được chèn vào mà thôi), ta dùng lệnh sau ( /./ biểu diễn các dòng không trống)</p>
<pre>
sed '/./s/$\ (enter)
&gt; /g' filename</pre>
<p align="justify">In các dòng từ thứ 1 đến thứ 10 (Chú ý: số MỘT, không phải chữ LỜ)</p>
<pre>
sed -n '1,10p' filename</pre>
<p align="justify">Xóa các dòng từ thứ 1 dến thứ 10</p>
<pre>
sed '1,10d' filename</pre>
<p align="justify">Xóa các dòng chứa xâu &#8220;thing&#8221;</p>
<pre>
sed '/thing/d' filename</pre>
<p align="justify">Xóa file từ dòng thứ 1 đến dòng trống đầu tiên. Chú ý /^$/ biểu diễn các dòng mà kí tự cuối dòng ($) xuất hiện ngay sau kí tự đầu dòng (^), tức là các dòng trống</p>
<pre>
sed '1,/^$/d' filename</pre>
<p align="justify">Xóa dòng cuối dùng</p>
<pre>
sed '$d' filename</pre>
<p align="justify">Một hạn chế của sed là không có khả năng biểu diễn các dòng một cách tương đối, tức là, chúng ta KHÔNG thể dùng lệnh sau đây để xóa dòng nằm ngay trước dòng cuối cùng của file</p>
<pre>
sed '$-1d' filename</pre>
<p align="justify"><strong>Lệnh q</strong></p>
<p align="justify">Lệnh q in ra các dòng từ dòng thứ nhất cho đến dòng đầu tiên thỏa mãn một tính chất nào đó. Chúng ta có thể viết lại lệnh in ra các dòng từ thứ 1 đến thứ 10 ở phần trên bằng lệnh q như sau</p>
<pre>
sed '10q' filename</pre>
<p align="justify">Lệnh này tương đương với lệnh sau đây</p>
<pre>
head -10 filename</pre>
<p align="justify">In ra các dòng từ dòng thứ nhất đến dòng đầu tiên chứa xâu &#8220;thing&#8221;</p>
<pre>
sed '/thing/q' filename</pre>
<p align="justify">In ra các file trong thư mục hiện thời mới hơn file kien <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<pre>
ls -t | sed '/^kien$/q'</pre>
<p align="justify">Bản chất của lệnh trên là: ls -t in ra các file trong thư mục hiện thời theo thứ tự từ mới đến cũ, mỗi tên file nằm trên một dòng. Đầu ra này được chuyển sang cho sed xử lí, sed in ra các dòng từ dòng thứ nhất cho đến dòng đầu tiên chỉ chứa xâu &#8220;kien&#8221; (là tên file). Chú ý rằng /^kien$/ biểu diễn cho các dòng bao gồm kí tự đầu dòng (^), xâu &#8220;kien&#8221;, kí tự cuối dòng ($), tức là các dòng chỉ chứa xâu &#8220;kien&#8221;. Sở dĩ phải dùng pattern /^kien$/ thay vì /kien/ vì có thể tên một file nào đó lại là một phần của tên một file khác( kien với kienabc chẳng hạn)<br />
Tương tự, lệnh sau in ra các file trong thư mục hiện thời cũ hơn file kien</p>
<pre>
ls -tr | sed '/^kien$/q'</pre>
<p align="justify">Tham số -r (revert) yêu cầu ls đảo ngược thứ tự sắp xếp, từ cũ đến mới.</p>
<p align="justify"><strong>Xử lí các dòng KHÔNG thỏa mãn một tính chất nào đó</strong></p>
<p align="justify">Các lệnh ở trên đều xử lí trên các dòng thỏa mãn một tính chất nào đó. Chúng ta có thể thực hiện thao tác ngược lại, xử lí các dòng không thoả mãn một tính chất bằng cách sử dụng dấu chấm than ! (exclamation mark)</p>
<p align="justify">In ra các dòng không chứa xâu &#8220;thing&#8221; (chú ý tham số -n)</p>
<pre>
sed -n '/thing/!p' filename</pre>
<p align="justify">Xóa các dòng không chứa xâu &#8220;thing&#8221;</p>
<pre>
sed '/thing/!d' filename</pre>
<p align="justify"><strong>Thực hiện nhiều xử lí cùng lúc </strong></p>
<p align="justify">sed cho phép thực hiện nhiều xử lí trong cùng một câu lệnh, các thao tác xử lí được phân cách bằng kí tự xuống dòng (enter)</p>
<p align="justify">Lệnh sau đây thay thế xâu &#8220;thing&#8221; trong các dòng bằng xâu &#8220;things&#8221; đồng thời thay thế xâu &#8220;hello&#8221; trong các dòng bằng xâu &#8220;hellos&#8221; (chú ý: &gt; là dấu nhắc của shell, không phải gõ vào)</p>
<pre>
sed 's/thing/things/ (enter)
&gt; s/hello/hellos' filename</pre>
<p align="justify"><strong>In đầu ra của sed ra các file</strong></p>
<p align="justify">Chúng ta có thể in đầu ra của sed ra cá file bằng hai cách sau đây:</p>
<p align="justify">Cách 1: Chuyển hướng đầu ra từ đầu ra chuẩn (màn hình) thành file</p>
<p align="justify">Ví dụ: Ghi ra một file các dòng chứa xâu &#8220;thing&#8221;</p>
<pre>
sed -n '/thing/p' filename &gt;outfile</pre>
<p align="justify">Cách 2: Dùng lệnh w</p>
<p align="justify">Lệnh ghi ra một file các dòng chứa xâu &#8220;thing&#8221; bây giờ được thực hiện như sau</p>
<pre>
sed '/thing/w outfile' filename</pre>
<p align="justify">Kết hợp với dấu chấm than !, chúng ta có câu lệnh khá thú vị sau đây:</p>
<pre>
sed '/thing/w outfile1 (enter)
&gt; /thing/!w outfile2' filenames</pre>
<p align="justify">Câu lệnh trên ghi các dòng chứa xâu &#8220;thing&#8221; vào outfile1 và ghi các dòng không chứa xâu &#8220;thing&#8221; vào outfile2.</p>
<p align="justify">Một ví dụ khác, lệnh sau đây thay thế tất cả các xâu &#8220;thing&#8221; trong file bằng xâu &#8220;things&#8221; và ghi đầu ra ra outfile</p>
<pre>
sed 's/thing/things/gw outfile' filenames</pre>
<p align="justify"><strong>Đặt các lệnh sed trong file đầu vào</strong></p>
<p align="justify">Từ trước đến nay các lệnh sed luôn được gõ trực tiếp trên dòng lệnh, tuy nhiên đối với các lệnh được sử dụng thường xuyên chúng ta nên đặt chúng vào một file đầu vào, sau đó thực hiện sed với tham số -f.</p>
<p align="justify">Ví dụ, để thay thế tất các xâu &#8220;thing&#8221; trong một file bằng xâu &#8220;things&#8221;, chúng ta có thể làm một cách dài dòng như sau:</p>
<p align="justify">Bước 1: Tạo file đầu vào chứa lệnh sed</p>
<pre>
echo 's/thing/things/g' &gt;infile</pre>
<p align="justify">Bước 2: Chạy sed với tham số -f</p>
<pre>
sed -f infile filename</pre>
<p align="justify"><strong>Truyền tham số vào lệnh sed</strong></p>
<p align="justify">Ở phần trên chúng ta đã biết lệnh sau đây in ra các file trong thư mục hiện thời mới hơn file &#8216;kien&#8217;</p>
<pre>
 ls -t | sed '/^kien$/q'</pre>
<p align="justify">Tuy nhiên nếu chúng ta muốn chạy câu lệnh này nhiều lần, mỗi lần với một tên file khác nhau, thì cách tốt nhất là đặt lệnh này vào trong một shell file và truyền tên file (kien,&#8230;) vào như một tham số của shell, ví dụ:</p>
<pre>
newer kien</pre>
<p align="justify">Nội dung của file newer sẽ như sau</p>
<pre>
#newer file: list files newer than file
ls -t | sed '/^'$1'$/q'</pre>
<p align="justify">Các dấu nháy đơn đảm bảo rằng kí tự $ của lệnh sed không bị dịch bởi shell. Trái lại, $1 sẽ bị shell thay thế bởi tham số thứ nhất trên dòng lệnh. Một cách khác để đảm bảo kí tự $ của sed không bị dịch bởi shell là dùng kí tự backslash (\). Khi đó nội dung file newer sẽ như sau (chú ý dấu nháy kép)</p>
<pre>
#newer file: list files newer than file
ls -t | sed "/^$1\$/q"</pre>
<p align="justify">Tương tự chúng ta có file older như sau:</p>
<pre>
#older file: list files older than file
ls -tr | sed '/^'$1'$/q'</pre>
<p align="justify">Hoặc</p>
<pre>
#older file: list files older than file
ls -tr | sed "/^$1\$/q"</pre>
<p align="justify"><strong>Kết luận </strong></p>
<p align="justify">Lệnh sed còn rất nhiều tính năng phức tạp khác như kiểm tra điều kiện, rẽ nhánh, vòng lặp&#8230;Tuy nhiên những tính năng được trình bày ở trên là cơ bản nhất và được sử dụng thường xuyên nhất. Điểm mạnh của sed là khả năng xử lí dữ liệu vào có độ dài tùy ý với tốc độ nhanh. Điểm yếu của sed là khả năng nhớ dữ liệu kém và không xử lí được dữ liệu số. Những điểm yếu này được khắc phục bởi awk.</p>
<p align="justify"><strong>Tài liệu tham khảo</strong></p>
<p align="justify">- Sách: <a href="http://cm.bell-labs.com/cm/cs/upe/">&#8220;The Unix Programming Environment&#8221;</a> của Brian W. Kernighan và Rob Pike</p>
<p align="justify">- Sách: <a href="http://www.unix.org.ua/orelly/unix/sedawk/index.htm">sed &amp; awk</a> by O&#8217;Reilly</p>
<p align="justify">- Manual Page: Dùng lệnh <strong>man sed </strong></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/46/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/46/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/46/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=46&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/12/22/the-stream-editor-sed/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>POSIX Thread (7) &#8211; Condition Variables</title>
		<link>http://openandfree.wordpress.com/2007/12/09/posix-thread-7-condition-variables/</link>
		<comments>http://openandfree.wordpress.com/2007/12/09/posix-thread-7-condition-variables/#comments</comments>
		<pubDate>Sun, 09 Dec 2007 07:30:28 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[multithread]]></category>
		<category><![CDATA[posix]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://www.openandfree.org/blog/?p=37</guid>
		<description><![CDATA[Chúng ta đã biết cách thức dùng mutex để đồng bộ hóa giữa các thread, tránh xung đột giữa các thread khi cùng truy cập tài nguyên. Tuy nhiên thì không phải mutex làm được tất cả. Ví dụ như chúng ta sẽ làm thế nào nếu chúng ta muốn thread đợi một điều kiện [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=43&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;">Chúng ta đã biết cách thức dùng mutex để đồng bộ hóa giữa các thread, tránh xung đột giữa các thread khi cùng truy cập tài nguyên. Tuy nhiên thì không phải mutex làm được tất cả. Ví dụ như chúng ta sẽ làm thế nào nếu chúng ta muốn thread đợi một điều kiện nào đó xảy ra với dữ liệu trong vùng chia sẻ? Chắc chắn là chúng ta sẽ làm bằng cách lặp liên tục việc khóa và mở khóa mutex (để đảm bảo đồng bộ giữa các thread cùng truy nhập vào vùng dữ liệu chia sẻ) và kiểm tra bất cứ sự thay đổi nào trên dữ liệu. Cùng lúc đó sẽ rất nhanh chóng thread sẽ mở khóa mutex cho các thread khác có thể thực hiện sự thay đổi trên vùng dữ liệu đó. Như vậy thì cách tiếp cận này thật kinh khủng bởi vì chúng ta sẽ cần một vòng lặp busy-loop để nhận ra sự thay đổi ở vùng dữ liệu. Nó thật là &#8220;lãng phí CPU&#8221;. (Bạn cần phải phân biệt giữa một chương trình ở trạng thái idle và busy!!!). </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Chúng ta có thể để cho thread “ngủ” một chút, ví dụ 3 giây chẳng hạn ở giữa mỗi một lần kiểm tra, nhưng rõ ràng nó không tối ưu tuyệt đối. Cái chúng ta cần là phải đưa thread đó vào trạng thái ngủ cho đến khi một “điều kiện” nào đó được thỏa mãn. Một khi điều kiện được thỏa mãn thì nó sẽ đánh thức thread của chúng ta để nó tiếp tục sử lý. Đó chính là một kiểu cơ chế báo hiệu (signal). Khi có tín hiệu thì thread mới được đánh thức để sử lý tín hiệu đó.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Trong bài này chúng ta sẽ sử dụng biến pthread_condition_t để thể hiện cách thức báo hiệu này. Biến pthread_condition_t có cách sử dụng cũng tương tự như biến mutex. Chúng ta khai báo và khởi tạo như sau: </span></p>
<p><span id="more-43"></span></p>
<pre>pthread_cond_t condition = PTHREAD_COND_INITIALIZER;</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">hoặc sử dụng </span></p>
<pre>int pthread_cond_init(pthread_cond_t *cond, pthread_cond_attr_t *cond_attr);</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Nhiều thread có thể thực hiện việc chờ trên cùng một biến điều kiện. Khi một thread nào đó muốn báo hiệu việc thay đổi cho các thread đang chờ thì nó sẽ phát tín hiệu đến biến điều kiện đó. Khi đó thì các thread đang chờ trên biến điều kiện đó sẽ thoát khỏi trạng thái &#8220;ngủ&#8221;. Để phát tín hiệu đến biến điều kiện chúng ta dùng</span></p>
<pre>int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">pthread_cond_broadcast gửi tín hiệu kiểu broadcast sẽ đánh thức toàn bộ các thread đang chờ.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Để chờ một biến điều kiện, chúng ta hy vọng một hàm pthread_cond_wait() chấp nhận một biến pthread_cond_t làm tham số. Khi hàm pthread_cond_wait được gọi nó sẽ rơi vào trạng thái ngủ cho tới khi biến điều kiện được đánh thức và đánh thức nó. Khi nó được đánh thức là lúc hàm pthread_cond_wait trả về. Một vấn đề quan trọng là biến điều kiện cũng phải đồng bộ hóa, nghĩa là khi một thread đang chờ trên biến điều kiện cond thì các thread khác không được thay đổi thuộc tính, hay hủy biến điều kiện đó. Thế nhưng bản thân biến điều kiện pthread_cond_t lại không cung cấp cơ chế này nên thường chúng ta sẽ phải sử dụng chung với một biến mutex.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Đầu tiên chúng ta khóa mutex để việc chờ đợi trên biến điều kiện là đồng bộ </span></p>
<pre>int rc = pthread_mutex_lock(&amp;a_mutex);</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Sau khi khóa mutex chúng ta sẽ gọi
<pre>rc = pthread_cond_wait(&amp;a_cond, &amp;a_mutex);</pre>
<p> để thực hiện việc chờ đợi trên biến điều kiện</span></p>
<pre>int pthread_cond_wait(pthread_cond_t* , pthread_mutex_t* );
int pthread_cond_timewait(pthread_cond_t* , pthread_mutex_t* , timespec);</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Việc đầu tiên pthread_cond_wait() làm gần như đồng thời là unlock biến mutex a_mutex (do đó các thread khác có thay đổi vùng dữ liệu chờ đợi) và chờ đợi trên biến điều kiện (do đó pthread_cond_wait() sẽ thức dậy khi nó được báo hiệu bởi thread khác). Bây giờ thì a_mutex đã bị unlock và các thread khác có thể thay đổi vùng nhớ dữ liệu đang theo dõi. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Lúc này thì pthread_cond_wait() vẫn chưa trả về. Việc unlock mutex sảy ra ngay lập tức nhưng việc chờ đợi trên biến điều kiện a_cond thì sẽ block thread của chúng ta, có nghĩa là thread đó sẽ sleep và không tiêu tốn một chu kỳ máy CPU nào cho đến khi nó thức dậy. Nhìn từ phía thread thì nó đơn giản chỉ là chờ đợi hàm pthread_cond_wait() trả về. Khi pthread_cond_wait() trả về chính là lúc thread đó &#8220;thức giấc&#8221; và xử lý tiếp tục các lệnh tiếp theo ở thread sau pthread_cond_wait().</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Giả sử có một thread khác là thread 2 làm nhiệm vụ lock a_mutex sau đó thay đổi dữ liệu trên một biến &#8220;a&#8221; nào đó, unlock a_mutex rồi gửi tín hiệu báo hiệu biến &#8220;a&#8221; thay đổi đến biến điều kiện a_cond bằng pthread_cond_signal(&amp;a_cond). Ngay khi thread 1 ở trên gọi pthread_cond_wait(&amp;a_cond, &amp;a_mutex) thì nó sẽ unlock a_mutex, do đó lúc này thì thread 2 bắt đầu bước vào thực hiện. Thread 2 sẽ khóa mutex thành công, thay đổi trên biến &#8220;a&#8221;, unlock và gửi tín hiệu đến biến a_cond. Lúc này ở thread 1 đang bị blocking, hàm pthread_cond_wait chưa trả về. Ngay khi thread 2 gửi tín hiệu đến a_cond thì nó sẽ đánh thức thread 1 và hàm pthread_cond_wait trả về. Chú ý rằng ngay khi pthread_cond_wait trả về thì nó cũng ngay lập tức khóa lại biến mutex. Nó sẽ xử lý tín hiệu và chúng ta sẽ unlock sau khi xử lý xong ở thread 1.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Tóm lại các bước chờ đợi một biến điều kiện diễn ra như sau.</span></p>
<pre>
int rc = pthread_mutex_lock(&amp;a_mutex);
rc = pthread_cond_wait(&amp;a_cond, &amp;a_mutex);
if (rc == 0) {
    // Do something
}
pthread_mutex_unlock(&amp;a_mutex);
</pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Vậy là chúng ta đã sẵn sàng viết những chương trình multithread thật hay. Hãy suy nghĩ về bài tập sau mà tôi sẽ thể hiện nó trong các bài tiếp theo:</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Verdana;">Bạn có n threads và m jobs. Bạn muốn rằng chúng ta làm n jobs trên n threads đó. Mỗi khi có một job đến thì bạn sẽ cấp một thread cho job đó. </span><span style="font-size:10pt;font-family:Verdana;"></span></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/43/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/43/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/43/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/43/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/43/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/43/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/43/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=43&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/12/09/posix-thread-7-condition-variables/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>POSIX Thread (6) &#8211; Mutex</title>
		<link>http://openandfree.wordpress.com/2007/11/09/posix-thread-6-mutex/</link>
		<comments>http://openandfree.wordpress.com/2007/11/09/posix-thread-6-mutex/#comments</comments>
		<pubDate>Fri, 09 Nov 2007 03:14:05 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[multithread]]></category>
		<category><![CDATA[posix]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://openandfree.wordpress.com/2007/11/09/posix-thread-6-mutex/</guid>
		<description><![CDATA[Chúng ta đã hiểu vấn đề nảy sinh ở chương trình trước. Để giải quyết bài toán xung đột đó chũng ta hãy xem đoạn mã đúng sử dụng mutex:
thread3.c
#include &#60;pthread.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;unistd.h&#62;
#include &#60;stdio.h&#62;
int myglobal;
pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;
void *thread_function(void *arg)
{
    int i,j;
    for ( i=0; i&#60;20; i++ ) {
   [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=32&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p class="western" style="margin-bottom:0;" align="justify"><font face="Arial"><font size="2">Chúng ta đã hiểu vấn đề nảy sinh ở chương trình trước. Để giải quyết bài toán xung đột đó chũng ta hãy xem đoạn mã đúng sử dụng mutex:</font></font></p>
<p class="western" style="margin-bottom:0;" align="justify"><strong><font face="Arial"><font size="2">thread3.c</font></font></strong></p>
<pre><font size="2"><tt><strong><font color="#000080">#include</font></strong> <font color="#ff0000">&lt;pthread.h&gt;</font>
<strong><font color="#000080">#include</font></strong> <font color="#ff0000">&lt;stdlib.h&gt;</font>
<strong><font color="#000080">#include</font></strong> <font color="#ff0000">&lt;unistd.h&gt;</font>
<strong><font color="#000080">#include</font></strong> <font color="#ff0000">&lt;stdio.h&gt;</font>
<font color="#009900">int</font> myglobal<font color="#990000">;</font>
pthread_mutex_t mymutex<font color="#990000">=</font>PTHREAD_MUTEX_INITIALIZER<font color="#990000">;</font>
<font color="#009900">void</font> <font color="#990000">*</font><strong><font color="#000000">thread_function</font></strong><font color="#990000">(</font><font color="#009900">void</font> <font color="#990000">*</font>arg<font color="#990000">)</font>
<font color="#ff0000">{</font>
    <font color="#009900">int</font> i<font color="#990000">,</font>j<font color="#990000">;</font>
    <strong><font color="#0000ff">for</font></strong> <font color="#990000">(</font> i<font color="#990000">=</font><font color="#993399">0</font><font color="#990000">;</font> i<font color="#990000">&lt;</font><font color="#993399">20</font><font color="#990000">;</font> i<font color="#990000">++</font> <font color="#990000">)</font> <font color="#ff0000">{</font>
        <strong><font color="#000000">pthread_mutex_lock</font></strong><font color="#990000">(&amp;</font>mymutex<font color="#990000">);</font>
        j<font color="#990000">=</font>myglobal<font color="#990000">;</font>
        j<font color="#990000">=</font>j<font color="#990000">+</font><font color="#993399">1</font><font color="#990000">;</font>
        <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"."</font><font color="#990000">);</font>
        <strong><font color="#000000">fflush</font></strong><font color="#990000">(</font>stdout<font color="#990000">);</font>
        <strong><font color="#000000">sleep</font></strong><font color="#990000">(</font><font color="#993399">1</font><font color="#990000">);</font>
        myglobal<font color="#990000">=</font>j<font color="#990000">;</font>
        <strong><font color="#000000">pthread_mutex_unlock</font></strong><font color="#990000">(&amp;</font>mymutex<font color="#990000">);</font>
    <font color="#ff0000">}</font>
    <strong><font color="#0000ff">return</font></strong> NULL<font color="#990000">;</font>
<font color="#ff0000">}</font>
<font color="#009900">int</font> <strong><font color="#000000">main</font></strong><font color="#990000">(</font><font color="#009900">void</font><font color="#990000">)</font>
<font color="#ff0000">{</font>
    pthread_t mythread<font color="#990000">;</font>
    <font color="#009900">int</font> i<font color="#990000">;</font>
    <strong><font color="#0000ff">if</font></strong> <font color="#990000">(</font> <strong><font color="#000000">pthread_create</font></strong><font color="#990000">(</font> <font color="#990000">&amp;</font>mythread<font color="#990000">,</font> NULL<font color="#990000">,</font> thread_function<font color="#990000">,</font> NULL<font color="#990000">)</font> <font color="#990000">)</font> <font color="#ff0000">{</font>
        <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"error creating thread."</font><font color="#990000">);</font>
        <strong><font color="#000000">abort</font></strong><font color="#990000">();</font>
    <font color="#ff0000">}</font>
    <strong><font color="#0000ff">for</font></strong> <font color="#990000">(</font> i<font color="#990000">=</font><font color="#993399">0</font><font color="#990000">;</font> i<font color="#990000">&lt;</font><font color="#993399">20</font><font color="#990000">;</font> i<font color="#990000">++)</font> <font color="#ff0000">{</font>
        <strong><font color="#000000">pthread_mutex_lock</font></strong><font color="#990000">(&amp;</font>mymutex<font color="#990000">);</font>
        myglobal<font color="#990000">=</font>myglobal<font color="#990000">+</font><font color="#993399">1</font><font color="#990000">;</font>
        <strong><font color="#000000">pthread_mutex_unlock</font></strong><font color="#990000">(&amp;</font>mymutex<font color="#990000">);</font>
        <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"o"</font><font color="#990000">);</font>
        <strong><font color="#000000">fflush</font></strong><font color="#990000">(</font>stdout<font color="#990000">);</font>
        <strong><font color="#000000">sleep</font></strong><font color="#990000">(</font><font color="#993399">1</font><font color="#990000">);</font>
    <font color="#ff0000">}</font>
    <strong><font color="#0000ff">if</font></strong> <font color="#990000">(</font> <strong><font color="#000000">pthread_join </font></strong><font color="#990000">(</font> mythread<font color="#990000">,</font> NULL <font color="#990000">)</font> <font color="#990000">)</font> <font color="#ff0000">{</font>
        <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"error joining thread."</font><font color="#990000">);</font>
        <strong><font color="#000000">abort</font></strong><font color="#990000">();</font>
    <font color="#ff0000">}</font>
    <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"</font><font color="#cc33cc">\n</font><font color="#ff0000">myglobal equals %d</font><font color="#cc33cc">\n</font><font color="#ff0000">"</font><font color="#990000">,</font>myglobal<font color="#990000">);</font>
    <strong><font color="#000000">exit</font></strong><font color="#990000">(</font><font color="#993399">0</font><font color="#990000">);</font>
<font color="#ff0000">}</font></tt></font></pre>
<p><span id="more-32"></span></p>
<p class="western" style="margin-bottom:0;" align="justify"><font face="Arial"><font size="2">Nếu chúng ta so sánh đoạn mã này với đoạn mã trong chương trình thread2.c, chúng ta sẽ thấy có thêm những lời gọi hàm pthread_mutex_lock() và pthread_mutex_unlock(). Những lời gọi hàm này rất cần thiết trong lập trình với thread. Chúng cung cấp cách thức loại trừ lẫn nhau (mutual exclusion) – có nghĩa là khi một thread sử dụng tài nguyên thì thread khác không được sử dụng và phải chờ cho đến khi thread kia giải phóng tài nguyên. Không có hai thread nào có thể cùng có một mutex bị khóa ở một thời điểm. </font></font></p>
<p class="western" style="margin-bottom:0;" align="justify"><font size="2"><font face="Arial">Đó chính là cách thức mutex làm việc. Nếu thread “a” cố gắng khóa một mutex trong khi thread “b” đã khóa cùng mutex đó rồi thì thread “a” sẽ rơi vào trạng thái ngủ (sleep). Ngay khi thread “b” giải phóng mutex (thông qua pthread_mutex_unlock()), thread “a” sẽ có thể khóa mutex (nói một cách khác thì khi đó giá trị của hàm pthread_mutex_lock() sẽ được trả về với mutex bị lock). Ngược lại, nếu thread “c” cố gắng khóa mutex trong khi thread “a” đang nắm giữ nó thì thread “c” cũng sẽ bị rơi vào trạng thái ngủ tạm thời. Tất cả thread mà cùng rơi vào trạng thái ngủ từ lệnh gọi pthread_mutex_lock() vì một khóa mutex đã bị lock sẵn rồi sẽ được đưa vào hàng đợi để truy nhập vào mutex. </font></font></p>
<p class="western" style="margin-bottom:0;" align="justify"><font size="2"><font face="Arial">pthread_mutex_lock() và pthread_mutex_unlock() thường được sử dụng để bảo vệ cấu trúc dữ liệu. Nó đảm bảo rằng chỉ có một thread tại một thời điểm có thể truy nhập vào cấu trúc dữ liệu bằng việc khóa và mở khóa. Bạn cũng có thể đoán ra rằng POSIX threads sẽ cấp một khóa mà không đưa thread đó vào trạng thái ngủ khi nó cố gắng khóa một mutex không bị khóa. </font></font></p>
<p class="western" style="margin-bottom:0;" align="justify"><font size="2"><font face="Arial">Thế nhưng tại sao chúng ta lại phải đưa các thread vào trạng thái sleep? Rõ ràng nó không phải là ưu điểm chính của thread khi mà ta muốn tận dụng khả năng làm việc độc lập và đồng thời của các thread? Đúng vậy. Tuy nhiên thì các chương trình sử dụng thread vẫn thường cần phải sử dụng mutex. </font></font></p>
<p class="western" style="margin-bottom:0;" align="justify"><font face="Arial"><font size="2">Nếu bạn nhìn vào hàm thread_function(), bạn sẽ nhận ra rằng mutex bị khóa vào lúc đầu vòng lặp và giải phóng khi kết thúc. Trong ví dụ này thì mymutex được dùng để bảo vệ giá trị của myglobal. Nếu nhìn cẩn thận hơn, sẽ thấy rằng đoạn mã tăng giá trị của myglobal trong thread_function sẽ copy giá trị myglobal và một biến local và ghi trở lại từ local vào myglobal sau khi tăng (đoạn mã sleep một giây có thể bỏ đi. Chẳng có lý do gì để thread_function phải sleep khi đã sử dụng mutex. Nó được thêm vào để có thể nhìn rõ hơn sự khác biệt).<br />
</font></font></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/32/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/32/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/32/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=32&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/11/09/posix-thread-6-mutex/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>POSIX Thread (5) &#8211; Synchronization</title>
		<link>http://openandfree.wordpress.com/2007/11/06/posix-thread-5-synchronization/</link>
		<comments>http://openandfree.wordpress.com/2007/11/06/posix-thread-5-synchronization/#comments</comments>
		<pubDate>Tue, 06 Nov 2007 15:28:12 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[multithread]]></category>
		<category><![CDATA[posix]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://openandfree.wordpress.com/2007/11/06/posix-thread-5-synchronization/</guid>
		<description><![CDATA[Bây giờ hãy xem xét một chương trình mà nó có kết quả không như chúng ta mong đợi.
thread2.c
#include &#60;pthread.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;unistd.h&#62;
#include &#60;stdio.h&#62;

int myglobal;

void *thread_function(void *arg) {
  int i,j;
  for ( i=0; i&#60;20; i++ ) {
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
  [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=31&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:Arial;">Bây giờ hãy xem xét một chương trình mà nó có kết quả không như chúng ta mong đợi.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;"><strong>thread2.c</strong></span></p>
<pre><font size="2"><tt><strong><font color="#000080">#include</font></strong> <font color="#ff0000">&lt;pthread.h&gt;</font>
<strong><font color="#000080">#include</font></strong> <font color="#ff0000">&lt;stdlib.h&gt;</font>
<strong><font color="#000080">#include</font></strong> <font color="#ff0000">&lt;unistd.h&gt;</font>
<strong><font color="#000080">#include</font></strong> <font color="#ff0000">&lt;stdio.h&gt;</font>

<font color="#009900">int</font> myglobal<font color="#990000">;</font>

<font color="#009900">void</font> <font color="#990000">*</font><strong><font color="#000000">thread_function</font></strong><font color="#990000">(</font><font color="#009900">void</font> <font color="#990000">*</font>arg<font color="#990000">)</font> <font color="#ff0000">{</font>
  <font color="#009900">int</font> i<font color="#990000">,</font>j<font color="#990000">;</font>
  <strong><font color="#0000ff">for</font></strong> <font color="#990000">(</font> i<font color="#990000">=</font><font color="#993399">0</font><font color="#990000">;</font> i<font color="#990000">&lt;</font><font color="#993399">20</font><font color="#990000">;</font> i<font color="#990000">++</font> <font color="#990000">)</font> <font color="#ff0000">{</font>
    j<font color="#990000">=</font>myglobal<font color="#990000">;</font>
    j<font color="#990000">=</font>j<font color="#990000">+</font><font color="#993399">1</font><font color="#990000">;</font>
    <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"."</font><font color="#990000">);</font>
    <strong><font color="#000000">fflush</font></strong><font color="#990000">(</font>stdout<font color="#990000">);</font>
    <strong><font color="#000000">sleep</font></strong><font color="#990000">(</font><font color="#993399">1</font><font color="#990000">);</font>
    myglobal<font color="#990000">=</font>j<font color="#990000">;</font>
  <font color="#ff0000">}</font>
  <strong><font color="#0000ff">return</font></strong> NULL<font color="#990000">;</font>
<font color="#ff0000">}</font>

<font color="#009900">int</font> <strong><font color="#000000">main</font></strong><font color="#990000">(</font><font color="#009900">void</font><font color="#990000">)</font> <font color="#ff0000">{</font>

  pthread_t mythread<font color="#990000">;</font>
  <font color="#009900">int</font> i<font color="#990000">;</font>

  <strong><font color="#0000ff">if</font></strong> <font color="#990000">(</font> <strong><font color="#000000">pthread_create</font></strong><font color="#990000">(</font> <font color="#990000">&amp;</font>mythread<font color="#990000">,</font> NULL<font color="#990000">,</font> thread_function<font color="#990000">,</font> NULL<font color="#990000">)</font> <font color="#990000">)</font> <font color="#ff0000">{</font>
    <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"error creating thread."</font><font color="#990000">);</font>
    <strong><font color="#000000">abort</font></strong><font color="#990000">();</font>
  <font color="#ff0000">}</font>

  <strong><font color="#0000ff">for</font></strong> <font color="#990000">(</font> i<font color="#990000">=</font><font color="#993399">0</font><font color="#990000">;</font> i<font color="#990000">&lt;</font><font color="#993399">20</font><font color="#990000">;</font> i<font color="#990000">++)</font> <font color="#ff0000">{</font>
    myglobal<font color="#990000">=</font>myglobal<font color="#990000">+</font><font color="#993399">1</font><font color="#990000">;</font>
    <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"o"</font><font color="#990000">);</font>
    <strong><font color="#000000">fflush</font></strong><font color="#990000">(</font>stdout<font color="#990000">);</font>
    <strong><font color="#000000">sleep</font></strong><font color="#990000">(</font><font color="#993399">1</font><font color="#990000">);</font>
  <font color="#ff0000">}</font>

  <strong><font color="#0000ff">if</font></strong> <font color="#990000">(</font> <strong><font color="#000000">pthread_join </font></strong><font color="#990000">(</font> mythread<font color="#990000">,</font> NULL <font color="#990000">)</font> <font color="#990000">)</font> <font color="#ff0000">{</font>
    <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"error joining thread."</font><font color="#990000">);</font>
    <strong><font color="#000000">abort</font></strong><font color="#990000">();</font>
  <font color="#ff0000">}</font>

  <strong><font color="#000000">printf</font></strong><font color="#990000">(</font><font color="#ff0000">"</font><font color="#cc33cc">\n</font><font color="#ff0000">myglobal equals %d</font><font color="#cc33cc">\n</font><font color="#ff0000">"</font><font color="#990000">,</font>myglobal<font color="#990000">);</font>

  <strong><font color="#000000">exit</font></strong><font color="#990000">(</font><font color="#993399">0</font><font color="#990000">);</font>

<font color="#ff0000">}</font></tt></font></pre>
<p><span id="more-31"></span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;">Đây là chương trình giống như thread1.c lần trước dùng để tạo một thread mới. Cả thread chính và thread mới tạo ra đều tăng một biến toàn cùng là <em>myglobal</em> 20 lần. Nhưng chương trình đó có một kết quả không dự đoán trước được. Biên dịch:</span></p>
<pre><font size="2"><tt>$ gcc thread2<font color="#990000">.</font>c <font color="#990000">-</font>o thread2 <font color="#990000">-</font>lpthread</tt></font></pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;">Và chạy nó</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;">Và đây là kết quả từ máy tính của tôi</span></p>
<pre><font size="2"><tt>$ <font color="#990000">./</font>thread2
<font color="#990000">..</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>oo<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">..</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o<font color="#990000">.</font>o
myglobal equals <font color="#993399">21</font></tt></font></pre>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;">Thật lạ! Vì biến myglobal bắt đầu từ 0 và cả hai thread đều tăng nó lên 20 lần nên rõ ràng khi kết thúc chuwong trình chúng ta sẽ phải nhìn thấy myglobal là 40. Như vậy phải có điều gì đó đã xảy ra làm cho myglobal chỉ còn là 21.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;">Hãy nhìn vào hàm thread_function(). Chú ý rằng chúng ta đã copy myglobal vào một biến cục bộ là “j”. Chúng ta tăng j lên một, sau đó sleep một giây rồi mới copy giá trị mới của j vào myglobal. Hãy tưởng tượng sẽ xảy ra điều gì nếu trong thread chính lại tăng myglobal ngay sau khi thread mới copy giá trị của myglobal vào j. Khi thread_function() khi giá trị của j trở lại myglobal, nó đã ghi đè lên sự thay đổi biến đó mà thread chính tạo ra. </span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;">Khi viết một chương trình multithread, chúng ta muốn tránh những hiệu ứng phụ vô dụng như trường hợp này. Loại bỏ nó như thế nào?</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;">Bởi vì vấn đề xảy ra bởi vì chúng ta copy myglobal vào j và giữ nó ở đó 1 giây trước khi ghi nó lại, chúng nên cố gắng tránh sử dụng các biến cục bộ tạm thời và và tăng trực tiếp vào biến myglobal. Giải pháp này có thể hoạt động trong trường hợp cụ thể này nhưng nó không đúng hoàn toàn. Giả sử chúng ta thực hiện các phép toán học phức tạp với biến myglobal thay vì chỉ tăng nó lên một, chúng ta chắc chắn sẽ thất bại.</span></p>
<p class="MsoNormal" style="text-align:justify;"><span style="font-size:10pt;font-family:Arial;">Để hiểu vấn đề này, chúng ta cần phải nhớ rằng các thread được chạy “đồng thời”, thậm chí khi chạy với một máy chỉ có một bộ vi xử lý (nhưng hệ điều hành cung cấp cáh thức cho multitasking). Từ khía cạnh nhận thức của lập trình viên, hãy tưởng tượng các thread thực thi cùng một lúc. Thread2.c có vấn đề vì đoạn mã trong thread_function() không đảm bảo rằng myglobal sẽ không bị thay đổi trong suốt một giây trước khi nó được tăng. Chúng ta cần một vài cách để cho một thread có thể bị tạm dừng khi nó thực hiện thay đổi biến myglobal.</span></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/31/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/31/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/31/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/31/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/31/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/31/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/31/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/31/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/31/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/31/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/31/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/31/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=31&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/11/06/posix-thread-5-synchronization/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>POSIX Thread (4) &#8211; No parents, no children</title>
		<link>http://openandfree.wordpress.com/2007/11/06/posix-thread-4-no-parents-no-children/</link>
		<comments>http://openandfree.wordpress.com/2007/11/06/posix-thread-4-no-parents-no-children/#comments</comments>
		<pubDate>Tue, 06 Nov 2007 14:11:47 +0000</pubDate>
		<dc:creator>Hoang Tran</dc:creator>
				<category><![CDATA[Unix/Linux/BSD]]></category>
		<category><![CDATA[multithread]]></category>
		<category><![CDATA[posix]]></category>
		<category><![CDATA[thread]]></category>

		<guid isPermaLink="false">http://openandfree.wordpress.com/2007/11/06/posix-thread-4-no-parents-no-children/</guid>
		<description><![CDATA[Nguồn: http://www.ibm.com/developerworks/linux/library/l-posix1.html?

Nếu bạn đã từng sử dụng system call fork(), bạn chắc chắn đã quen với khái niệm về parent và child processes. Khi một process tạo ra một process mới bằng cách sử dụng fork() thì process mới được xem như là child process và process nguyên thủy của nó là parent. Nó sẽ [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=30&subd=openandfree&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p style="text-align:justify;"><span class="atitle"><span style="font-size:10pt;font-family:Arial;">Nguồn: http://www.ibm.com/developerworks/linux/library/l-posix1.html?<br />
</span></span></p>
<p style="text-align:justify;"><span class="atitle"><span style="font-size:10pt;font-family:Arial;">Nếu bạn đã từng sử dụng system call fork(), bạn chắc chắn đã quen với khái niệm về parent và child processes. Khi một process tạo ra một process mới bằng cách sử dụng fork() thì process mới được xem như là child process và process nguyên thủy của nó là parent. Nó sẽ tạo ra một cây process có mối quan hệ với nhau và có thể tương tác với nhau; như chờ đợi cho child process kết thúc. Ví dụ hàm waitpid() sẽ nói cho process hiện tại chờ tất cả child processes kết thúc. Waitpid được sử dụng để implement một hàm “dọn dẹp” cho parent process. </span></span></p>
<p style="text-align:justify;"><span class="atitle"><span style="font-size:10pt;font-family:Arial;">Có một chút thú vị khi sử dụng POSIX threads. Những thuật ngữ như parent thread hay child thread không có bởi vì thực sự với POSIX thread thì một mô hình các mối quan hệ giữa các thread là không tồn tại. Khi một thread chính có thể tạo ra một thread mới và thread mới này cũng có thể tạo thêm một thread mới khác. Tuy nhiên thì không có gì nói rằng thread mới đó là “cháu” của main thread. POSIX thread sẽ coi tất cả các thread như là một cái pool (giống như queue) đơn lẻ mà mỗi phần tử là một thread ngang hàng nhau. Do đó khái niệm đợi một child thread không có ý nghĩa. Việc thiếu tính chất thế hệ này ngụ ý một điểm chính: nếu bạn muốn đợi một thread kết thúc thì bạn cần phải xác định thread nào bạn đợi bằng truyền chính xác tid vào pthread_join. Thư viện thread không thể làm điều này hộ chúng ta.</span></span></p>
<p style="text-align:justify;"><span class="atitle"><span style="font-size:10pt;font-family:Arial;">Đối với nhiều người thì điều này không phải là tin tốt vì nó có thể làm phức tạp chương trình có nhiều hơn hai threads. Đừng để điều đó là phiền chúng ta. Chuẩn POSIX thread cung cấp công cụ để chúng ta có thể quản lý nhiều thread một cách dễ dàng. Sự thực thì việc không có mối quan hệ parent/children mở ra một cách nghĩ sáng tạo. Ví dụ, nếu chúng ta có một thread gọi là thread 1 và thread 1 này tạo ra một thread khác là thread 2, thì không nhất thiết là bản thân thread 1 phải gọi pthread_join cho thread 2. Bất kỳ thread nào khác trong chương trình cũng có thể làm điều này. Nó cho phép những khả năng thú vị khác khi bạn viết những đoạn mã multithread khổng lồ. Ví dụ bạn có thể tạo một “dead list” toàn cục bao gồm tất cả những thread đã bị dừng lại và sau đó có một thread đặc biệt để “dọn dẹp” chúng, đơn giản chỉ là đợi một item được thêm vào trong list. Thread có nhiệm vụ dọn dẹp đó sẽ gọi pthread_join() để merge với chính nó. Bây giờ thì quá trình “dọn dẹp” có thể được kiểm soát gọn gằng và hiệu quả trong một thread đơn.</span></span></p>
<p style="text-align:justify;"><span class="atitle"><span style="font-size:10pt;font-family:Arial;">(Còn tiếp)</span></span></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/openandfree.wordpress.com/30/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/openandfree.wordpress.com/30/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/openandfree.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/openandfree.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/openandfree.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/openandfree.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/openandfree.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/openandfree.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/openandfree.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/openandfree.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/openandfree.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/openandfree.wordpress.com/30/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=openandfree.wordpress.com&blog=1590880&post=30&subd=openandfree&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://openandfree.wordpress.com/2007/11/06/posix-thread-4-no-parents-no-children/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>
	</channel>
</rss>