2010年3月30日 星期二

IPC facility - Pipes

Process Pipes

#include stdio.h

FILE* popen( const char* command, const char* open_mode );
int pclose( FILE* pipe_stream );

open_mode 必須是 "r" or "w",但不允許同時開放讀寫的權限,
這是 pipe 與一般 file stream 不同的特性

FILE* read_fp;
read_fp = popen( "cat prog.c | wc -l", "r" );
nread = fread( buf, sizeof(char), sizeof(buf), read_fp );
pclose( read_fp );


The Pipe Call

#include unistd.h

int pipe( int file_descriptor[2] );
任何寫到 file_descriptor[1] 的資料,可以從 file_descriptor[0] 讀回來
*資訊的順序是 FIFO

另外由於 file descriptor 是屬於較低階的檔案處理,
所以不可以使用 file streams 相關的函數,
而需要使用較低階的 read 和 write 等等的 system calls來讀寫

一般來說,pipe 都會使用在相關的兩個 process 之間,例如 parent & child
file descriptors 會跨 fork,所以 parent 開啟的 fd,child 會有同樣的值

int pipefd[2]
pipe( pipefd );
pid_t pid = fork();

switch( pid ) {
case 0: /* parent */
close( pipefd[0] ); /* parent 關閉讀的 pipe */
write( pipefd[1], data, sizeof(data) );
break;

case -1: /* error */
exit( 1 );
break;

default: /* children */
close( pipefd[1] ); /* children 關閉寫的 pipe */
read( pipefd[1], data, sizeof(data) );
break;
}


Named Pipe: FIFO

#include sys/types.h
#include sys/stat.h

int mkfifo( const char* filename, mode_t mode );
mode 是設定 RWX 的權限,例如 0666,實際建置結果還要看系統的 mask 設定
mkfifo 執行成功後,就會產生一個 pipe file
例如 prwxr-xr-x ...... /tmp/myfifo
之後就使用 open, close, read, write 等低階函數來操作它

open 一個 pipe file,有以下三個 flag 可以使用
O_RDONLY, O_WRONLY, O_NONBLOCK (會對 open read write 3個函數產生影響 )





[Reference]
Begining Linux Programming 4th edition
Chapter 13 Inter-Process Communication: Pipes

2010年3月26日 星期五

IPC facility - Message Queue

IPC facility - Shared Memory

IPC facility - Semaphore

Semaphore

#include sys/types.h
#include sys/ipc.h
#include sys/sem.h

int semget ( key_t key, int num_sems, int sem_flags );
key在這邊是要做 IPC 的各個 process 之間共有的一個鍵值 (key value)
key需要相同,才能確保 semget 會回傳代表共用 semaphore 的 sem_id
每個 process 都會拿到只供自己使用的 sem_id 但是其指向的是共用的 semaphore
後文所述另外兩種 IPC facilities 也是採用同樣的原理

num_sems 表示需要使用的 semaphore 數量,一般最常見的值是 1
表示只使用一個 semaphore,如有需要也可以使用多個,會以陣列表示
*所以一個 sem_id 可以表示一個 semaphore 也可以表示一組 semaphores

sem_flags 可以設定類似檔案擁有的權限值,如 0666 這種值
另外可與 IPC_CREAT 或 IPC_EXCL 使用 bitwise OR 運算結合
IPC_CREAT: create a semaphore
IPC_EXCL: exclusive action

[example]
key_t key = (key_t) 54321;
int sem_id = semget ( key, 1, 0644 | IPC_CREAT );



int semop ( int sem_id, struct sembuf* sem_ops, size_t num_sem_ops );
sem_id 是由 semget 回傳的 semaphore identifier
在此表示指定要使用的 semaphore

struct sembuf {
short sem_num; /* index */
short sem_op; /* operations on semaphore */
short sem_flg; /* SEM_UNDO */
}

num_sem_ops 表示 sem_ops 陣列的 size

[example]
/* P action [wait] */
struct sembuf sembuf_p;
sembuf_p.sem_num = 0;
sembuf_p.sem_op = -1; /* for P( ) action */
sembuf_p.sem_flg = SEM_UNDO;

/* V action [signal] */
struct sembuf sembuf_v;
sembuf_v.sem_num = 0;
sembuf_v.sem_op = 1; /* for V( ) action */
sembuf_v.sem_flg = SEM_UNDO;

semop( sem_id, &sembuf_p, 1 );



int semctl ( int sem_id, int sem_num, int command, ... );
sem_id 是由 semget 回傳的 semaphore identifier
在此表示指定要使用的 semaphore

