zoukankan      html  css  js  c++  java
  • Linux 进程

    在用户空间,进程是由进程标识符(PID)表示的。从用户的角度来看,一个 PID 是一个数字值,可惟一标识一个进程。一个 PID 在进程的整个生命期间不会更改,但 PID 可以在进程销毁后被重新使用,所以对它们进行缓存并不见得总是理想的。

    进程表示

      在 Linux 内核内,进程是由相当大的一个称为 task_struct 的结构表示的。此结构包含所有表示此进程所必需的数据,此外,还包含了大量的其他数据用来统计(accounting)和维护与其他进程的关系(父和子)。

    struct task_struct {
    
        volatile long state;
        void *stack;
        unsigned int flags;
    
        int prio, static_prio;
    
        struct list_head tasks;
    
        struct mm_struct *mm, *active_mm;
    
        pid_t pid;
        pid_t tgid;
    
        struct task_struct *real_parent;
    
        char comm[TASK_COMM_LEN];
    
        struct thread_struct thread;
    
        struct files_struct *files;
    
        ...
    
    };
    View Code

    进程管理

      在很多情况下,进程都是动态创建并由一个动态分配的 task_struct 表示。一个例外是init 进程本身,它总是存在并由一个静态分配的 task_struct 表示。

      在 Linux 内虽然进程都是动态分配的,但还是需要考虑最大进程数。在内核内最大进程数是由一个称为max_threads 的符号表示的,它可以在 ./linux/kernel/fork.c 内找到。可以通过 /proc/sys/kernel/threads-max 的 proc 文件系统从用户空间更改此值。

      Linux进程分为两种基本类型,分别为内核进程用户进程。内核进程通过内核中kernel_thread()函数创建的,用户进程通过fork()和clone()创建。

    进程的创建&内存的复制

      创建一个子进程,就创建了一个新的子任务,并为它复制了父进程的内存。但是这两个进程使用的内存是相互独立的。在调用fork函数的时候,父进程当时的所有变量对子进程都是可见的,fork函数执行完成之后,父进程的变量对于子进程来说就隐藏了。

      在创建一个新进程时,父进程使用的内存并不是真正的全部复制给子进程。它们都指向同一处内存空间,但是把内存页面标记为copy-on-write。当任何一个进程试图向这些内存中写入内容时,就会产生一组新的内存页面由这个进程私有。这样,通过这种方法提高了创建新进程的效率,因为内存空间的复制推迟到了发生写操作的时候。

    进程相关API

      Linux下进程模型提供了很多API函数,下面的这些函数可以完成基本的工作。

    API函数 用途
    fork 创建子进程
    wait 将进程挂起,直到子进程退出
    waitpid      将进程挂起,直到指定子进程退出
    signal 注册新的信号
    pause  将进程挂起,直到捕捉到信号
    kill 向某个指定的进程发出信号
    raise 向当前进程发出信号
    exec 将当前进程映像用一个新的进程映像来替换
    exit 正常终止当前进程

    函数详解&具体应用

      日常编程中,我们常用到多进程的方式,可以让我们的程序同步执行多个任务。接下来,从函数原型依次介绍并用实例来分析。

    进程的创建fork

      API函数fork用于在一个已经存在的父进程中创建一个新的进程,子进程除了ID与父进程不同,其余都相同。

    pid_t fork( void );
    //pid_t是进程描述符
    //返回值:>0, 当前进程就是父进程;==0,当前进程就是子进程;<0,失败 //返回值<0时,有两种错误,都是内存不足问题,分别为EAGAIN/ENOMEM
     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <sys/types.h>
     4 
     5 int main()
     6 {
     7     pid_t pid;
     8     
     9     pid = fork();
    10     if(pid > 0)
    11         printf("Parent process pid: %d
    ",getpid());
    12     else if(pid == 0)
    13         printf("Child process pid: %d
    ",getpid());
    14     else
    15         printf("fork error!
    ");
    16     
    17     return 0;
    18 }
    View Code

    进程关闭exit

      API函数exit终止调用进程,传入exit的参数会返回给父进程。

      子进程调用exit函数还会向父进程发出SIGCHLD信号,释放当前进程的资源。

    //关闭进程
    void exit( int status ); //status用来保存状态信息

    信号signal

      signal_handler函数允许用户为进程注册信号句柄。在信号被注册后,就可以在需要的时候调用信号API函数。

      PS: 在做一个WIN32控制台下的程序时候需要动态控制,我就利用SIGINT信号,在程序运行的过程中使用CTRL+C,调用中断函数完成操作。

    复制代码
    复制代码
    //信号句柄
    void signal_handler( int signal_number );
    
    //注册信号
    sighandler_t signal( int signum, sighandler_t handler );
    //signum为信号类型
    //handler为与信号相关联的动作 //sighandler_t的类型定义:typedef void (*sighandler_t)(int)  *****指向函数的指针******
    复制代码
    复制代码

      进程的信号句柄分为三种类型,分别为忽略类型、默认指定类型和用户自定义句柄类型。

      例子:

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <sys/types.h>
     4 #include <signal.h>
     5 
     6 void catch_signal_ctrl_c(int sig_num)
     7 {
     8     printf("catch signal ctrl+n");
     9     fflush(stdout);
    10 }
    11 
    12 int main()
    13 {
    14     signal(SIGINT, catch_signal_ctrl_c);       //注册信号SIGINT软件中断,通过输入ctrl+c可以发出
    15     
    16     printf("run a pause");
    17     pause();                          //进程挂起,等待信号
    18     
    19     return 0;
    20 }
    View Code

    进程挂起pause

      函数pause会把进程挂起,直到接收到信号。在信号接收后,进程会从pause函数中退出,继续运行。

    //挂起
    int pause( void );

    向当前进程发送信号raise

      raise函数只可以向当前进程发出信号。

    int raise( int sig_num );

      例子:

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <sys/types.h>
     4 #include <signal.h>
     5 
     6 void catch_signal_ctrl_c(int sig_num)
     7 {
     8     printf("catch signal ctrl+n");
     9     fflush(stdout);
    10 }
    11 
    12 int main()
    13 {
    14     signal(SIGINT, catch_signal_ctrl_c);       //注册信号SIGINT软件中断,通过输入ctrl+c可以发出
    15     
    16     raise(SIGINT);
    17     
    18     return 0;
    19 }
    View Code

    向其他进程发送信号kill

      kill函数可以向一个进程或一系列进程发送信号。

    int kill( pid_t pid, int sig_num );

      参数pid的不同会有不同的效果:

    pid 说明
    >0   发送信号到pid指定进程
    0 发送信号到与本进程同组的所有进程
    -1 发送信号到所有进程(init进程除外)
    <0 发送信号到由pid绝对值指定的进程组中的所有进程

    进程挂起wait&waitpid

      wait函数与waitpid函数都是将进程挂起,直到某个进程退出或信号发生,避免僵尸进程的产生。

      子进程退出,父进程没有等待(调用wait / waitpid)它, 那么她将变成一个僵尸进程。 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init 来接管它,成为它的父进程

      wait只根据任何一个子进程退出状态。

    pid_t wait( int *status );
    //返回退出子进程的pid值,如果为-1,发生错误
    //status用来保存子进程退出的状态信息

      通过评估函数可以知道子进程退出的状态信息。

    说明
    WIFEXITED 如果子进程正常退出,则不为0 
    WEXITSTATUS   返回子进程的exit状态
    WIFSIGNALED 如果子进程因为信号退出,则为ture
    WTERMSIG 返回引起子进程退出的信号号(仅在WIFSIGNALED为ture的时候)

      例子:

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <sys/types.h>
     4 #include <signal.h>
     5 
     6 int main()
     7 {
     8     pid_t pid;
     9     int status;
    10     
    11     pid = fork();
    12     if(pid > 0){
    13         printf("Parent process, pid: %d
    ", getpid());
    14         
    15         printf("父进程挂起!
    ");        
    16         pid = wait(&status);
    17         
    18         printf("父进程恢复
    ");
    19         
    20     }else if(pid == 0){
    21         
    22         printf("Child process, pid: %d
    ", getpid());
    23         printf("子进程退出
    ");
    24         exit(status);
    25         
    26     }else{
    27         printf("fork error
    ");
    28     }
    29     
    30     return 0;
    31 }
    View Code

      waitpid是挂起父进程直到某个指定的子进程退出。

    复制代码
    复制代码
    pid_t waitpid( pid_t pid, int *status, int options);
    //pid用来指定进程
    //status保存退出进程状态
    //options有两种选择,WNOHANG/WUNTRACED
    
    /*
    WNOHANG:设定在子进程未退出时也不挂起调用进程,但仅在子进程退出时返回
    WUNTRACED:返回已经停止而且自停止之后还未报告状态的子进程
    */
    复制代码
    复制代码

      pid的取值:

    pid 说明
    >0        挂起直到由pid指定的子进程退出
    0 挂起直到任何一个与调用进程的组ID相同的子进程退出 
    -1 挂起直到任何子进程退出,与wait相同
    <-1 挂起直到任何一个其组ID与pid参数的绝对值相同的子进程退出 

      waitpid增加两个宏:

        WIFSTOPPED: 如果子进程现在已经停止,返回true

        WSTOPSIG:  返回使子进程停止的信号(WIFSTOPPED为true) 

    发送警告信号alarm

      alarm函数在其他函数超时的时候会发送一个SIGALRM信号。

    unsigned it alarm( unsigned int secs );
    //secs为时间,单位为秒
    //如果在超时,未接收到警告信号,返回0,否则,返回等待警告信号的时间

      例子:

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <sys/types.h>
     4 #include <signal.h>
     5 #include <string.h>
     6 
     7 void wakeup(int sig_num)
     8 {
     9     print("timeout quit");
    10     raise(SIGINT);//产生中断信号
    11 }
    12 
    13 int main()
    14 {
    15     int ret;
    16     char buffer[1024];
    17     
    18     signal(SIGALRM, wakeup);
    19     
    20     printf("timeout in 3 seconds, please enter your password
    ");
    21     alarm(3);
    22     
    23     ret = read(0, buffer, 1023);
    24     if(ret >= 0){
    25         buffer[strlen(buffer)-1]='';
    26         printf("password : %s", buffer);
    27     }
    28     
    29     return 0;
    30 }
    View Code

    替换当前进程exec

      exec函数用于完全替换当前进程映像。实际上,就是用一个新的程序来替换当前的进程。值得注意的是,如果替换了,就不可能恢复,是完全的替换。

    复制代码
    复制代码
    int execl( const char *path, const char *arg, ... )
    //path确定要运行的程序
    //其他都为参数
    
    /*
    例子:
       execl( "/bin/ls", "ls", "-la", NULL  );
       //用ls命令程序来替换当前进程  
    */
    复制代码
    复制代码

      例子:

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <signal.h>
     6 #include <string.h>
     7 
     8 
     9 int main()
    10 {
    11     int status;
    12     char cmd[100];
    13     char* ret;
    14     pid_t pid;
    15     
    16     while(1){
    17         printf("mysh>>");
    18         ret = fgets(cmd, sizeof(cmd), stdin);
    19         if(ret == NULL)
    20             exit(-1);
    21         cmd[strlen(cmd)-1]='';
    22         
    23         if(!strncmp(cmd, "quit", 4))
    24             exit(0);
    25             
    26         pid = fork();
    27         if(pid == 0){
    28             execlp(cmd, cmd, NULL);
    29         }else if(pid >0){
    30             waitpid(pid, &status, 0);
    31         }
    32         printf("
    ");
    33     }
    34 }
    View Code

    参考

    GNU/LINUX环境编程

    http://www.cnblogs.com/avril/archive/2010/03/22/1691793.html

    http://www.ibm.com/developerworks/cn/linux/l-linux-process-management/

    知识共享许可协议作品cococo点点创作,采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可。欢迎转载,请注明出处: 转载自:cococo点点 http://www.cnblogs.com/coder2012
     
     
    分类: Linux
     
  • 相关阅读:
    HYSBZ 3813 奇数国
    HYSBZ 4419 发微博
    HYSBZ 1079 着色方案
    HYSBZ 3506 排序机械臂
    HYSBZ 3224 Tyvj 1728 普通平衡树
    Unity 3D,地形属性
    nginx 的naginx 种包含include关键字
    Redis 出现NOAUTH Authentication required解决方案
    mysql 8.0出现 Public Key Retrieval is not allowed
    修改jar包里的源码时候需要注意的问题
  • 原文地址:https://www.cnblogs.com/vhwuln/p/3564969.html
Copyright © 2011-2022 走看看