UNIX系统程序设计

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

写在前面的话。这是去年夏天刚来SOHU的时候写的。在本版最初好像发过一遍。现在整理个人文集,重发一遍。大家见谅。说明:本文所有程序,应在UNIX类操作系统下编译执行。第一章:生成一个Process(进程)进程是什么?简单地说,进程就是在执行状态下的一个程序(包括CPU状态,所占内存的状态,等等)A进程生成了B进程,也就是说,A程序在执行的时候,又生成了另一个进程B。这个时候,我们可以把A进程叫做父进程,把B进程叫做子进程。例程序://Usage:./a.out20#includeintmain(intargc,char*argv[]){intdep_time;dep_time=atoi(argv[1])*60;//将参数中给出的20(分钟)转换成整型的秒数if(fork()==0)//生成子进程,然后父进程中止{sleep(dep_time);fprintf(stderr,!!!!!!!!\n);}return0;}上面的程序是一个闹钟程序。当你执行之后。程序不会显示什么,而是一下就回到UNIX的提示符下。但是你在命令行中指定了20分钟后你有事,那么在你执行这个程序之后20分钟,他会提醒你到时间了。本程序只是做示例用,没有检查参数是否正确,等等。生成一个新的进程,可以使用fork()函数。以下说说fork()函数。头文件:#include形式pid_tfork();参数无返回值成功时:父进程中:子进程的PID(ProcessID)子进程中:0失败时:父进程中:-1由于失败,没有生成子进程;fork()刚执行完的时候,子进程和父进程是完全一模一样的两份进程(当然,PID是不一样的)。他们的各个变量的值都是一样的,而且都认为自己已经执行完fork()了。fork()后,区分父进程和子进程,只要看fork()的返回值就行了。if(fork()==0)printf(这是子进程);elseprintf(这是父进程);同理:if(fork()==0){//接下来要子进程做的工作}else{//接下来要父进程做的工作}一般,我们会把fork()返回给父进程的值保存下来(其实就是子进程的PID),等到需要结束子进程的时候,我们关掉他,如下:pid_tchild_pid;child_pid=fork();if(child_pid==0){//......}else{//......}//......需要结束子进程的时候kill(child_pid,SIGKILL)//kill()函数是用来发给另一个进程一个消息的。以后再讲。先写这些,试试手。喜欢就顶。要是没人爱看我就不写了。呵呵。省得大家说我乱贴垃圾。以后计划贴的东西:在程序中执行UNIX命令或者另一个程序取得环境变量并利用UNIX文件系统(在程序中取得分区信息,等等)使用管道操作达到在各进程互相交流数据信号(signal)进程间共享内存用message实现进程间共享信息本文所有程序均应在UNIX系操作系统下编译执行。第二章:在程序中执行UNIX命令或者其它程序在UNIX下,像DOS的command.com那样的程序,我们称之为外壳(shell)。外壳就是一个命令解释器,你在外壳的提示符下输入命令(如同DOS的提示符一样),系统便会执行。DOS的提示符一般是C:\,当然,你想改成什么样就能改成什么样,又当然,像BBS一样贴张图上去是不太现实的。UNIX的提示符根据外壳的不同是不同的。为了更好地说明本章想讲解的内容,我们先做一个外壳试试(玩具级别的)。我们给他起名叫SSH(SohuShell)吧。想取名叫CSH,可惜CSH在没生我之前就有了。呵呵。/*简单的外壳程序*/#includeintmain(){staticcharprompt[64]=;charcommand[256];intst;fprintf(stderr,%s,prompt);//在屏幕上输出提示符while(gets(command)!=NULL)//取得命令{if(fork()==0)//生成子进程{//这里是子进程接下来要做的事if(execl(command,command,(char*)0)==(-1))//上一句是执行命令exit(1);//当出错时子进程异常中止}else{//父进程wait(&st);//等待子进程结束fprintf(stderr,%s,prompt);//输出提示符,等待命令}}return0;}执行方法:%./ssh/bin/ls当前目录下文件名一览Ctrl+D%普通的外壳在执行exit命令后会关闭。也就是说,退出一层外壳。咱们这个程序现在还做不到。愿意的话加上这个功能试试好了。所以要关闭这个外壳就得来点狠的。Ctrl+D,Ctrl+C什么的。再不你就再开一个外壳然后ps-ef再kill。再狠一些……拆硬盘,拨电源我们这里有了一个新的函数:execl()。其实他是一组函数中的一个。这组函数如下:intexecl(path,arg0,arg1,...,argn,(char*)0);intexecv(path,argv);intexecle(path,arg0,arg1,...,argn,(char*)0,envp);intexecve(path,argv,envp);intexeclp(file,arg0,arg1,...,argn,(char*)0);intexecvp(file,argv);其中的参数定义如下:char*path;char*file;char*arg0,*arg1,...,*argn;char*argv[];char*envp[];返回值:成功时:所执行的命令将会覆盖现有的进程,所以无返回值失败时:-1用过TC的朋友应该知道,TC的Process.h里有一个system()函数。这组函数其实和system()的功能差不多。比方说:execl(/bin/ls,/bin/ls,-al,/home,(char*)0);或者char*argv[];strcpy(argv[0],/bin/ls);strcpy(argv[1],-al);strcop(argv[2],/home);execv(/bin/ls,argv);都相当于在命令行下敲入了/bin/ls-al/home并且回车。(引号不是命令里的。是我解释时加上去的。别看混了)。execle()和execve(),函数名最后一个字母都是e。就是说,这两个函数在调用其它程序的同时,还可以把环境变量一起传给被调程序execlp()和execvp(),函数名最后一个字母都是p,就是说,这两个函数在使用的时候,就算你不指定命令文件所在的路径,它也会根据环境变量PATH去挨个地方找。找着就执行。找不着拉倒。比方说:setenv$path=(/bin$path)这句话将环境变量PATH的第一个路径设为/bin。这是在SHELL下执行的。C里没这东西吧。在程序中这样用这个函数execlp(ls,ls,-al,/home,(char*)0);与上面的效果一样。当然。如果你PATH变量没设好的话。它就不一定找到哪儿去了。还有一个函数是wait(),说明如下:#includepid_twait(int*stat_loc);返回值就是一个PID了。忘了PID是什么意思的朋友光顾一下我上一篇贴子。它的参数有些意思。其实它与你的子进程用什么方式结束有关系。当你的子进程以exit()方式结束的话,stat_loc所指向的地址的前8位将会是exit()的参数的后8位,而stat_loc所指向的地址的后8位是0。比方说:你的子进程是exit(1);那stat_loc所指向的地址的内容应该是0000000100000000。exit():#includevoidexit(intstatus);就算你在程序中没写exit()函数,编译器也是认为你是在最后加入了这个函数。下一篇贴子,咱们再把这个Shell完善一下本文所有程序均应在UNIX系操作系统下编译执行。第三章:增强ssh的功能(使用环境变量)还记得上次做的那个ssh吧?这回咱们把它再改一改。大家知道。C语言的main函数是这样的:intmain(intargc,char*argv[],char*envp);前两个不用说了吧。其实知道前两个的话应该也知道第三个:取得系统环境变量。UNIX和DOS一样。有着各种各样的环境变量(明确地说,应该是比DOS用得更广泛)。比方说常用的$PATH,$HOME,$USER等等。如果用的是csh,可以改~/.cshrc或者干脆直接在命令行下设就行了。这些是UNIX的东西,不属于我们在C/C++讨论的东西了。有兴趣的朋友可以到UNIX版去看一看。下面是一个取得系统环境变量并输出的小程序/*getenv.c取得系统环境变量并输出*/#includeintmain(intargc,char*argv[],char*envp[]){inti;for(i=0;envp[i]!=NULL;i++){printf(%s\n,envp[i]);}return0;}编译执行后应该是这样:%./getenvDISPLAY=:0.0HOME=/home/syuuiUSER=syuui以及你的系统中其它的环境变量。想一想它是怎么运行的:当你在命令行下敲入getenv并回车,shell就fork出一个新的进程,然后让这个进程去执行getenv,并把现在系统中的各个环境变量存入到envp里去。这个时候,原来shell的进程就是getenv进程的父进程。envp的参数是从父进程中取得的。现在你知道上一节中为什么有那两个带p的函数了?上一回做的ssh这个外壳的命令是带不了参数的。因为咱们的程序不知道要去读参数。这回不妨做一个能读参数的试试#include#defineSP0#defineNOSP1voidgetarg(char*argv[],char*p);//取得各个参数intmain(){staticcharprompt[64]=;charcommand[256],*argv[256],*p;intst;fprintf(stderr,%s,prompt);while((p=gets(command))!=NULL){getarg(argv,p);if(fork()==0){if(execv(argv[0],argv)==(-1))exit(1);}else{wait(&st);fprintf(stderr,%s,prompt);}}return0;}voidgetarg(char*argv[],char*p){inti,sp_flag;sp_flag=SP;//SP代表空格,NOSP代表非空格的意思for(i=0;*p!='\0';p++){if(sp_flag==SP&&*p!='')//如果现在状态是读过空格,但现在这个字母是非空格//那很显然,读到一个新的参数{argv[i]=p;i++;sp_flag=NOSP;}if(*p==''){*p='\0';sp_flag=SP;}}argv[i]=(char*)0;}这篇文章东西说得比较少。给大家出个问题吧:看到了吧。C能做的事情很多。谁说C不支持多进程?那是因为DOS。呵呵上回做的ssh,必须输入绝对路径才能执行。现在要求,咱们做一个只输入命令以及参数,程序自动在$PATH里定义的各路径查找这个命令。找到就执行,找不到就报错的外壳出来试试?有做出来的,请贴程序。2003-7-27关于进程状态的一些补足:众所周知,UNIX是一个多用户多任务的操作系统。所谓多任务,就是指在同一个时间内,看上去有许多任务在同时执行。这一点是与DOS不同的。所以TurboC下找不到fork()函数。当然。大家知道,实际上CPU同一个时间只能处理一件事。它是在轮流执行这些进程,由于速度很快。所以看起来像是所有进程在一起跑。同样,一个进程,它有使用CPU的时候,也有等待使用CPU的时候。这就决定了进程的几种状态。进程大致可以分为“执行中”,“准备执行”,

1 / 31
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功