<% dim ModuleName,InfoID,ChannelShortName,CorrelativeArticle,InstallDir,ChannelDir,Keyword,PageTitle,ArticleIntro,Articlecontent Keyword=stripHTML("C语言,多进程,编程") PageTitle=stripHTML("C语言多进程编程") ArticleIntro=stripHTML("") Articlecontent=stripHTML("写在前面的话  本文主要根据本人在UNIX系统上的编程实践经验总结而成, 既做为自己在一个时期内编程实践的部分总结, 又可成为文章发表. 对UNIX程序员初学者…") ModuleName = stripHTML("programme") InfoID = stripHTML("180896") ChannelShortName=stripHTML("编程") InstallDir=stripHTML("http://www.77169.com/") ChannelDir=stripHTML("programme") %> C语言多进程编程 - 华盟网 - http://www.77169.com
您现在的位置: 华盟网 >> 编程 >> C语言 >> 正文

C语言多进程编程

2015/2/5 作者:不详 来源: 华盟收集
导读 <% if len(ArticleIntro)<3 then Response.Write Articlecontent 'Response.Write "Articlecontent" else Response.Write ArticleIntro 'Response.Write "ArticleIntro" end if %>

  写在前面的话

  本文主要根据本人在UNIX系统上的编程实践经验总结而成, 既做为自己在一个时期内编程实践的部分总结, 又可成为文章发表. 对UNIX程序员初学者来说是一个小小的经验, 仅供参考; 对UNIX老手来说则不值一哂, 请各位多多指教.

  一.多进程程序的特点

  由于UNIX系统是分时多用户系统, CPU按时间片分配给各个用户使用,而在实质上应说CPU按时间片分配给各个进程使用,每个进程都有自己的运行环境以使得在CPU做进程切换时不会"忘记"该进程已计算了一半的"半成??. 以DOS的概念来说, 进程的切换都是一次"DOS中断"处理过程, 包括三个层次:

  (1)用户数据的保存: 包括正文段(TEXT), 数据段(DATA,BSS), 栈段(STACK),共享内存段(SHARED MEMORY)的保存.

  (2)寄存器数据的保存: 包括PC(program counter,指向下一条要执行的指令的地址), PSW(processor status Word,处理机状态字), SP(stack pointer,栈指针),PCBP(pointer of process control block,进程控制块指针), FP(frame pointer,指向栈中一个函数的local变量的首地址), AP(augumentpointer,指向栈中函数调用的实参位置), ISP(interrupt stack pointer,中断栈指针),

  以及其他的通用寄存器等.

  (3)系统层次的保存:

  包括proc,u,虚拟存储空间管理表格,中断处理栈.以便于该进程再一次得到CPU时间片时能正常运? 既然系统已经处理好所有这些中断处理的过程, 我们做程序还有什么要担心的呢? 我们尽可以使用系统提供的多进程的特点, 让几个程序精诚合作,简单而又高效地把结果给它搞出来.

  另外,UNIX系统本身也是用C语言写的多进程程序,多进程编程是UNIX的特点,当我们熟悉了多进程?将会对UNIX系统机制有一个较深的认识.首先我介绍一下多进程程序的一些突

  出的特点:

  1.并行化

  一件复杂的事件是可以分解成若干个简单事件来解决的, 这在程序员的大脑中早就形成了这种概念, 首先将问题分解成一个个小问题, 将小问题再细分, 最后在一个合适的规模上做成一个函数. 在软件工程中也是这么说的. 如果我们以图的方式来思考, 一些小问题的计算是可以互不干扰的, 可以同时处理, 而在关键点则需要统一在一个地方来处理, 这样程序的运行就是并行的, 至少从人的时间观念上来说是这样的. 而每个小问题的计算又是较简单的.

  2.简单有序

  这样的程序对程序员来说不亚于管理一班人, 程序员为每个进程设计好相应的功能, 并通过一定的通讯机制将它们有机地结合在一起, 对每个进程的设计是简单的, 只在总控部分小心应付(其实也是蛮简单的), 就可完成整个程序的施工.

  3.互不干扰

  这个特点是操作系统的特点, 各个进程是独立的, 不会串位.

  4.事务化

  比如在一个数据电话查询系统中, 将程序设计成一个进程只处理一次查询即可, 即完成一个事务. 当电话查询开始时, 产生这样一个进程对付这次查询; 另一个电话进来时, 主控程序又产生一个这样的进程对付, 每个进程完成查询任务后消失. 这样的编程多简单, 只要做一次查询的程序就可以了.

  二.常用的多进程编程的系统调用

  1.fork()

  功能:创建一个新的进程.

  语法:#include

  #include

  pid_t fork();

  说明:本系统调用产生一个新的进程, 叫子进程, 是调用进程的一个复制品. 调用进程叫父进程, 子进程继承了父进程的几乎所有的属性:

  . 实际UID,GID和有效UID,GID.

  . 环境变量.

  . 附加GID.

  . 调用exec()时的关闭标志.

  . UID设置模式比特位.

  . GID设置模式比特位.

  . 进程组号.

  . 会话ID.

  . 控制终端.

  . 当前工作目录.

  . 根目录.

  . 文件创建掩码UMASK.

  . 文件长度限制ULIM99v.

  . 预定值, 如优先级和任何其他的进程预定参数, 根据种类不同决定是否可以继承.

  . 还有一些其它属性.

  但子进程也有与父进程不同的属性:

  . 进程号, 子进程号不同与任何一个活动的进程组号.

  . 父进程号.

  . 子进程继承父进程的文件描述符或流时, 具有自己的一个拷贝并且与父进程和其它子进程共享该资源.

  . 子进程的用户时间和系统时间被初始化为0.

  . 子进程的超时时钟设置为0.

  . 子进程的信号处理函数指针组置为空.

  . 子进程不继承父进程的记录锁.

  返回值: 调用成功则对子进程返回0, 对父进程返回子进程号, 这也是最方便的区分父子进程的方法. 若调用失败则返回-1给父进程,

  子进程不生成.

  例子:pid_t pid;

  if ((pid=fork())>0) {

  /*父进程处理过程*/

  }

  else if (pid==0) {

  /*子进程处理过程*/

  exit(0); /*注意子进程必须用exit()退出运行*/

  }

  else {

  printf("fork error\n");

  exit(0);

  }

  2.system()

  功能:产生一个新的进程, 子进程执行指定的命令.

  语法:#include

  #include

  int system(string)

  char *string;

  说明:本调用将参数string传递给一个命令解释器(一般为sh)执行, 即

