Linux进程编程.docx
- 文档编号:11153810
- 上传时间:2023-05-29
- 格式:DOCX
- 页数:61
- 大小:45.54KB
Linux进程编程.docx
《Linux进程编程.docx》由会员分享,可在线阅读,更多相关《Linux进程编程.docx(61页珍藏版)》请在冰点文库上搜索。
Linux进程编程
Linux程序设计入门-fork,pthread,andsignals
转自:
Linux技术中坚站
在UNIX程序设计中,学会fork及signal的运用,算是相当基本的功夫。
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()会产生一个与父程序相同的子程序,唯一不同之处在于其processid(pid)。
如果我们要撰写守护神程序,或是例如网络伺服器,需要多个进程来同时提供多个连线,可以利用fork()来产生多个相同的进程。
函数声明
pid_tfork(void);
pid_tvfork(void);
返回值:
-1:
失败。
0:
子程序。
>0:
将子程序的processid传回给父程序。
在Linux下fork()及vfork()是相同的东西。
范例一:
fork.c
在这个范例中,我们示范fork()的标准用法。
#include
#include
#include
voidmain(void)
{
pid_tpid;
printf("hello\n");
pid=fork();
switch(pid){
case-1:
printf("failure!
\n");break;
case 0:
printf("Iamchild!
\n");break;
default:
printf("mychildis%d\n",pid);break;
}
for(;;){/*dosomethinghere*/}
}
编译:
gcc-oex1fork.c
执行结果:
./ex1&
hello
mychildis8650
Iamchild!
我们可以见到,使用fork(),可将一个程式分成两个。
在分之前的程序代码只执行一次。
检验行程:
ps|grepex1
8649 p0R 0:
40./ex1
8650 p0R 0:
40./ex1
8649是父程序的pid,8650则为子程序的pid。
您会需要用到"killallex1"来杀掉两个行程。
范例二:
daemon.c
在UNIX中,我们一般都利用fork(),来实作所谓的"守护神程序",也就是DOS中所谓的"常驻程序"。
一般的技巧是将父程序结束,而子程序便成为"守护神"。
这个范例中,示范一般标准的daemon写法。
#include
#include
#include
voidmain(void)
{
pid_tpid;
pid=fork();
if(pid>0){
printf("daemononduty!
\n");
exit(0);
}
elseif(pid<0){
printf("Can'tfork!
\n");
exit(-1);
}
for(;;){
printf("Iamthedaemon!
\n");
sleep(3);
/*dosomethingyourownhere*/
}
}
编译:
gcc-oex2daemon.c
执行结果:
./ex2
daemononduty!
Iamthedaemon!
接下来每三秒钟,都会出现一个"Iamthedaemon!
"的信息,这表示您的程序已经"长驻"在系统中了。
检验进程:
ps|grepex2
8753 p0S 0:
00./ex2
注意到在范例一中,我们下的指令为"./ex1&",而在范例二中为"./ex2",没有"&"符号。
范例三:
lock.c
许多的时候,我们希望"守护神"在系统中只有一个,这时候会需要用到pidlock的技巧。
如果您注意到/var/run目录中的内容,您会发现到有许多的*.pid档,观看其内容都是一些数字,这些数字其实就是该行程的pid。
#include
#include
#include
voidmain(void)
{
FILE*fp;
pid_tpid;
if(access("/var/run/lock.pid",R_OK)==0){
printf("Existingacopyofthisdaemon!
\n");
exit
(1);
}
pid=fork();
if(pid>0){
printf("daemononduty!
\n");
fp=fopen("/var/run/lock.pid","wt");
fprintf(fp,"%d",pid);
fclose(fp);
exit(0);
}
elseif(pid<0){
printf("Can'tfork!
\n");
exit(-1);
}
for(;;){
printf("Iamthedaemon!
\n");
sleep(3);
}
}
编译:
gcc-oex3lock.c
执行:
./ex3
daemononduty!
Iamthedaemon!
再执行一次
./ex3
Existingacopyofthisdaemon!
这时如果您将该行程杀掉,并重新执行:
killallex3
./ex3
Existingacopyofthisdaemon!
您会发现daemon无法再度长驻,因为/var/run/lock.pid并没有因为进程被杀掉而删除掉。
一般来说,开机后的启动Script,会将/var/run中所有内容自动清除,以避免这个问题的发生。
如果您想要在进程被杀掉时,将/var/run/lock.pid也一并删除,那么您需要利用signal来处理这件事。
您可手动删除该档案,daemon便可再度执行。
rm/var/run/lock.pid
范例四:
children.c
如果您正在写伺服器,您可能会需要复制出许多的子进程,用以提供同时多人的服务,这时可利用fork(),一次复制出多个子进程。
最佳的例子为ApacheWWWServer。
#include
#include
#include
#defineMAX_CHILD9
voidmain(void)
{
pid_tpid;
int n;
printf("hello\n");
n=0;
do{
pid=fork();
n++;
switch(pid){
case-1:
printf("failure!
\n");
exit(-1);
break;
case 0:
printf("Iamchild%d!
\n",n);
break;
default:
printf("mychildis%d\n",pid);break;
}
}while(pid!
=0&&nMAX_CHILD
if(pid>0)exit(0);
for(;;){/*dosomethinghere*/}
}
编译:
gcc-oex4children.c
执行结果:
./ex4
hello
mychildis8863
Iamchild1!
mychildis8864
Iamchild2!
mychildis8865
Iamchild3!
mychildis8866
Iamchild4!
mychildis8867
Iamchild5!
mychildis8868
Iamchild6!
mychildis8869
Iamchild7!
mychildis8870
Iamchild8!
mychildis8871
Iamchild9!
检验进程:
ps|grepex4
8863 p0R 0:
12./ex4
8864 p0R 0:
12./ex4
8865 p0R 0:
12./ex4
8866 p0R 0:
12./ex4
8867 p0R 0:
12./ex4
8868 p0R 0:
12./ex4
8869 p0R 0:
11./ex4
8870 p0R 0:
12./ex4
8871 p0R 0:
12./ex4
thread
我假设您对thread已经有一些基本的概念,因此,在此我将著重於如何实作。
函数声明
∙int pthread_create(pthread_t * thread,pthread_attr_t*attr,void*(*start_routine)(void*),void*arg);
∙intpthread_join(pthread_tth,void**thread_return);
∙intpthread_detach(pthread_tth);
∙voidpthread_exit(void*retval);
∙intpthread_attr_init(pthread_attr_t*attr);
资料结构
typedefstruct
{
intdetachstate;
intschedpolicy;
structsched_paramschedparam;
intinheritsched;
intscope;
}pthread_attr_t;
范例一:
#include
#include
#include
#include
void*mythread(void*arg)
{
for(;;){
printf("thread\n");
sleep
(1);
}
returnNULL;
}
voidmain(void)
{
pthread_tth;
if(pthread_create(&th,NULL,mythread,NULL)!
=0)exit(0);
for(;;){
printf("mainprocess\n");
sleep(3);
}
}
执行结果:
./ex1
mainprocess
thread
thread
thread
mainprocess
thread
thread
thread
mainprocess
thread
thread
thread
mainprocess
信号singals
信号的处理可以用一大章来写,涉及的层面也会深入整个作业系统中,我并不打算这样做,因为您可能会越搞越迷糊。
这里我只告诉您如何接上信号,在实用的层面上,这样便很够用了。
您可以先利用这些基本的技巧来编写程序,等到有进一步高级应用的需要时,找一本较深入的UNIXProgramming教材,专门研究signal的写法。
一般简单的signal写法如下:
voidmysignal(intsigno)
{
/*mysignalhandler*/
}
voidinitsignal(void)
{
structsigactionact;
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
#defineLOCK_FILE"/var/run/lock.pid"
voidquit(intsigno)
{
printf("Receivesignal%d\n",signo);
unlink(LOCK_FILE);
exit
(1);
}
voidmain(void)
{
FILE*fp;
pid_tpid;
structsigactionact;
if(access(LOCK_FILE,R_OK)==0){
printf("Existingacopyofthisdaemon!
\n");
exit
(1);
}
pid=fork();
if(pid>0){
printf("daemononduty!
\n");
fp=fopen(LOCK_FILE,"wt");
fprintf(fp,"%d",pid);
fclose(fp);
}else
exit(0); if(pid<0){
printf("Can'tfork!
\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-oex1lock.c
执行
./ex1
daemononduty!
送信号
我们先找出该守护神程序的pid
PID=`cat/var/run/lock.pid`
接下来利用kill来送信号
kill$PID
Receivesignal15
程序将会结束,并且/var/run/lock.pid将会被删除掉,以便下一次daemon再启动。
注意到如果quit函数内,没有放exit(),程序将永远杀不掉。
接下来送一些其它的信号试试看。
./ex1
PID=`cat/var/run/lock.pid`
kill-HUP$PID
Receivesignal1
您可以自行试试
kill-INT$PID
kill-QUIT$PID
kill-ILL$PID
.
.
.
等等这些信号,看看他们的结果如何。
信号的定义
在/usr/include/signum.h中有各种信号的定义
#defineSIGHUP 1 /*Hangup(POSIX). */
#defineSIGINT 2 /*Interrupt(ANSI). */
#defineSIGQUIT 3 /*Quit(POSIX). */
#defineSIGILL 4 /*Illegalinstruction(ANSI). */
#defineSIGTRAP 5 /*Tracetrap(POSIX). */
#defineSIGABRT 6 /*Abort(ANSI). */
#defineSIGIOT 6 /*IOTtrap(4.2BSD). */
#defineSIGBUS 7 /*BUSerror(4.2BSD). */
#defineSIGFPE 8 /*Floating-pointexception(ANSI). */
#defineSIGKILL 9 /*Kill,unblockable(POSIX). */
#defineSIGUSR1 10 /*User-definedsignal1(POSIX). */
#defineSIGSEGV 11 /*Segmentationviolation(ANSI). */
#defineSIGUSR2 12 /*User-definedsignal2(POSIX). */
#defineSIGPIPE 13 /*Brokenpipe(POSIX). */
#defineSIGALRM 14 /*Alarmclock(POSIX). */
#defineSIGTERM 15 /*Termination(ANSI). */
#defineSIGSTKFLT 16 /*?
?
?
*/
#defineSIGCLD SIGCHLD/*SameasSIGCHLD(SystemV). */
#defineSIGCHLD 17 /*Childstatushaschanged(POSIX). */
#defineSIGCONT 18 /*Continue(POSIX). */
#defineSIGSTOP 19 /*Stop,unblockable(POSIX). */
#defineSIGTSTP 20 /*Keyboardstop(POSIX). */
#defineSIGTTIN 21 /*Backgroundreadfromtty(POSIX). */
#defineSIGTTOU 22 /*Backgroundwritetotty(POSIX). */
#defineSIGURG 23 /*Urgentconditiononsocket(4.2BSD). */
#defineSIGXCPU 24 /*CPUlimitexceeded(4.2BSD). */
#defineSIGXFSZ 25 /*Filesizelimitexceeded(4.2BSD). */
#defineSIGVTALRM 26 /*Virtualalarmclock(4.2BSD). */
#defineSIGPROF 27 /*Profilingalarmclock(4.2BSD). */
#defineSIGWINCH 28 /*Windowsizechange(4.3BSD,Sun). */
#defineSIGPOLL SIGIO /*Pollableeventoccurred(SystemV). */
#defineSIGIO 29 /*I/Onowpossible(4.2BSD). */
#defineSIGPWR 30 /*Powerfailurerestart(SystemV). */
#defineSIGUNUSED 31
函数声明:
SignalOperators
∙intsigemptyset(sigset_t*set);
∙intsigfillset(sigset_t*set);
∙intsigaddset(sigset_t*set,intsignum);
∙intsigdelset(sigset_t*set,intsignum);
∙intsigismember(const
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 进程 编程