UNIX系统程序设计教程(5)

2019-02-15 23:59

这个函数有什么用呢?用处可大了。

在上一章中,我们用到了SIGKILL信号。这一节我们用的还是这个信号。实际上,信号(signal)有许多。而且根据UNIX系统的不同,这些信号也不太一样。

我们简单列举一些信号例子。其用途请恕小生才疏,解释不清。有兴趣的朋友可以自己查阅一下资料。

SIGHUP , SIGINT , SIGQUIT , SIGILL , SIGTRAP , SIGABRT , SIGEMT , SEGFPE(注意,这里是SEG),SIGKILL , SIGBUS , SIGSEGV , SIGSYS , SIGPIPE , SIGALRM , SIGTERM , SIGUSR1 , SIGUSR2 , SIGCLD , SIGPWR , SIGWINCH , SIGSTOP , SIGTSTP , SIGCONT , SIGTTIN , SIGTTOU 使用kill()函数,可以将这些信号中的一个或者多个送至另一个进程。刚才的程序中,我们就是将一个SIGKILL信号送至sleep那个进程,该进程收到信号,以为是用户按下了Ctrl+C,就中止了。

知道了这些, 我们再来做一个比较有意思的程序。我们做一个cat程序(类似于DOS下的type,查看文本文件的内容)。不过这个cat程序与UNIX的cat不太一样,我们把由参数指定的文件的内容输出到屏幕上。如果你没指定文件,那么这个程序会等你五秒,在这五秒钟里,你可以从标准输入输入一个文件。然后显示。万一这五秒钟里你也没输入什么文件名。程序会把当前目录下的所有文件名打印出来(ls),以督促你输入一个文件名。

/* show.c */ #include #include #include

jmp_buf env;

int func();

int main( int argc , char *argv[] ) {

char filename[256];

signal( SIGALRM , func ); // 如果收到一个SIGALRM信号,则转至func处执行 if( argc==1 ) {

fprintf( stderr , & );

alarm(5); // 5秒钟后向自己发送一个SIGALRM信号 setjpm(env); // 设置记号 scanf( %s , filename );

execl( /usr/bin/more , more , filename , (char *)0 ); } else {

execl( /usr/bin/more , more , argv[1] , (char *)0 ); }

return 0; }

