版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、写在前面的话。这是去年夏天刚来SOHU的时候写的。在本版最初好像发过一遍。现在整理个人文集,重发一遍。大家见谅。说明:本文所有程序,应在UNIX类操作系统下编译执行。第一章:生成一个Process(进程)进程是什么?简单地说,进程就是在执行状态下的一个程序(包括CPU状态,所占内存的状态,等等)A进程生成了B进程,也就是说,A程序在执行的时候,又生成了另一个进程B。这个时候,我们可以把A进程叫做父进程,把B进程叫做子进程。例程序:/ Usage : ./a.out 20#includeint main( int argc , char *argv)int dep_time;dep_time =
2、 atoi( argv1 )*60 ; /将参数中给出的20(分钟)转换成整型的秒数if( fork()=0 ) /生成子进程,然后父进程中止sleep( dep_time );fprintf( stderr , !n);return 0;上面的程序是一个闹钟程序。当你执行之后。程序不会显示什么,而是一下就回到UNIX的提示符下。但是你在命令行中指定了20分钟后你有事,那么在你执行这个程序之后20分钟,他会提醒你到时间了。本程序只是做示例用,没有检查参数是否正确,等等。生成一个新的进程,可以使用 fork() 函数 。以下说说fork()函数。头文件: #include 形式 pid_t fo
3、rk();参数 无返回值 成功时: 父进程中:子进程的PID (Process ID)子进程中:0失败时: 父进程中:-1由于失败,没有生成子进程;fork()刚执行完的时候,子进程和父进程是完全一模一样的两份进程(当然,PID是不一样的)。他们的各个变量的值都是一样的,而且都认为自己已经执行完fork()了。fork()后,区分父进程和子进程,只要看fork()的返回值就行了。if( fork()=0 ) printf(这是子进程);else printf(这是父进程);同理:if( fork()=0 )/接下来要子进程做的工作else/接下来要父进程做的工作一般,我们会把fork()返回给
4、父进程的值保存下来(其实就是子进程的PID),等到需要结束子进程的时候,我们关掉他,如下:pid_t child_pid ;child_pid=fork();if( child_pid=0 )/ . .else/ . ./ . .需要结束子进程的时候kill( child_pid , SIGKILL ) / kill()函数是用来发给另一个进程一个消息的。以后再讲。先写这些,试试手。喜欢就顶。要是没人爱看我就不写了。呵呵。省得大家说我乱贴垃圾。以后计划贴的东西:在程序中执行UNIX命令或者另一个程序取得环境变量并利用UNIX文件系统(在程序中取得分区信息,等等)使用管道操作达到在各进程互相交流
5、数据信号(signal)进程间共享内存用message实现进程间共享信息本文所有程序均应在UNIX系操作系统下编译执行。第二章:在程序中执行UNIX命令或者其它程序在UNIX下,像DOS的那样的程序,我们称之为外壳(shell)。外壳就是一个命令解释器,你在外壳的提示符下输入命令(如同DOS的提示符一样),系统便会执行。DOS的提示符一般是C:,当然,你想改成什么样就能改成什么样,又当然,像BBS一样贴张图上去是不太现实的。UNIX的提示符根据外壳的不同是不同的。为了更好地说明本章想讲解的内容,我们先做一个外壳试试(玩具级别的)。我们给他起名叫SSH(Sohu Shell)吧。想取名叫CSH,
6、可惜CSH在没生我之前就有了。呵呵。/* 简单的外壳程序 */#includeint main()static char prompt64= ;char command256;int st;fprintf(stderr,%s,prompt); / 在屏幕上输出提示符while(gets(command)!=NULL) / 取得命令if(fork()=0) / 生成子进程 / 这里是子进程接下来要做的事if( execl(command,command,(char *)0)=(-1) )/ 上一句是执行命令 exit(1); / 当出错时子进程异常中止else / 父进程wait(&st); /
7、 等待子进程结束fprintf(stderr,%s,prompt);/ 输出提示符,等待命令return 0;执行方法:%./ssh/bin/ls当前目录下文件名一览Ctrl+D%普通的外壳在执行exit命令后会关闭。也就是说,退出一层外壳。咱们这个程序现在还做不到。愿意的话加上这个功能试试好了。所以要关闭这个外壳就得来点狠的。Ctrl+D,Ctrl+C什么的。再不你就再开一个外壳然后ps -ef再kill。再狠一些拆硬盘,拨电源我们这里有了一个新的函数:execl()。其实他是一组函数中的一个。这组函数如下:int execl( path , arg0 , arg1 , . , argn ,
8、 (char *)0 );int execv( path , argv );int execle( path , arg0 , arg1 , . , argn , (char *)0 , envp );int execve( path , argv , envp );int execlp( file , arg0 , arg1 , . , argn , (char *)0 );int execvp( file , argv );其中的参数定义如下:char *path;char *file;char *arg0 , *arg1 , . , *argn;char *argv;char *envp
9、;返回值: 成功时:所执行的命令将会覆盖现有的进程,所以无返回值失败时:-1用过TC的朋友应该知道,TC的Process.h里有一个system()函数。这组函数其实和system()的功能差不多。比方说:execl( /bin/ls , /bin/ls , -al , /home , (char *)0 );或者char *argv;strcpy( argv0 , /bin/ls );strcpy( argv1 , -al );strcop( argv2 , /home );execv( /bin/ls , argv );都相当于在命令行下敲入了/bin/ls -al /home并且回车。(
10、引号不是命令里的。是我解释时加上去的。别看混了)。execle()和execve(),函数名最后一个字母都是e。就是说,这两个函数在调用其它程序的同时,还可以把环境变量一起传给被调程序execlp()和execvp(),函数名最后一个字母都是p,就是说,这两个函数在使用的时候,就算你不指定命令文件所在的路径,它也会根据环境变量PATH去挨个地方找。找着就执行。找不着拉倒。比方说:setenv $path = ( /bin $path ) 这句话将环境变量PATH的第一个路径设为/bin。这是在SHELL下执行的。C里没这东西吧。在程序中这样用这个函数execlp( ls , ls , -al
11、, /home , (char *)0 );与上面的效果一样。当然。如果你PATH变量没设好的话。它就不一定找到哪儿去了。还有一个函数是wait(),说明如下:#include pid_t wait(int *stat_loc);返回值就是一个PID了。忘了PID是什么意思的朋友光顾一下我上一篇贴子。它的参数有些意思。其实它与你的子进程用什么方式结束有关系。当你的子进程以exit()方式结束的话,stat_loc所指向的地址的前8位将会是exit()的参数的后8位,而stat_loc所指向的地址的后8位是0。比方说:你的子进程是exit(1);那stat_loc所指向的地址的内容应该是0000
12、 0001 0000 0000。exit():#include void exit(int status);就算你在程序中没写exit()函数,编译器也是认为你是在最后加入了这个函数。下一篇贴子,咱们再把这个Shell完善一下本文所有程序均应在UNIX系操作系统下编译执行。第三章:增强ssh的功能(使用环境变量)还记得上次做的那个ssh吧?这回咱们把它再改一改。大家知道。C语言的main函数是这样的:int main( int argc , char *argv , char *envp );前两个不用说了吧。其实知道前两个的话应该也知道第三个:取得系统环境变量。UNIX和DOS一样。有着各种
13、各样的环境变量(明确地说,应该是比DOS用得更广泛)。比方说常用的$PATH,$HOME,$USER等等。如果用的是csh,可以改 /.cshrc或者干脆直接在命令行下设就行了。这些是UNIX的东西,不属于我们在C/C+讨论的东西了。有兴趣的朋友可以到UNIX版去看一看。下面是一个取得系统环境变量并输出的小程序/* getenv.c 取得系统环境变量并输出 */#includeint main ( int argc , char *argv , char *envp )int i;for( i=0 ; envpi!=NULL ; i+ )printf( %sn , envpi );return
14、 0;编译执行后应该是这样:%./getenvDISPLAY=:0.0HOME=/home/syuuiUSER=syuui以及你的系统中其它的环境变量。想一想它是怎么运行的:当你在命令行下敲入getenv并回车,shell就fork出一个新的进程,然后让这个进程去执行getenv,并把现在系统中的各个环境变量存入到envp里去。这个时候,原来shell的进程就是getenv进程的父进程。envp的参数是从父进程中取得的。现在你知道上一节中为什么有那两个带p的函数了?上一回做的ssh这个外壳的命令是带不了参数的。因为咱们的程序不知道要去读参数。这回不妨做一个能读参数的试试#include #de
15、fine SP 0#define NOSP 1void getarg( char *argv , char *p ); /取得各个参数int main()static char prompt64= ;char command256, *argv256, *p;int st;fprintf( stderr , %s , prompt );while( (p=gets(command)!=NULL )getarg( argv , p );if( fork()=0 )if( execv(argv0,argv)=(-1) )exit(1);elsewait( &st );fprintf( stderr
16、 , %s , prompt);return 0;void getarg( char *argv , char *p )int i , sp_flag ;sp_flag=SP; /SP代表空格,NOSP代表非空格的意思for( i=0 ; *p!=0 ; p+ )if( sp_flag=SP & *p!= )/如果现在状态是读过空格,但现在这个字母是非空格/那很显然,读到一个新的参数argvi=p;i+;sp_flag=NOSP;if( *p= )*p=0;sp_flag=SP;argvi=(char *)0;这篇文章东西说得比较少。给大家出个问题吧:看到了吧。C能做的事情很多。谁说C不支持多
17、进程?那是因为DOS。呵呵上回做的ssh,必须输入绝对路径才能执行。现在要求,咱们做一个只输入命令以及参数,程序自动在$PATH里定义的各路径查找这个命令。找到就执行,找不到就报错的外壳出来试试?有做出来的,请贴程序。2003-7-27 关于进程状态的一些补足:众所周知,UNIX是一个多用户多任务的操作系统。所谓多任务,就是指在同一个时间内,看上去有许多任务在同时执行。这一点是与DOS不同的。所以Turbo C下找不到fork()函数。当然。大家知道,实际上CPU同一个时间只能处理一件事。它是在轮流执行这些进程,由于速度很快。所以看起来像是所有进程在一起跑。同样,一个进程,它有使用CPU的时候
18、,也有等待使用CPU的时候。这就决定了进程的几种状态。进程大致可以分为“执行中”,“准备执行”,“睡眠中”三种状态。执行中:进程在占用CPU。准备执行:想要使用CPU,但是CPU被别的进程占用中,所以等待CPU空闲。睡眠中:等待事件发生的进程。比方说,等待输入输出结束的进程。我们用fork()生成一个新的进程,或者用exec函数组覆盖当前进程后。当这个子进程结束的时候要给父进程送去一个信号(signal,后述),然后转为zombie状态。zombie状态是指一个进程已经结束,所占内存空间等等已经返还给系统,但是在系统的进程列表中仍然存在的这么一个状态。当父进程执行wait()后,子进程才真正完
19、全结束。如果父进程先一步结束了,那么由init代替父进程。所以,wait()不只是等待子进程结束。它还有上面所说的这个任务。我们写个程序来看一下:/* zombie */#includeint main()int st;if( fork()=0 )exit(1); /子进程生成后直接结束elsesleep( 300 ); /休眠300秒wait( &st );printf(Return code=%dn,i);return 0;编译后执行%./zombie &这个时候,程序在后台运行%ps -ef | grep zombie看一下,是不是有一个进程处在zombie状态?本文所有程序均应在UNI
20、X系操作系统下编译执行。第四章:文件系统UNIX所管理的机器一般是大型机而不是PC。所管理的硬盘一般也非常大。所以一般分成几个区,每个区都有其单独的文件系统。比方说你能大概能找到这样的一些文件/dev/sd/c0t0d0s0/dev/sd/c0t0d0s1. .当UNIX启动的时候,分区被挂装(mount)并统一成一个树状的文件系统分区的物理构造咱们暂且放在一边,先写个程序,读一下分区信息试试。/* ndf.c 计算参数所指定的分区的剩余空间比率 */#include #include #include int main(int argc , char *argv)struct statvfs
21、 buf1;sync();if( statvfs(argv1,buf)!=0 )fprintf(stderr , Cannot read super block !n);exit(1);fprintf(stderr , %4.1f % freen,(float)buf0.f_bfree / buf0.f_blocks*100 );return 0;编译执行:%./ndf /49.8 % free这里用了一个statvfs函数。其具体如下:#include#includeint statvfs( char *path , struct statvfs *buf );返回值: 成功时: 0失败时:
22、 -1还有一个sync()函数。用来更新分区的super block;void sync();UNIX系统为了加快处理速度,将分区的super block信息读到内存中保存。sync()函数就是把在内存中保存的super block信息再写回到硬盘上去。UNIX系统使用好几种文件系统。有S5,ufs,VxFS等等。虽然这些文件系统的构造非常不同,但一通百通,咱们在这几篇贴子里只讨论一下比较容易理解,而且“经典的”S5文件系统。(别的我也不会。呵呵)S5: 文件名最长14字节,构造简单ufs: 文件名最长255字节,BSD所用的文件系统。VxFS: Veritas Softwave公司开发的文件
23、系统。出现错误时可以快速恢复。由于这种文件系统保证文件在硬盘上连续存放,所以处理速度很快。现在说一下S5分区的构造一个分区包含如下四部分(按顺序): boot block super block i node block data block boot block :这个部分在分区的最开始处,用来存放引导程序。就算是不能引导的分区一样有boot block,这个时候这部分就没有用了。不过一般这部分也不大。大多数只有512或者1024字节。super block :super block在boot block之后,用来存放这个分区全体的管理信息。上面那个ndf.c就是读的这部分所存储的信息。里边
24、存放了i node block的大小,free block数组等等。根据这些信息可以得知data block的开始位置。i node block :i node是index node的缩写。i node block就是存放i node的部分UNIX把一切都看成是个文件。包括目录以及设备等等的所有的文件都有一个i node号,作为这个文件的管理信息。文件本身存在于数据区,但是i node号存在i node block里。主要包含文件的模式,链接数,文件所有者,文件大小,在硬盘上的位置,最后读写时间,最后更新时间等信息。为了加快存储速度,系统会把一定数量的i node存至内存。UNIX系统不一样,
25、存多少也就不一样。data block :这部分就是存放数据本身的了。这部分被分成一定大小的块,如同DOS的扇区一样。一般大小是1024字节,分到4096的也有。解说到这里,我们再来写个程序。打开一个目录,然后把这个目录下所有的文件的i node号及文件名输出来。/* nls.c */#include #define DIRSIZ 14int main( int argc , char *argv )struct dirint i_no;char f_name;struct dir dir_data1;FILE *fp;fp=fopen(argv1,r);while( fscanf(fp,%i
26、%s,&(dir_data0.i_no),dir_data0.f_name)!=EOF )printf(%i %sn , dir_data0.i_no , dir_data0.f_name );fclose(fp);return 0;%./nls /. .2048 usr2049 home. .别忘了,在UNIX下,目录也当成文件。最近,为了使目录的格式变得通用而不再依赖于操作系统,程序中大多使用统一的格式。这种情况下,我们最好就不直接用fopen()打开目录,而使用opendir(),readdir()等函数比较好。重写一下上面的程序。/* nls2.c */#include #includ
27、e int main( int argc , char *argv )DIR *fp;struct dirent *p;fp=opendir(argv1);while( (p=readdir(fp)!=NULL )printf(%i %sn, p-d_ino , p-d_name );closedir(fp);return 0;执行结果和上面一样。函数说明如下:#include DIR *opendir( char *dirname ); 打开一个目录,成功时返回DIR结构体的地址,失败返回NULLstruct dirent *readdir( DIR *dirp ); 取得打开目录中下一个文
28、件的信息,成功时返回下一个文件信息的地址,失败时,或者读至末尾时返回NULLint closeidr( DIR *dirp ); 关闭目录,成功时返回0,失败时返回-1注意:readdir在成功地读出一项之后,会自动指向下一项。假设你有如下程序:struct dirent *p;p=readdir(fp);p+; /千万不要像这行这样写。你无法保证这目录里的文件信息是连续存放的。你只要一遍一遍地用readir()这个函数就行了。它帮你全搞定本文所有程序均应在UNIX系操作系统下编译执行。第五章:目录及文件操作上一章我们说了一些UNIX的文件系统的物理构造。下面我们来看看具体怎么对文件进行操作。
29、当然这里所说的文件及目录操作不是fopen()。呵呵。我们要做一些fopen办不到的事。/* newer.c 比较参数所指定的两个文件将其中新的文件的文件名输出来 */#include #include #include int main( int argc , char *argvp )struct stat buf2 , *p ;if( argc!=3 )fprintf( stderr , Usage : %s file1 file2n , argv0 );exit(1);p=buf;if( stat(argv1,p)!=0 ) /取得第一个文件的信息fprintf( stderr , %
30、s not found !n , argv1 );exit(1);p+;if( stat(argv2,p)!=0 ) /取得第二个文件的信息fprintf( stderr , %s not found !n , argv2 );exit(1);if( buf0.st_mtime buf1.st_mtime ) /比较最终更新时间printf( %sn , argv1 );elseprintf( %sn , argv2 );return 0;执行结果:%newer afile bfilebfile使用stat()函数,可以得到文件的信息。这些信息是在i node中保存的这个文件信息的一部分。得到
31、的信息将会保存在stat.h中定义的stat型的结构体中。stat()函数解释如下:#include #include int stat( char *path , struct stat *buf );返回值: 成功时:0失败时:-1我们再来写一个玩玩 #include #include #include #define MASK 0555 /这个数字的意思等一下解释,他代表“可读”和“可执行”int main( int argc , char *argv )struct stat buf1;mode_t mode;if( argc!=2 )fprintf( stderr , Usage :
32、 %s filen , argv0 );exit(1);if( stat(argv1,buf)!=0 )fprintf( stderr , Cannot read i-noden );exit(1);mode = ( buf0.st_mode & MASK ); /计算文件新的权限标志if ( chmod(argv1,mode)!=0 ) /改变文件的权限标志fprintf( stderr , Cannot change moden );exit(1);return 0;现在来解释一下0555这个数字的意思。众所周知,UNIX是一个多用户多任务的操作系统。每个用户有自己的权限,这个权限限制了用
33、户可以做哪些事,不可以做哪些事。对于文件来说,用户可以分成四类:root(根用户,超级用户)这个用户是系统的管理呐,权限最大,可以随意读写任何文件。owner(文件拥有者)实际拥有文件的用户。文件属主。group(组成员)用户所在的用户组的成员other以上三类之外的其它用户UNIX中,每个文件信息中包括一组9位的权限标志。分别给文件属主,用户组和其他用户指定对该文件的读、写和执行权。请看下面的例子:%ls -l /bin/ls-rwxr-xr-x 1 root bin 27281 Aug 15 2002 /bin/ls*重要是看-rwxr-xr-x,第一个 - 表示这是一个普通文件,这个位置
34、也可以出现些别的字符,比方说目录的话这里会是 d 。而 l 表示一个链接。余下的9位可以分成三段,每段三位。本例中从左至右rwx 表示文件的属主拥有文件的读,写,执行权r-x 表示同组的用户拥有文件的读,执行权(注意 ,“写”权限的位置是一个 - )r-x 表示其它的用户拥有文件的读,执行权文件的访问权限还可以用三位8进制来表示。如上例rwx r-x r-x 可以换成111 101 101 (有该权限,则该位为1,否则为0)换成8进制, 二进制的111是八进制的7 , 二进制的101是八进制的5。现在看看0555是什么意思?就是说,可以读,可以写的意思。把0555和原来文件的权限标志做与运算,
35、得到的新的权限标志就是把原来的文件权限标志中所有的写权限全取消了。其余权限变。然后在程序中用chmod()把这个新的权限标志赋给文件即可。chomd()函数用法如下:#include #include int chmod( char *path , mode_t mode );返回值: 成功时:0失败时:-1关于目录,还有另一个比较有用的函数,即chdir()。用这个函数可以在程序中转换当前目录。#include #include int chdir( char *path );返回值: 成功时:0失败时:-1以上两章,简单地叙述了一下UNIX的文件系统以及在UNIX C中对文件的操作方法。并
36、列举了常用的一些函数。下一章,我们将简单地叙述一下UNIX C的输入输出,以及用管道(pipe)实现两个进程互换数据。本文所有程序均应在UNIX系操作系统下编译执行。第六章:标准输入输出以及管道操作标准输入输出大概所有的操作系统都差不多吧。从键盘输入。从屏幕输出。除非你用的还是打纸带的老家伙。呵呵。主要说一下管道操作。注意:此处所说的管道操作不是% cat -n test.c | more 这是在提示符状态下使用管道,把第一个程序(cat)的输出作为输入传给第二个程序(more)。我们现在要在程序中使用的管道原理与此相同。将某一个程序的输出变为另一个程序的输入。做一个石头剪子布的程序。其中包括
37、一个父程序和一个子程序。注意是两个程序,不是两个函数。也不是两个进程。不过因为父程序运行的时候要通过exec()函数来执行子程序。所以我们也可以把它看成是两个进程。父进程取得用户的输入(石头S,剪子C,布P中的某一个,P=0,S=1,C=2)并通过管道传给子进程子进程取得父进程传来的数字,加上自己的PID后做为种子数,生成一个随机数然后被3除,得出来一个余数是0、1或者2。再通过管道传回给父进程。父进程根据两个数字(用户输入的,以及子进程传回来的)判定胜负,输出/* parent. */#include #include int main()int i , j ,st , fd2 ;pid_t
38、 pid ;static int result33=1,0,2,2,1,0,0,1,2;char argv13 , argv23 ,ch ;ch=getchar();switch(ch)case P: i=0;break;case S: i=1;break;case C: i=2;break;default:fprintf( stderr , Enter P , S or C , Please!n );exit(1);if( pipe(fd)!=0 ) /建立管道fprintf( stderr , PIPE Error !n );exit(1);sprintf( argv1 , %d , fd
39、0 );sprintf( argv1 , %d , fd1 );switch( pid=fork() ) / fork出一个新的进程,执行子程序case 0:if( execl(child,child,argv1,argv2,(char *)0) = (-1) exit(1);/执行了子程序break;case -1:fprintf( stderr , fork Error !n );exit(1)write( fd1, &i , sizeof(i) ); /向管道写数据wait( &st ); /等待子程序结束read( fd0 , &j , sizeof(j) ); /从管道读数据swit
40、ch(resultij)case 0:printf( You wonn );break;case 1:printf( Samen );break;case 2:printf( You lostn );close(fd0);close(fd1);return 0;/* child.c */#include int main(int argc , char *argv)int i,j,read_fd,write_fd;read_fd=atoi( argv1 ); /设定输入用管道write_fd=atoi( argv2 ); /设定输出用管道read( read_fd , &i , sizeof(
41、i) ); /从管道中取得数据srand( i+getpid() ); /设定随机数的种子数j=rand()%3; /生成随机数write( write_fd , &j , sizeof(j) ); /写向管道close( read_fd );close( write_fd );return 0;编译执行父程序即可。这种管道是用来在父子进程间传递数据的。如同大家在程序中所见:父进程开辟管道,然后生成子进程执行子程序,并将管道参数作为main()函数的参数传给子程序。通过一个相同的管道实现读写。开辟管道时,我们用到了这个函数:int pipe( int fd2 ); 开辟一个管道参数fd2是一个
42、有两个元素的数组。可以看成是两个管道的记述子。fd0用来读,fd1用来写。返回值: 成功时:0失败时:-1读取/写入管道时,我们用到了下面函数读取管道中的数据int read( int fd , char *buf , unsigned int nbyte );向管道中写入数据int write( int fd , char *buf , unsigned int nbyte );其中,fd是管道记述子,也就是我们前面说的fd0或者fd1,buf装数据,nbyte指定读/写数据的数量,单位是字节。成功时返回0,失败时返回-1。由于准备考研。这篇文章耽误了一些时日。最近还有些事,也许下一篇也得几
43、天后才能再贴。另外,在此向诸位致歉。我的程序是在学校的UNIX下写的,一般是用软盘带回来,写上一篇贴子程序的时候没有带软盘,只好打印出来回来再敲。在输入的时候有一个错误(现已改正)int main( int argc , char *argvp);应为int main( int argc , char *argv);下一章准备说说UNIX的进程(Process)和信号(signal)。另外,感谢版主将我前几篇贴子选进了精华区。愚作不堪,如有错误及不到之处请诸位高人指正为盼。对本贴内容如有不明,请给我留言。我会尽快回答(如果我明白的话)。谢谢远方的洁白的哈达,雾中雪,初学无罪,hellosam,
44、xiaoyu等朋友。有你们的支持,我才有信心继续写下去。愿拙文能对诸位学习C语言有所帮助。下章见。本文所有程序均应在UNIX系操作系统下编译执行。第七章:UNIX进程(Process)及信号(signal)在进程执行过程中,如果出现什么事件(event),系统将会给进程一个信号。进程可以在得到这个信号后做些适当的处理。还是与以前一样,咱们先来个小程序吧。/* slot.c */#include #include int x3;long kin;int kekka();int main( int argc , char *argv )srand( getpid() ); /* 设定随机数的种子数
45、 */signal( SIGKILL , kekka ); /* 当Ctrl+C按下时,执行kekka函数 */kin = atoi( argv1 ); /* 取得赌注数目 */printf(press Ctrl+C to stop maching !n);while(1)x0=rand()%10;x1=rand()%10;x2=rand()%10;printf(%d %d %dn,x0,x1,x2);fflush( stdout );return 0;int kekka() if( x0=x1 & x1=x2 ) printf(Great , you won %d n , kin*3000)
46、;elseprintf(You lost %dn,kin);exit(0); /* 游戏结束。退出程序 */执行方法:%./slot 100执行后。画面上会出现一排排的数字。当你按下Ctrl+C的时候,程序得到信号,跳转至kekka()函数入,判断随机数。然后退出程序。这里有这样一个新函数。是我们在UNIX程序设计中时常用得到的。#include int ( *signal( int sig , void (*func() )();返回值: 成功时:进程中断地址错误时:SIG_ERR这个函数挺不好懂呵。解释解释。它的意思是,当收到SIG这个信号时,跳转至func()这个函数处继续执行。int
47、sig这个参数其实好办。下面我们会给出一个表。将其所有的参数一并列出。这是在头文件中#define的,代表各种不同的信号。void ( * func() )这个参数是一个函数指针。意思是在SIG信号被传给进程后,所要执行的部分。此外,func()这个参数还可以为如下值:SIG_DEL 系统的默认动作SIG_IGN 忽略这个信号。signal作用只有一次,当一个信号发生,触发signal动作的之后,signal就不起作用了。比方说,在上例中,如果你把所有的exit(0)全去掉(不退出程序),程序会进入无限循环中。你按一次Ctrl+C,signal执行一次,显示出结果。然后继续循环,生成随机数,输
48、出。你再按Ctrl+C,signal就不好使了。如果需要重复使用signal()函数,可以考虑在signal调用的动作函数中再加一个signal,比方说在上例的程序中再加一个 signal( SIGKILL , kekka );加了signal这个函数后,整个程序如果没收到信号,则正常执行。否则跳转至signal中指定的func()函数处继续。当然,我们不可能,也没有必要去为每一个信号指定一个动作。当未指定动作的信号发生的时候,系统会执行默认的动作。比方说我们知道:当Ctrl+C按下的时候,正常默认的动作是结束程序。但是,Ctrl+C按下时,发生的信号是SIGKILL,如果你为SIGKILL这
49、个信号指定了一个动作的话,系统将去执行你指定的这个动作,而不管原来默认的了。打个比方让大家更容易懂一些:比方说:你在睡午觉。突然来了一个美女(帅哥)(Ctrl+C)让你陪她/他去逛街购物共进晚餐(此处删去若干字),你正常的默认的动作是马上起来陪她出去玩(程序中止)。但是你妈说不许去,你妈说你得在家擦窗户,你只好留在家擦窗户(signal()指定的动作)。擦完窗户之后你可以选择陪美女去逛街(默认动作,Ctrl+C的埸合是退出程序,即上例程序中的exit(0)),可以选择继续睡觉(回到原来中断的地方继续执行,上例程序中如果没有exit(0),便会回到中断处继续执行)。(大哥。打个比方嘛。干嘛拿臭鸡
50、蛋砸我)signal种类非常多,在此不一一列出。使用这个函数,你可以防止用户按下Ctrl+C结束程序。还可以做很多其它的事只有你想不到的,没有C做不到的。注意:根据UNIX系统的不同,signal的定义是不一样的。比方说,有的老式UNIX工作站上SIGINT是按下del键后发生的信号,而有些机型上刚是按下Ctrl+Z发生的。在使用时要注意。再一次感谢SOHU C/C+论坛的朋友们的支持。我考上研究生了。下一次,准备说说UNIX下C程序中,如何向别的进程发送信号。本文所有程序均应在UNIX系操作系统下编译执行。第八章:向其它进程传递信号上一章,我们简单地说了一下信号。信号不仅仅可以在本进程内使用
51、,还可以向其它的进程传递。确切地说,一般情况下,一个进程收到的信号绝大部分是从系统的INIT传来的。而INIT也是一个进程。用过UNIX或者LINUX的朋友大概都知道,UNIX系的操作系统里,有一个kill命令。如果你的一个进程无法中止,可以使用kill命令强行干掉它。%kill mypro我们自己也可以做个简单的kill命令。/* nkill.c */#include #include #include int main( int argc , char *argv )pid_t pid;if( argc != 2 ) / 检查命令行参数个数fprintf( stderr , Usage :
52、 %s PID n , argv0 );exit(1);pid = atol( argv1 );if( kill( pid , SIGKILL )!=0 ) / 向pid这个进程传送一个SIGKILL信号fprintf( stderr , kill failedn );return 0;执行例:%sleep 300 & (后台睡眠300秒)1 520%nkill 5201 + Killed sleep 300你用自己写的程序杀死了一个sleep进程。没事。杀了就杀了吧。留着也是垃圾说明一下,众所周知,UNIX是非常注意用户的权限的。所以,如果你向你自己生成的进程发送SIGKILL的话,这个进程
53、会中止。如果你是向别的(你没有写权限的)进程发送SIGKILL的话,除非那个进程允许你动它,否则你发了也是白发。打个不恰当的比较下流一些的比方:如果你要求亲一下你自己老婆的话(你有“亲”的权限),你会如愿以偿。但是如果你想亲一下别人的老婆的话(你没有“亲”的权限),除非他及她同意,否则呵呵。你死定了。所以,执行上面的程序的时候,你必须保证你有权限关掉那个进程。可不是说你ps -ef然后随便挑一个进程就能关的。要不那黑客也太好当了。几乎是咱们的老规矩了:先写个程序,然后解释这个程序,说说其中新出现的函数。这回还照旧吧。大家看到了。这里新出来一个kill()函数。解释如下:#include #include int kill( pid_t pid , int sig );返回值: 成功时:0失败时:-1这个函数是向进程ID(PID)为pid的进程传送一个sig信号。这个函数有什么用呢?用处可大了。在上一章中,我们用到了SIGKILL信号。这一节我们用的还是这个信号。实际上,信号(signa
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年南通客运资格证考试题目
- 吉首大学《家具设计》2021-2022学年第一学期期末试卷
- 吉首大学《插画设计B》2021-2022学年第一学期期末试卷
- 吉林艺术学院《艺术概论》2021-2022学年第一学期期末试卷
- 吉林艺术学院《曲式基础》2021-2022学年第一学期期末试卷
- 吉林艺术学院《行草临摹与创作》2021-2022学年第一学期期末试卷
- 吉林艺术学院《CG美宣图创作实践》2021-2022学年第一学期期末试卷
- 2024年大众帕萨特购买协议书模板
- 引进外劳协议书范文模板范文
- 吉林艺术学院《节奏训练II》2021-2022学年第一学期期末试卷
- 文件与文件夹测试题(含参考答案)
- 电工安全培训课件
- 维修工程技术标
- 《长津湖》电影赏析PPT
- 人音版初中八年级上册音乐教案 全册
- GB/T 588-2009船用法兰青铜截止止回阀
- 反歧视虐待、骚扰控制程序A
- Python数据可视化课程教学大纲
- 基坑工程作业活动风险分级管控清单
- 光动力治疗在气道肿瘤中的临床应用课件
- 小学语文人教三年级上册 群文阅读《奇妙的中心句》
评论
0/150
提交评论