Linux程式设计入门 - fork, pthread, and signals

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("hellon");
pid = fork();

switch (pid) {
case -1: printf("failure!n"); break;
case 0: printf("I am child!n"); break;
default: printf("my child is %dn",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
执行结果:
./ex2

daemon on duty!
I am the daemon!
接下来每三秒钟,都会出现一个"I am the daemon!"的讯息,这表示您的程式已经"长驻"在系统中了。

检验行程:
ps | grep ex2

8753 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
执行:
./ex3

daemon 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("hellon");

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 %dn",pid); break;
}
} while (pid!=0&&n

if (pid>0) exit(0);

for (;;) { /* do something here */ }
}

编译:
gcc -o ex4 children.c
执行结果:
./ex4

hello
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("threadn");
sleep(1);
}
return NULL;
}

void main(void)
{
pthread_t th;

if (pthread_create(&th,NULL,mythread,NULL)!=0) exit(0);

for (;;) {
printf("main processn");
sleep(3);
}
}

执行结果:
./ex1

main 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 %dn",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
执行
./ex1

daemon on duty!

送信号
我们先找出该守护神程式的pid

PID=`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);
Signal Handling Functions
  • 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);
Structure Signal Action
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值