sem_num 用來表示 semaphore number,用在 semaphore 陣列
其值通常是 0,表示第一個而且是唯一的 semaphore ( 單一 semaphore 的情況 )

command 表示要採取的 action,主要使用的是 SETVAL 以及 IPC_RMID
SETVAL 是拿來設定值,會用到第四個參數 union semnu 中的 val 成員
IPC_RMID 是用來移除 semaphore

第四個參數,如果有設定的話,是一個 C union,定義如下

union semnu{
int val;
struct semid_ds* buf;
unsigned short array;
}

[example]
union semun sem_union;
sem_union.val = 1;
semctl ( sem_id, 0, SETVAL, sem_union );
------
semctl ( sem_id, 0, IPC_RMID, sem_union );



[Reference]
Begining Linux Programming 4th edition
Chapter 14 Semaphore, Shared Memory, and Message Queues

2010年3月24日 星期三

POSIX Threads

POSIX Threads

#include pthread.h

int pthread_create( pthread_t* thread, pthread_attr_t* attr,
void *(*start_routine)(void*), void* arg );

pthread_attr_t 可以利用其他設定 thread attribute 的函數 customize
arg 是丟入 start_routine 的參數

void pthread_exit( void* retval );

在成為 thread 的 routine 當中,呼叫 pthread_exit以傳回 retval

int pthread_join( pthread_t th, void** thread_return );

thread_return 會存 retval 的 address

跟 process-based fork-exec 組合不同的是
同屬一個 process 的 threads 之間是可以共享 全域變數
thread 是去執行一個 program 裡面的一個 function
而 fork-exec 是去執行一個 program

使用 thread 有一點需要特別注意:
critical timing condition
在使用 thread 共享的資源中,包含 call by address 的資源
要注意 thread 執行順序並沒有固定
所以資料值可能會因為 timing 的關係而產生錯誤

[example]
函數A使用 thread 的方式執行函數B,並且使用 call by address 的方式傳遞參數 x
若之後 A 會繼續改變 x 變數,而 B 在執行開始就會讀取 x 的值 (單純讀取)
Here comes the timing problem of scheduling :
A before B => B 原本要讀的值被 A 改變了
B before A => B 正確執行

[solution]
將參數傳遞的方式改為 call by value
則 A 對 x 所做的改變就不會影響到 B 的執行結果

不過若是 B 也需要改變 x 的值,則需要更進一步的處理

==========================================================

Synchronization with Semaphores

#include semaphore.h

int sem_init ( sem_t* sem, int pshared, unsigned int value );
int sem_destory ( sem_t* sem );
A semaphore is created with sem_init function, and destoryed by sem_destroy

value 的值決定是 binary semaphore 或 counting semaphore

int sem_wait ( sem_t* sem ); /* value-- */
atomically decrease the value of the semaphore by 1
int sem_post ( sem_t* sem ); /* value++ */
atomically increase the value of the semaphore by 1

利用 semaphore 可以防止 polling 所帶來的 計算資源浪費

==========================================================

Synchronization with Mutexes

#include pthread.h

int pthread_mutex_init ( pthread_mutex_t* mutex,
const pthread_mutexattr_t* mutexattr );
int pthread_mutex_destroy ( pthread_mutex_t* mutex );

int pthread_mutex_lock ( pthread_mutex_t* mutex );
int pthread_mutex_unlock ( pthread_mutex_t* mutex );

To control access to a critical section of code you lock a mutex
before entering the code section and then unlock it when you have finished.

[example]
pthread_mutex_lock( &mutex );
/***************************
***** critical section ******
***************************/
pthread_mutex_unlock( &mutex );


[Reference]
Begining Linux Programming 4th edition
Chapter 12 POSIX Threads

Processes and Signals

Starting New Processes

#include stdlib.h
int system (const char* string);
/* not recommened for use */

==========================================================

Replacing a Process Image
#include unistd.h
exec family

[example]
/* argument list vector version */
char* const ps_argv[] = { "ps", "ax", 0 };
/* environment variable vector */
char* const ps_envp[] = { "PATH=/bin:/usr/bin", "TERM=console", 0 };

-------End with null pointer-----------
execl ( "/bin/ps", "ps", "ax", 0 );
execlp ( "ps", "ps", "ax", 0 ); /* assumes /bin is in PATH */
execle ( "/bin/ps", "ps", "ax", 0, ps_envp ); /* passes own environment */
-----------vector version--------------
execv ( "/bin/ps", ps_argv );
execvp ( "ps", ps_argv );
execve ( "/bin/ps", ps_argv, ps_envp );
---------------------------------------