[NextPage]

  string被解释为一条命令, 由sh执行该命令.若参数string为一个空指针则为检查命令解释器是否存在.

     该命令可以同命令行命令相同形式, 但由于命令做为一个参数放在系统调用中, 应注意编译时对特殊意义字符的处理. 命令的查找是按PATH环境变量的定义的. 命令所生成的后果一般不会对父进程造成影响.

        返回值:当参数为空指针时, 只有当命令解释器有效时返回值为非零.若参数不为空指针, 返回值为该命令的返回状态(同waitpid())的返回值. 命令无效或语法错误则返回非零值,所执行的命令被终止. 其他情况则返回-1.

  例子:char command[81];

  int i;

  for (i=1;i<8;i++) {

  sprintf(command,"ps t tty%02i",i);

  system(command);

  }

  3.exec() 功能:执行一个文件

  语法:#include

  int execl(path,arg0,...,argn,(char*)0)

  char *path,*arg0,...,*argn;

  int execv(path,argv)

  char *path,*argv[];

  int execle(path,arg0,...,argn,(char*)0,envp)

  char *path,*arg0,...,*argn,*envp[];

  int execve(path,argv,envp)

  char *path,*argv[],*envp[];

  int execvp(file,argv)

  char *file,*argv[];

  说明:这是一个系统调用族, 用于将一个新的程序调入本进程所占的内

  存, 并覆盖之, 产生新的内存进程映象. 新的程序可以是可执行

  文件或SHELL批命令.

  当C程序被执行时,是如下调用的:

  main(int argc,char *argv[],char *envp[]);

  argc是参数个数,是各个参数字符串指针数组,envp是新进程的环境变量字符串的指针数组.argc至少为1,argv[0]为程序文件名,所以,在上面的exec系统调用族中,path为新进程文件的路径名,file为新进程文件名,若file不是全路径名,系统调用会按PATH环境变量自动找对应的可执行文件运行.若新进程文件不是一个可执行的目标文件(如批处理文件),则execlp()和execvp()会将该文件内容作为一个命令解释器的标准输入形成system().arg0,...等指针指向'\0'结束的字符串,组成新进程的有效参数,且该参数列表以一个空指针结束.反过来,arg0至少必须存在并指向新进程文件名或路径名.

  同样,argv是字符串指针数组,argv[0]指向新进程文件名或路径名,并以一空指针结束.envp是一个字符串指针数组,以空指针结束,这些字符串组成新进程的环境.在调用这些系统调用前打开的文件指针对新进程来说也是打开的,除非它已定义了close-on-exec标志.打开的文件指针在新进程中保持不变,所有相关的文件锁也被保留.调用进程设置并正被捕俘的信号在新进程中被恢复为缺省设置,其它的则保持不变.

  新进程启动时按文件的SUID和SGID设置定义文件的UID和GID为有效UID和GID.

[NextPage]

  新进程还继承了如下属性:

  . 附加GID.

  . 进程号.

  . 父进程号.

  . 进程组号.

  . 会话号.

  . 控制终端.

  . alarm时钟信号剩下的时间.

  . 当前工作目录.

  . 根目录.

  . 文件创建掩码.

  . 资源限制.

  . 用户时间,系统时间,子进程用户时间,子进程系统时间.

  . 记录锁.

  . 进程信号掩码.

  . 信号屏蔽.

  . 优先级.

  . 预定值.

  调用成功后,系统调用修改新进程文件的最新访问时间.

  返回值:该系统调用一般不会有成功返回值, 因为原来的进程已荡然无存.

  例子:printf("now this process will be ps command\n");

  execl("/bin/ps","ps","-ef",NULL);



  • 上一篇编程:

  • 下一篇编程: