unix_c++学习笔记3
内容:
* 信号机制
* daemon
* 进程间通信
* makefile

1. 信号简介
* signal 是一种软中断,通知一个进程发生了某些事件
* 信号是一种异步事件
* 对信号的典型处理:
(1)强制忽略,相当于这个信号来和没来都一样
(2)捕获, 用自己定义的行为对该信号进行处理
(3)default, 系统的默认行为,系统为46个信号都定义了行为
2. 几个重要信号
* SIGABRT 调用abort()时产生
* SIGALRM 闹钟信号
* SIGCHLD 当子进程终止时产生
* SIGINT ctrl+c 时产生
* SIGKILL 强制杀死
* SIGSTOP 作业控制信号,停止一个进程,不能被捕获或忽略
3. signal 函数
#include<signal.h>
void (*signal( int signo, void( *func )(int) ) ) (int);
例子:

#include<iostream>
#include<signal.h>
#include<unistd.h>
#include<errno.h>
using namespace std;

// signal 处理函数
void fn( int sig ){
cout<<
"capture signal "<<sig<<endl;
cout<<
"SIGINT="<<SIGINT<<endl;
//signal( SIGINT, fn ); // unix要加这句,unix中注册的signal只生效一次,linux中不用
}
int main(){
cout<<
"pid=" << getpid() <<endl;
// 在信号表中注册SIGINT的处理方法为fn, 用SIG_ERR宏判断注册是否出错
if( signal( SIGINT, fn ) == SIG_ERR ){
cout<< strerror( errno ) <<endl;
exit( -1 );
}
while( 1 ){
sleep( 1 );
}
return 0;
}

// ctrl-z
// bg
// kill -9 pid




例子: 上一章我们在父进程中处理子进程的退出用wait(),这样父进程必须等子进程退出后才能执行
这里用SIGCHLD信号处理子进程的退出,父子进程可以很好的并发

#include<iostream>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<signal.h>
using namespace std;

void handler( int );
int main(){
if( signal( SIGCHLD, handler ) == SIG_ERR ){
cout<<
"signal error"<<endl;
exit( -1 );
}
pid_t cid=fork();
if( cid==0 ){
//child
cout<<
"[child] pid = "<<getpid()<<endl;
for( int i=0; i<3; i++ ){
cout<< 3-i <<
" seconds remaind "<<endl;
sleep( 1 );
}
cout<<
"[child] exit ..."<<endl;
exit( 0 );
}else if( cid > 0 ){
//father
while( 1 ){
cout<<
"[father] running ..."<<endl;
sleep( 1 );
}
}else{
cout<<
"error."<<endl;
exit( -1 );
}
}
void handler( int sig ){
cout<<
"capture signal "<< sig <<endl;
int statloc=0;
pid_t pid = wait( &statloc );
cout<<
"child "<< pid <<"exited"<<endl;
if( WIFEXITED( statloc ) ){
cout<<
"return value = "<< WEXITSTATUS( statloc ) <<endl;
}else{
cout<<WTERMSIG( statloc )<<endl;
}
}



4. kill 命令和kill 函数
kill -sig pid // sig 为信号名去掉SIG前缀,如: kill -INT pid
int kill( pid_t, int sig )
kill 是用来给进程发信号的,发给进程号为pid的进程信号为sig
例子:

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<errno.h>
using namespace std;

void user1_handler( int sig ){
cout<<
"capture signal SIGUSR1 "<<sig<<endl;
signal( SIGUSR1, user1_handler );
}
void user2_handler( int sig ){
cout<<
"capture signal SIGUSR2 "<<sig<<endl;
signal( SIGUSR2, user2_handler );
}
int main(){
if( signal( SIGUSR1, user1_handler )==SIG_ERR ){
cout<<strerror( errno )<<endl;
exit( -1 );
}
if( signal( SIGUSR2, user2_handler )==SIG_ERR ){
cout<<strerror( errno )<<endl;
exit( -1 );
}
cout<<
"process id="<< getpid() <<endl;
while( 1 ){
sleep( 1 );
}
}

// create a new terminal
// kill -USR1 pid
// kill -USR2 pid
// kill -INT pid



上面使用命令发信号,下面用函数发

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<errno.h>
using namespace std;

int main(){
pid_t pid=0;
cout<<
"enter a pid: ";
cin>>pid;
for( int i=0;i<9;i++ ){
if( i%2 ) kill( pid, SIGUSR1 );
else kill( pid, SIGUSR2 );
sleep( 1 );
}
kill( pid, SIGINT );
return 0;
}



5. 忽略一个信号
signal( int sig, SIG_IGN );

例子:

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<errno.h>
using namespace std;

int main(){

if( signal( SIGINT, SIG_IGN )==SIG_ERR ){
cout<< strerror( errno )<<endl;
exit( -1 );
}

pause();
return 0;
}


运行后按 ctrl-c 没有任何反应,只有 kill -9 杀掉它

6. pause 函数
阻塞调用进程,直到他接收某个信号被打断
例子:

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<errno.h>
using namespace std;

void fn( int sig ){
cout<<
"capture sig "<< sig <<endl;
}
int main(){
if( signal( SIGUSR1, fn )==SIG_ERR ){
cout<<
"signal error"<<endl;
exit( -1 );
}
cout<<
"pid = "<< getpid() <<endl;
cout<<
"------------ begin pause -----------"<<endl;
pause();
cout<<
"------------ end pause -------------"<<endl;

return 0;
}

// create a new terminal
// kill -USR1 pid



7. sleep alarm 函数
sleep是阻塞函数,有两种情况被唤醒:
* 时间到
* 捕获信号
sleep的返回值是没睡足的秒数