l 代表 list [ 表示在exec內的命令列參數直接以字串 list寫入 ]
v 代表 vector [ 表示以陣列方式將命令列參數傳入exec函數 ]
p 代表 path [ 表示 command 可以不需要標明絕對路徑 ]
e 代表 environment [ 表示以陣列方式將環境變數傳入exec函數 ]

=========================================================

Duplicating a Process Image
#include sys/types.h
#include unistd.h
pid_t fork( void );

open file descriptors are preserved across calls to fork and exec

Waiting for a Process
#include sys/types.h
#include sys/wait.h
pid_t wait( int* stat_val );
pid_t waitpid( pid_t pid, int* stat_val, int options );

WIFEXITED( stat_val )
WEXITSTATUS( stat_val )

for parent process to release zombie child processes

=========================================================

Sinals
#include signal.h
void (*signal (int sig, void (*func)(int) ) ) (int);
/* not recommened for use */

#include sys/types.h
#include signal.h
int kill ( pid_t pid, int sig );

#include unistd.h
unsigned int alarm ( unsigned int seconds );

#include signal.h
int sigaction( int sig, const struct sigaction* act, struct sigaction* oldact );


Functions that are safe to call inside a signal handler,
either to be re-entrant or not to raise signals themselves.


[Reference]
Begining Linux Programming 4th edition
Chapter 11 Processes and Signals

2010年3月23日 星期二

Linux 程式設計 --使用C語言--

Linux開發工具介紹
--GNU make
--GNU gcc

Linux檔案處理
--低階 file descriptor [ int fd = open( ) ]
open, close, read, write, creat, link, unlink, symlink, ...

--高階 file stream [ FILE* fp = fopen( ) ]
fopen, fclose, fread, fwrite, ...

Linux環境
command line argument
environment variable
date and time
temporary file creation
user information
host information
logging
resource limits

struct file 簡介

struct file 定義在 /linux/include/linux/fs.h(Linux 2.6.11 核心)中,其原型是:




struct file 代表一個open的 file,
系統中的每個open的file在kernel space都有一個關連的struct file,
此資料結構由kernel在open file時創建,
並傳遞給在file上進行操作的任何函數。
在file的所有instance都close後,kernel會釋放這個資料結構,
在kernel或者driver source code中,
struct file的pointer通常被命名為file或filp。


[Reference]
http://linux.chinaunix.net/techdoc/system/2008/08/19/1026178.shtml

2010年3月17日 星期三

GDB 使用簡介

*編譯原始程式碼時,gcc需加-g選項,以在程式中加入除錯資訊
  • $ gcc -g -o hello hello.c
*執行GDB有兩種方式
  • 先單獨鍵入gdb,等進入gdb環境之後,再鍵入file hello
  • 直接鍵入 gdb hello
*退出GDB有兩種模式
  • (gdb) quit 完全退出GDB程式,回到 linux console
  • (gdb) kill 終止當前正在除錯的程式,仍在gdb環境下
*原始程式碼的檢視,指令是 list,預設顯示10行
  • (gdb) list [行號|函數名稱]
  • (gdb) list 9 或者是 list main
*設定程式的命令列參數,指令是 set args [arg1 ... argn]
  • (gdb) set args add 2 3
  • 如同執行 ./hello add 2 3
*在GDB中執行程式
  • run
  • step [單步執行,若有函數呼叫,會進入函數內部]
  • next [單步執行,不會進入函數內部]
  • continue
*中斷點 (break points) 的操作
  • 設定:break [行號|函數名稱|條件運算式]
  • 撤銷:delete [中斷點編號]
  • 查看:info break
*監看點 (watch points) 的操作
  • 設定:watch [變數名稱]
  • 查看:info watchpoints



[Reference]
Linux系統程式設計 使用C

2010年3月5日 星期五

GNU make manual 閱讀整理

Chpater 4 Writing Rules

Chapter 5 Writing the Commands in Rules

@command: suppress the echoing
-command: suppress the error, keep going
+command: ignore -n -t -q options
=======================
-n: --just-print
-t: --touch
-q: --question
=======================

2010年3月3日 星期三

C語言的Ternary Operator

C Variants

A GNU extension to C allows the second operand to be omitted,

and the first operand is implicitly used as the second as well:

a = x ? : y;

The expression is equivalent to

a = x ? x : y;

except that if x is an expression, it is evaluated only once.

The difference is significant if evaluating the expression has side effects.