Linux 进程

来源:互联网 发布:网络主播唱歌是真的吗 编辑:程序博客网 时间:2024/06/06 00:53

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\n",getpid());12     else if(pid == 0)13         printf("Child process pid: %d\n",getpid());14     else15         printf("fork error!\n");16     17     return 0;18 }
View Code

进程关闭exit

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

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

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

信号signal

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

//信号句柄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\n", getpid());14         15         printf("父进程挂起!\n");        16         pid = wait(&status);17         18         printf("父进程恢复\n");19         20     }else if(pid == 0){21         22         printf("Child process, pid: %d\n", getpid());23         printf("子进程退出\n");24         exit(status);25         26     }else{27         printf("fork error\n");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\n");21     alarm(3);22     23     ret = read(0, buffer, 1023);24     if(ret >= 0){25         buffer[strlen(buffer)-1]='\0';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]='\0';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("\n");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/


<script type="text/javascript"><!--google_ad_client = "ca-pub-1944176156128447";/* cnblogs 首页横幅 */google_ad_slot = "5419468456";google_ad_width = 728;google_ad_height = 90;//--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
原创粉丝点击