alarm 是非阻塞的,可以设定一个时钟,时钟到期后产生一个SIGALRM信号

例子:

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<errno.h>
using namespace std;

void fn( int sig ){
cout<<
"capture sig "<< sig <<endl;
}
int main(){
if( signal( SIGALRM, fn )==SIG_ERR ){
cout<< strerror( errno ) <<endl;
exit( -1 );
}
alarm( 5 );
int ret=sleep( 3 );
cout<<
"sleep return "<< ret <<endl;

ret=sleep( 10 );
cout<<
"sleep return "<< ret <<endl;
return 0;
}



8. daemon 精灵进程
* 特点:
(1)父进程的ID是1
(2)pgid( process group id ) 和sid( session id ) 就是它自己
(3)没有控制终端
* 标准写法:
fork();
setsid(); // 摆脱shell 新建session
umask( 077 );
for( int i=0; i<OPEN_MAX; i++ ) close( i ); // 关闭所有控制终端的文件描述符

例子:

#include<iostream>
#include<fstream>
#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<fcntl.h>
using namespace std;

void handler( int sig );
int main(){
if( signal( SIGUSR1, handler )==SIG_ERR ){
cout<< strerror( errno )<<endl;
exit( -1 );
}
pid_t cid=fork();
if( cid == 0 ){
cout<<
"pid="<< getpid() <<endl;
setsid();
umask( 077 );
for( int i=0; i<255; i++ ){
close( i );
}
while( 1 ){
sleep( 1 );
}
}else if( cid > 0 ){
exit( 0 );
}else{
cout<<
"error"<<endl;
exit( -1 );
}
return 0;
}
void handler( int sig ){
ofstream o(
"log.txt", ios::app );
if( o==NULL ){
exit( -1 );
}
o<<
"capture signal "<< sig <<endl;
o.close();
}



9. pipe 通信方式
* 只能父子进程间单向通信
* #include<unistd.h>
int pipe( int fd[2] );
* fd[1]可写,fd[0]可读
如果fd[1]关了, 从fd[0]读是回返回0
如果fd[0]关了,向fd[1]写会产生SIGPIPE(坏管道信号)
例子:

#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;

int main(){
int fd[2];
pipe( fd );
pid_t cid=fork();

if( cid == 0 ){
close( fd[1] );
char buf[ 100 ];
memset( buf, 0x00, sizeof( buf ) );
read( fd[0], buf, sizeof( buf ) );
cout<<
"get message: "<< buf <<endl;
exit( 0 );
}else if( cid > 0 ){
close( fd[0] );
// 不用的关掉
char buf[ 100 ];
memset( buf, 0x00, sizeof( buf ) );
cout<<
"enter a line > ";
cin.getline( buf, sizeof( buf ) );
write( fd[1], buf, strlen( buf ) );
exit( 0 );
}else{
cout<<
"error"<<endl;
exit( -1 );
}
}



10. FIFO( file in file out )通信
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo( const char* pathname, mode_t mode );
例子:

// 写入程序,运行时会阻塞,有程序读时开始执行
#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
using namespace std;

int main(){
mkfifo(
"fifo_test", 0777 );
int fd=open(
"fifo_test", O_WRONLY );
char buf[ 100 ];
memset( buf, 0x00, sizeof( buf ) );
cout<<
"enter a line > ";
cin.getline( buf, sizeof( buf ) );
write( fd, buf, strlen( buf ) );
cout<<
"write message ok"<<endl;
close( fd );
return 0;
}

// 读取程序
#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
using namespace std;

int main(){
int fd=open(
"fifo_test", O_RDONLY );
char buf[ 100 ];
memset( buf, 0x00, sizeof( buf ) );
read( fd, buf, sizeof( buf ) );
cout<< buf <<endl;
close( fd );
return 0;
}


11. 比较pipe 和 fifo
* 相同点:
都只能按顺序发送与接受数据
都是先进先出的
都可使用文件IO函数来操作
都不需要同步机制
* 差别
pipe是单向,需要两个fd 来通讯
fifo 是任意方向的,取决你用什么方式打开
pipe 只能用于相关进程,fifo 可用于任意进程间
pipe 的读者与写者必须同时存在,fifo 的不用

12. makefile
* 格式:
目标:依据,依据,...
[tab键]命令
例子:
[code]
// 头文件,类声明
#ifndef __MY_HEADER__
#define __MY_HEADER__
class A{
int v;
public:
A( int );
~A();
void disp();
};
#endif

// 类实现
#include
"my_header.h"
#include<iostream>
using namespace std;

A::A( int i ) { v=i; }
A::~A() {}
void A::disp(){
cout<<
"A[ v="<<v<<" ]"<<endl;
}

#include<iostream>
#include
"my_header.h"
using namespace std;

int main(){
A a( 20 );
a.disp();
return 0;
}

// makefile文件,命名时只有名字没有后缀
CC=g++
N=-o
C=-c
a:A_impl.o A_main.o
$(CC) $(N) a A_impl.o A_main.o;
rm *.o
A_impl.o:A_impl.cc
$(CC) $(C) $(N) A_impl.o A_impl.cc
A_main.o:A_main.cc
$(CC) $(C) $(N) A_main.o A_main.cc



可以用变量,便于更改
$@ 代表目标名
多个命令用;号隔,一个命令用多行表示时用反斜杠
编译:make -f name



irini   2005-10-17 21:47:46 评论:0   阅读:899   引用:0

发表评论>>

署名发表(评论可管理,不必输入下面的姓名)

姓名:

主题:

内容: 最少15个,最长1000个字符

验证码: (如不清楚,请刷新)

Copyright@2008 powered by YuLog