int func() { int st;

fprintf( stderr , Which file do you want to read ?\\n\\n ); if( fork()==0 ) {

execl( /bin/ls , ls , -FCa , (char *)0 ); }

wait(&st);

longjmp( env , 0 ); // 跳转至先前的setjmp()处 }

这个程序看明白了吗?可能有些不太好懂。我们从头跑一遍试试。 首先是装入头文件等等。没关系了。然后signal( SIGALRM , func ),即,当收到一个SIGALRM信号的时候,中断现在所做的工作。转而执行func()函数。

不过可惜得很,现在暂时还没人给咱们发送SIGALRM信号,我们继续往下看。下面是判断命令行参数了。如果只有这个程序的可执行文件一个参数的话(也就是说,没有指明要显示哪个文件的内容),那就打印出一个&号来。然后设置alarm(5),也就是说,在5秒后给自己发一个SIGALRM信号。下一句setjpm(env)是设置一个记号,以后当执行至longjmp()函数的时候,就会跳转到这里来。然后等待你输入文件名。

如果在这五秒种里,你输入了一个文件名的话,那么就向下执行——通过execl()函数调用系统的more命令,显示该文件的内容。然后退出。这只需要要很少的一段时间。绝对用不上一秒(看你当前目录下有多少文件了。没人会在一个目录下装上1G吧?)。最后结束程序。alarm()在5秒后发出的信号乐意咋地咋地去吧。咱不用管了。

问题是:如果在这五秒钟里,你没有输入文件名的话,事情就变得非常微妙了。这种情况下,程序将停留在 scanf( %s , filename );

这一行。等待你输入文件名。然后五秒种的期限到了。alarm()函数向这个进程发了一个SIGALRM信号。signal()函数检测到这个信号,转向func()函数处执行。 func()函数先是输出了一个提示信息 Which file do you want to read ? 。然后fork()出一个子进程。这个子进程通过execl()函数调用/bin/ls命令,显示出当前目录下的所有文件。execl()函数生成的进程覆盖掉了子进程。显示完所有文件名后退出。父进程等待子进程结束(wait())后,执行longjmp()函数,返回到刚才的setjmp那行程序结尾处。继续执行下一步,也就是再执行一次 scanf( %s , filename )。当然,上一次的scanf()已经中止了。剩下的就简单地一路到底。明白了?

为什么要fork()出一个子进程,而不用原来的进程呢?是因为execl()函数会覆盖掉当前进程。如果直接使用execl()的话,咱们这个宝贝进程就被execl()覆盖,调用完 /bin/ls 后直接结束退出了。还怎么跳来跳去的呀。呵呵。

好。又多了三个函数:

unsigned alarm( unsigned sec );

在sec指定的秒数之后,向本进程发送一个SIGALRM信号。 返回值是以前的alarm()函数执行后所剩余的时间。

还记不记得第一章中,咱们做过的闹钟程序了?那个时候是让进程休眠一段时间,然后再报时。现在我们有了另一个法宝——alarm()。这回也别让进程浪费大好时光睡大觉去了。让它做点别的什么事吧。写写试试?

#include

int setjmp( jmp_buf env ); // 设置跳转记号env

void longjmp( jmp_buf env , int val ); // 跳转至记号env处。

jmp_buf env就是跳转记号了。那么longjmp第二个参数 int val 是干什么用的呢?当你用longjmp()函数跳转至setjmp()处的时候,setjmp()函数应该会返回一个值吧?这个值就是int val,也就是longjmp()第二个参数。

setjmp()的返回值,在第一次使用的时候是0。在由longjmp()跳转过来的时候,返回值是longjmp()的第二个参数。 但是有一个特例:像刚才程序中所写的,longjmp( env , 0 ),longjmp()第二个参数是 0 的时候,setjmp()的返回值是 1 。

有很多界面比较友好的程序中,当等待用户输入数据的时候,如果用户过了一段时间还没有输入的话,程序会输入一些提示信息,或者是可供选择的选项来供用户参考。在Windows编程中,例如VC,VB等等,大概可以用定时器 timer 或者其它的什么办法来解决(我粗通VB。VC一点都不会)。在UNIX下,上面那样便是一种解决方法。当然。编程习惯不同,解决方法也不尽相同。

信号(signal)这个东西,在UNIX编程中占很重要的地位。笔者一直想用通俗的语言来解释,可是限于笔者的语文水平,所以做到的仅此而已。总的来说,signal可以看成是一种软中断。当一个signal被发送到程序的时候,会引发程序的默认过程(比方说SIGKILL的场合是强制结束)或者程序的作者自定义的过程(用signal()函数指定)。

另外。longjmp()和setjmp()两个函数由于使用了跳转(类似于goto语句,但是longjmp()能实现函数外跳转,就是说能跳到另一个函数里去。goto办不到吧),这不符合结构化程序设计的要求,所以如果不是万不得以,不推荐使用。

第九章:利用共享内存实现进程间通信

在两个进程间共享数据的方法,至今为止我们只说过利用管道。管道只是利用了输入输出重定向的原理,非常简单。而且只能在父子进程间使用。很多场合下这种方法是无法满足我们的要求的。

那么现在,我们又有了一种新的方法——利用共享内存(shared memory)。这可以使我们在任意两个进程间传递数据,而且也是相对简单容易实现的一个方法。

注意:在正常情况下,一个进程的所使用的内存区是不允许其它进程访问的。这里我们要开辟的共享内存是例外。 我们来做一个简单的剪贴板。从标准输入向mcut输入数据,mcut将其放在共享内存中,然后mpaste从共享内存中读出数据并显示。

/* mcut.c */ #include #include #include #include

int main() {

key_t shmkey;

int shmid , in_tmp ; char *head , *pos ,

in_data[4096] , *in_ptr ;

shmkey = ftok( mcut , 'a' ); // 计算标识符 // shmid开辟共享内存

shmid = shmget( shmkey , sizeof(in_data) , IPC_CREAT | 0666 ) ;

head = pos = shmat( shmid , 0 , 0 ); // 允许本进程使用这块共享内存

in_ptr = in_data ;

// 开始从标准输入输入数据,暂时存在in_data里。 while( (in_tmp=getchar()) != EOF ) {

*in_ptr = in_tmp ; in_ptr++ ; }

*in_ptr = '\\0' ; in_ptr = in_data ;

// 开始写入共享内存 while( *in_ptr != '\\0' ) {

*pos = *in_ptr ; pos++; in_ptr++; }

*pos = '\\0' ;

shmdt( head ); // 禁止本进程使用这块内存

return 0; }

/* mpaste.c */ #include #include #include #include

int main() {

key_t shmkey; int shmid;

char *head , *pos ,

out_data[4096] , *out_ptr ;

shmkey = ftok( mcut , 'a' ); // 计算标识符 // 开辟共享内存

shmid = shmget( shmkey , sizeof(out_data) , IPC_ALLOC | 0666 );

head = pos = shmat( shmid , 0 , 0 ); // 允许本进程使用这块共享内存 out_ptr = out_data ;

// 从共享内存中取得数据 while( *pos != '\\0' ) {

*out_ptr = *pos ; out_ptr++ ; pos++ ; }

*out_ptr = '\\0' ;

printf( %s\\n , out_data ); fflush( stdout );

shmdt( head ); // 禁止本进程使用这块共享内存 return 0; }

如何?明白多少了?

要使用共享内存,应该有如下步骤: 1.开辟一块共享内存 shmget()

2.允许本进程使用共某块共享内存 shmat() 3.写入/读出

4.禁止本进程使用这块共享内存 shmdt()

5.删除这块共享内存 shmctl()或者命令行下ipcrm


UNIX系统程序设计教程(5).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:小学四年级乘法除法应用题

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: