Linux 多进程编程

在开发大型项目的时候如果用单一进程执行程序效率是非常低的,我们可以同时创建多个进程来完成一个任务,这样程序的执行效率将大大提高,在Linux下对进程的操作主要以下接口:

 

    l 常见进程控制函数
        – fork  – 创建一个新进程
        – exec  – 用一个新进程去执行一个命令行
        – exit  – 正常退出进程
        – abort – 非正常退出一个进程
        – kill  – 杀死进程或向一个进程发送信号
        – wait  – 等待子进程结束
        – sleep – 将当前进程休眠一段时间
        – getpid – 取得进程编号
        – getppid – 取得父进程编号

 

   2、进程的的运行状态

        – 新建(new) – 正在创建
        – 运行(running) – 正在执行指令
        – 阻塞(blocked) – 等待像I/O这样的事件
        – 就绪(ready) – 等待分配处理器
        – 完成(done) – 结束



        

 

   

  ps查看进程状态
     PS显示状态
     – D 不可中断睡眠 (通常是在IO操作)
     – R 正在运行或可运行(在运行队列排队中)
     – S 可中断睡眠 (在等待某事件完成)
     – T Stopped, either by a job control signal or because it is being traced.
     – W 正在换页(2.6.内核之前有效)
     – X 死进程 (should never be seen)
     – Z 僵尸

 

  3. 进程调度
     -- CPU 一个时刻只能运行一个程序。在操作系统实现的多进程,看起来好象在同时运行多个程序。实际是是OS 的制造的假象


     -- 像一个人只有一双手,但是可以同时操作N 个桔子一样。这牵涉到一个调度的问题(schedule )。


     -- 在Linux 中,每个进程在创建时都会被分配一个数据结构,称为进程控制块(ProcessControl Block,简称PCB)。PCB中包含了很多重要的信息,供系统调度和进程本身执行使用.
  

     -- 在调度时,操作系统不断进行上下文切换(context switch),即将一个进程从运行状态退出,并运行另一个进程。

 

    4. 进程的创建
l 在Linux 中,创造新进程的方法只有一个:fork(), 创建子进程, 其它调用system,exec 最
后也是调用fork.
  

     – pid_t fork();

     – shell 执行一个命令相当于调用了 fork


   当一个进程调用了fork 以后, 系统会创建一个子进程. 这个子进程和父进程不同的地方只有他的进程ID 和父进程ID, 其他的都是一样. 就象符进程克隆(clone) 自己一样


– 参考代码.fork_test.c

 

   eg: 创建链式进程

 **************************************************************

 # include <unistd.h>

 # include <sys/type>

 int main(void)

 {  pid_t pid;

    int i;

    for(i = 0; i < 3; i++)

    {

      pid = fork();

      if(pid != 0)

       { break;}

    }

    sleep(5);

    return 0;

 }

**************************************************************

5. 进程的退出
    exit,_exit 或者main 函数里return 来设置进程退出值.
      为了得到这个值Linux定义了几个宏来测试这个返回值.
     WIFEXITED:判断子进程退出值是非0
     WEXITSTATUS:判断子进程的退出值(当子进程退出时非0).
     WIFSIGNALED:子进程由于有没有获得的信号而退出.
     WTERMSIG:子进程没有获得的信号号(在WIFSIGNALED 为真时才有意义).
     在shell用$?做同样事情.
     exit()函数与_exit()函数最大的区别就在于exit()函数在调用exit系统调用之前要检查文件
的打开情况,把文件缓冲区中的内容写回文件,就是清理I/O缓冲

 

6. 进程的阻塞


      一旦子进程被创建,父子进程一起从fork 处继续执行,相互竞争系统的资源.有时候我们希望子进程继续执行,而父进程阻塞直到子进程完成任务.这个时候我们可以调用wait 或者


waitpid 系统调用.


– pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid,int *stat_loc,int options);
      wait 系统调用会使父进程阻塞直到一个子进程结束或者是父进程接受到了一个信号.如
果没有父进程没有子进程或者他的子进程已经结束了wait 回立即返回.成功时(因一个子
进程结束)wait将返回子进程的ID,否则返回-1,并设置全局变量errno.stat_loc是子进程的
退出状态.子进程调用
     从本质上讲,系统调用waitpid 和wait的作用是完全相同的,但waitpid 多出了两个可由
用户控制的参数pid 和options,从而为我们编程提供了另一种更灵活的方式。下面我们
就来详细介绍一下这两个参数:
– static inline pid_t wait(int * wait_stat) { return waitpid(-1,wait_stat,0); }

 

 7. 进程间同步
    进程同步就是要协调好2 个以上的进程,使之以安排好地次序依次执行,比如进程间同步有多种方法,其中用wait是一种方法之一


     – wait 只能用于有亲戚关系的进程之间进行同步

    1.管道:速度慢,容量有限,只有父子进程能通讯    
    2.FIFO:任何进程间都能通讯,但速度慢    
    3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题    
    4.信号量:不能传递复杂消息,只能用来同步    
    5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。
 
  8、exec 相关函数
 

       int execl(const char * path,const char * arg,....);

 

       execl()用来执行参数path 字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的     argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。

     eg:

       #include<unistd.h>
       main()
      {
        execl(“/bin/ls”,”ls”,”-al”,”/etc/passwd”,(char * )0);
      }

 

        int execv (const char * path, char * const argv[ ]);

  

      execv()用来执行参数path 字符串所代表的文件路径,与execl()不同的地方在于execve()只需两个参数,第二个参数利用数组指针来传递给执行文件。

 

  9、  System 函数
       system 是直接用一个字符串执行一个命令
       – system(“ls –l”)
       – system(“ifconfig eth0 192.168.0.104”);
       – 定义:int system(const char * c m d s t r i n g) ;

 

        int system(const char * string);

       

      system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string 来执行参数string 字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT 和
SIGQUIT 信号则会被忽略。

 

作者: lin_13824307069   发布时间: 2010-10-31