fork()及signal经常运用在daemon守护神这一类常驻程序,另外像 a4c.tty/yact/chdrv这些中文终端机程序也会用到,一般如 Mozilla/Apache/Squid等大程序几乎都会用到。
虽 然在UNIX下的程序写作,对thread的功能需求并非很大,但thread在现代的 作业系统中,几乎都已经存在了。pthread是Linux上的thread函数库,如果您 要在Linux下撰写多线程程序,例如MP3播放器,熟悉pthread的用法是必要的。
pthread及signal都可以用一大 章来讨论。在这里,我只谈及最简单及常用的技巧,当您熟悉这些基本技巧的运用后,再找一些专门深入探讨pthread及signal 程序写作的书籍来研究。这些进阶的写法,用到的机会较少,将层次分明,学习速度应该会比较快。
程序分支fork()
fork()会产生一个与父程序相同的子程序,唯一不同之处在于其process id(pid)。
如果我们要撰写守护神程序,或是例如网络伺服器,需要多个进程来同时提供多个连线,可以利用fork()来产生多个相同的进程。
函数声明
pid_t fork(void);pid_t vfork(void);
返回值:
-1 : 失败。0 : 子程序。
>0 : 将子程序的process id传回给父程序。
在Linux下fork()及vfork()是相同的东西。
范例一: fork.c
在这个范例中,我们示范fork()的标准用法。#include
#include
#include
void main(void)
{
pid_t pid;
printf("hello\n");
pid = fork();
switch (pid) {
case -1: printf("failure!\n"); break;
case 0: printf("I am child!\n"); break;
default: printf("my child is %d\n",pid); break;
}
for (;;) { /* do something here */ }
}
编译:
gcc -o ex1 fork.c执行结果:
./ex1 & hello
my child is 8650
I am child!
我们可以见到,使用fork(),可将一个程式分成两个。在分之前的程序代码只执行一次。
检验行程:
ps | grep ex1 8649 p0 R 0:40 ./ex1
8650 p0 R 0:40 ./ex1
8649是父程序的pid,8650则为子程序的pid。
您会需要用到"killall ex1"来杀掉两个行程。
范例二: daemon.c
在UNIX中,我们一般都利用fork(),来实作所谓的"守护神程序",也就是DOS中所谓的"常驻程序"。一般的技巧是将父程序结束,而子程序便成为"守护神"。这个范例中,示范一般标准的daemon写法。
#include
#include
#include
void main(void)
{
pid_t pid;
pid = fork();
if (pid>0) {
printf("daemon on duty!\n");
exit(0);
}
else if (pid<0) {
printf("Can't fork!\n");
exit(-1);
}
for (;;) {
printf("I am the daemon!\n");
sleep(3);
/* do something your own here */
}
}
编译:
gcc -o ex2 daemon.c执行结果:
./ex2daemon on duty!
I am the daemon!
接下来每三秒钟,都会出现一个"I am the daemon!"的信息,这表示您的程序已经"长驻"在系统中了。
检验进程:
ps | grep ex28753 p0 S 0:00 ./ex2
注意到在范例一中,我们下的指令为"./ex1 &",而在范例二中为"./ex2",没有"&"符号。
范例三: lock.c
许多的时候,我们希望"守护神"在系统中只有一个,这时候会需要用到pid lock的技巧。如果您注意到/var/run目录中的内容,您会发现到有许多的*.pid档,观看其内容都是一些数字,这些数字其实就是该行程的pid。#include
#include
#include
void main(void)
{
FILE *fp;
pid_t pid;
if (access("/var/run/lock.pid",R_OK)==0) {
printf("Existing a copy of this daemon!\n");
exit(1);
}
pid = fork();
if (pid>0) {
printf("daemon on duty!\n");
fp = fopen("/var/run/lock.pid","wt");
fprintf(fp,"%d",pid);
fclose(fp);
exit(0);
}
else if (pid<0) {
printf("Can't fork!\n");
exit(-1);
}
for (;;) {
printf("I am the daemon!\n");
sleep(3);
}
}
编译:
gcc -o ex3 lock.c执行:
./ex3daemon on duty!
I am the daemon!
再执行一次
./ex3
Existing a copy of this daemon!
这时如果您将该行程杀掉,并重新执行:
killall ex3
./ex3
Existing a copy of this daemon!
您 会发现daemon无法再度长驻,因为/var/run/lock.pid并没有因为进程被杀掉而删除掉。一般来说,开机后的启动Script,会将 /var/run中所有内容自动清除,以避免这个问题的发生。如果您想要在进程被杀掉时,将/var/run/lock.pid也一并删除,那么您需要利 用signal来处理这件事。
您可手动删除该档案,daemon便可再度执行。
rm /var/run/lock.pid
范例四: children.c
如果您正在写伺服器,您可能会需要复制出许多的子进程,用以提供同时多人的服务,这时可利用fork(),一次复制出多个子进程。最佳的例子为Apache WWW Server。 #include
#include
#include
#define MAX_CHILD 9
void main(void)
{
pid_t pid;
int n;
printf("hello\n");
n = 0;
do {
pid = fork();
n++;
switch (pid) {
case -1:
printf("failure!\n");
exit(-1);
break;
case 0:
printf("I am child %d!\n",n);
break;
default:
printf("my child is %d\n",pid); break;
}
} while (pid!=0&&nMAX_CHILD
if (pid>0) exit(0);
for (;;) { /* do something here */ }
}
编译:
gcc -o ex4 children.c执行结果:
./ex4hello
my child is 8863
I am child 1!
my child is 8864
I am child 2!
my child is 8865
I am child 3!
my child is 8866
I am child 4!
my child is 8867
I am child 5!
my child is 8868
I am child 6!
my child is 8869
I am child 7!
my child is 8870
I am child 8!
my child is 8871
I am child 9!
检验进程:
ps | grep ex4 8863 p0 R 0:12 ./ex4
8864 p0 R 0:12 ./ex4
8865 p0 R 0:12 ./ex4
8866 p0 R 0:12 ./ex4
8867 p0 R 0:12 ./ex4
8868 p0 R 0:12 ./ex4
8869 p0 R 0:11 ./ex4
8870 p0 R 0:12 ./ex4
8871 p0 R 0:12 ./ex4
thread
我假设您对thread已经有一些基本的概念,因此,在此我将著重於如何实作。函数声明
- int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);
- int pthread_join(pthread_t th, void **thread_return);
- int pthread_detach(pthread_t th);
- void pthread_exit(void *retval);
- int pthread_attr_init(pthread_attr_t *attr);
资料结构
typedef struct{
int detachstate;
int schedpolicy;
struct sched_param schedparam;
int inheritsched;
int scope;
} pthread_attr_t;
范例一:
#include#include
#include
#include
void * mythread(void *arg)
{
for (;;) {
printf("thread\n");
sleep(1);
}
return NULL;
}
void main(void)
{
pthread_t th;
if (pthread_create(&th,NULL,mythread,NULL)!=0) exit(0);
for (;;) {
printf("main process\n");
sleep(3);
}
}
执行结果:
./ex1main process
thread
thread
thread
main process
thread
thread
thread
main process
thread
thread
thread
main process
信号singals
信 号的处理可以用一大章来写,涉及的层面也会深入整个作业系统中,我并不打算这样做,因为您可能会越搞越迷糊。这里我只告诉您如何接上信号,在实用的层面 上,这样便很够用了。您可以先利用这些基本的技巧来编写程序,等到有进一步高级应用的需要时,找一本较深入的UNIX Programming教材,专门研究signal的写法。一般简单的signal写法如下:
void mysignal(int signo)
{
/* my signal handler */
}
void initsignal(void)
{
struct sigaction act;
act.sa_handler = mysignal;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGHUP,&act,NULL);
sigaction(SIGINT,&act,NULL);
sigaction(SIGQUIT,&act,NULL);
sigaction(SIGILL,&act,NULL);
sigaction(SIGTERM,&act,NULL);
}
范例一: lock.c
在fork的范例三中提到,在daemon被杀掉时,需要在离开前,将/var/run/lock.pid删除。这里我们可以利用signal来处理这件事。#include
#include
#include
#include
#define LOCK_FILE "/var/run/lock.pid"
void quit(int signo)
{
printf("Receive signal %d\n",signo);
unlink(LOCK_FILE);
exit(1);
}
void main(void)
{
FILE *fp;
pid_t pid;
struct sigaction act;
if (access(LOCK_FILE,R_OK)==0) {
printf("Existing a copy of this daemon!\n");
exit(1);
}
pid = fork();
if (pid>0) {
printf("daemon on duty!\n");
fp = fopen(LOCK_FILE,"wt");
fprintf(fp,"%d",pid);
fclose(fp);
} else
exit(0); if (pid<0) {
printf("Can't fork!\n");
exit(-1);
}
act.sa_handler = quit;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGTERM,&act,NULL);
sigaction(SIGHUP,&act,NULL);
sigaction(SIGINT,&act,NULL);
sigaction(SIGQUIT,&act,NULL);
sigaction(SIGUSR1,&act,NULL);
sigaction(SIGUSR2,&act,NULL);
for (;;) {
sleep(3);
}
}
编译:
gcc -o ex1 lock.c执行
./ex1daemon on duty!
送信号
我们先找出该守护神程序的pidPID=`cat /var/run/lock.pid`
接下来利用kill来送信号
kill $PID
Receive signal 15
程序将会结束,并且/var/run/lock.pid将会被删除掉,以便下一次daemon再启动。注意到如果quit函数内,没有放exit(),程序将永远杀不掉。
接下来送一些其它的信号试试看。
./ex1
PID=`cat /var/run/lock.pid`
kill -HUP $PID
Receive signal 1
您可以自行试试
kill -INT $PID
kill -QUIT $PID
kill -ILL $PID
.
.
.
等等这些信号,看看他们的结果如何。
信号的定义
在/usr/include/signum.h中有各种信号的定义#define SIGHUP 1 /* Hangup (POSIX). */
#define SIGINT 2 /* Interrupt (ANSI). */
#define SIGQUIT 3 /* Quit (POSIX). */
#define SIGILL 4 /* Illegal instruction (ANSI). */
#define SIGTRAP 5 /* Trace trap (POSIX). */
#define SIGABRT 6 /* Abort (ANSI). */
#define SIGIOT 6 /* IOT trap (4.2 BSD). */
#define SIGBUS 7 /* BUS error (4.2 BSD). */
#define SIGFPE 8 /* Floating-point exception (ANSI). */
#define SIGKILL 9 /* Kill, unblockable (POSIX). */
#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */
#define SIGSEGV 11 /* Segmentation violation (ANSI). */
#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */
#define SIGPIPE 13 /* Broken pipe (POSIX). */
#define SIGALRM 14 /* Alarm clock (POSIX). */
#define SIGTERM 15 /* Termination (ANSI). */
#define SIGSTKFLT 16 /* ??? */
#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */
#define SIGCHLD 17 /* Child status has changed (POSIX). */
#define SIGCONT 18 /* Continue (POSIX). */
#define SIGSTOP 19 /* Stop, unblockable (POSIX). */
#define SIGTSTP 20 /* Keyboard stop (POSIX). */
#define SIGTTIN 21 /* Background read from tty (POSIX). */
#define SIGTTOU 22 /* Background write to tty (POSIX). */
#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */
#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */
#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */
#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */
#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */
#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */
#define SIGPOLL SIGIO /* Pollable event occurred (System V). */
#define SIGIO 29 /* I/O now possible (4.2 BSD). */
#define SIGPWR 30 /* Power failure restart (System V). */
#define SIGUNUSED 31
函数声明:
Signal Operators- int sigemptyset(sigset_t *set);
- int sigfillset(sigset_t *set);
- int sigaddset(sigset_t *set, int signum);
- int sigdelset(sigset_t *set, int signum);
- int sigismember(const sigset_t *set, int signum);
- int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
- int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
- int sigpending(sigset_t *set);
- int sigsuspend(const sigset_t *mask);
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
评论
发表评论