Linux 进程
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; ... };
进程管理
在很多情况下,进程都是动态创建并由一个动态分配的 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
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h> int main()
{
pid_t pid; pid = fork();
if(pid > )
printf("Parent process pid: %d\n",getpid());
else if(pid == )
printf("Child process pid: %d\n",getpid());
else
printf("fork error!\n"); return ;
}
进程关闭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) *****指向函数的指针******
进程的信号句柄分为三种类型,分别为忽略类型、默认指定类型和用户自定义句柄类型。
例子:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h> void catch_signal_ctrl_c(int sig_num)
{
printf("catch signal ctrl+n");
fflush(stdout);
} int main()
{
signal(SIGINT, catch_signal_ctrl_c); //注册信号SIGINT软件中断,通过输入ctrl+c可以发出 printf("run a pause");
pause(); //进程挂起,等待信号 return ;
}
进程挂起pause
函数pause会把进程挂起,直到接收到信号。在信号接收后,进程会从pause函数中退出,继续运行。
//挂起
int pause( void );
向当前进程发送信号raise
raise函数只可以向当前进程发出信号。
int raise( int sig_num );
例子:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h> void catch_signal_ctrl_c(int sig_num)
{
printf("catch signal ctrl+n");
fflush(stdout);
} int main()
{
signal(SIGINT, catch_signal_ctrl_c); //注册信号SIGINT软件中断,通过输入ctrl+c可以发出 raise(SIGINT); return ;
}
向其他进程发送信号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的时候) |
例子:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h> int main()
{
pid_t pid;
int status; pid = fork();
if(pid > ){
printf("Parent process, pid: %d\n", getpid()); printf("父进程挂起!\n");
pid = wait(&status); printf("父进程恢复\n"); }else if(pid == ){ printf("Child process, pid: %d\n", getpid());
printf("子进程退出\n");
exit(status); }else{
printf("fork error\n");
} return ;
}
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,否则,返回等待警告信号的时间
例子:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h> void wakeup(int sig_num)
{
print("timeout quit");
raise(SIGINT);//产生中断信号
} int main()
{
int ret;
char buffer[]; signal(SIGALRM, wakeup); printf("timeout in 3 seconds, please enter your password\n");
alarm(); ret = read(, buffer, );
if(ret >= ){
buffer[strlen(buffer)-]='\0';
printf("password : %s", buffer);
} return ;
}
替换当前进程exec
exec函数用于完全替换当前进程映像。实际上,就是用一个新的程序来替换当前的进程。值得注意的是,如果替换了,就不可能恢复,是完全的替换。
int execl( const char *path, const char *arg, ... )
//path确定要运行的程序
//其他都为参数 /*
例子:
execl( "/bin/ls", "ls", "-la", NULL );
//用ls命令程序来替换当前进程
*/
例子:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h> int main()
{
int status;
char cmd[];
char* ret;
pid_t pid; while(){
printf("mysh>>");
ret = fgets(cmd, sizeof(cmd), stdin);
if(ret == NULL)
exit(-);
cmd[strlen(cmd)-]='\0'; if(!strncmp(cmd, "quit", ))
exit(); pid = fork();
if(pid == ){
execlp(cmd, cmd, NULL);
}else if(pid >){
waitpid(pid, &status, );
}
printf("\n");
}
}
参考
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 进程的更多相关文章
- Linux进程管理及while循环
目录 进程的相关概念 进程查看及管理工具的使用 Linux系统作业控制 调整进程优先级 网络客户端工具 bash之while循环 20.1.进程类型 守护进程 daemon,在系统引导过程中启动的进程 ...
- 如何灵活运用Linux 进程资源监控和进程限制
导读 每个 Linux 系统管理员都应该知道如何验证硬件.资源和主要进程的完整性和可用性.另外,基于每个用户设置资源限制也是其中一项必备技能. 在这篇文章中,我们会介绍一些能够确保系统硬件和软件正常工 ...
- TODO:Golang Linux进程退出说明
TODO:Golang Linux进程退出说明 Golang使用os.Exit(code)进程退出导致当前程序退出并返回给定的状态代码.传统上,code代码为零表示成功退出,非零错误退出. sysca ...
- Linux进程管理子系统分析【转】
本文转载自:http://blog.csdn.net/coding__madman/article/details/51298732 Linux进程管理: 进程与程序: 程序:存放在磁盘上的一系列代码 ...
- 12个Linux进程管理命令介绍(转)
12个Linux进程管理命令介绍 [日期:2015-06-02] 来源:Linux中国 作者:Linux [字体:大 中 小] 执行中的程序在称作进程.当程序以可执行文件存放在存储中,并且运行的 ...
- linux 进程管理相关内容
简介 当我们运行程序时,Linux会为程序创建一个特殊的环境,该环境包含程序运行需要的所有资源,以保证程序能够独立运行,不受其他程序的干扰.这个特殊的环境就称为进程. 每个 Linux 命令都与系统中 ...
- Linux - 进程查看与管理
标签(空格分隔): Linux 进程的静态查看 查看系统所有进程 ps -ef -- 输出来好乱,看不懂..: ps aux -- a表示所有与终端相关的进程,u表示所有以用户组织的进程状态的信息,x ...
- Linux进程关系
Linux进程关系 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Linux的进程相互之间有一定的关系.比如说,在Linux ...
- Linux进程基础
Linux进程基础 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 计算机实际上可以做的事情实质上非常简单,比如计算两个数的和 ...
- linux进程编程:子进程创建及执行函数简介
linux进程编程:子进程创建及执行函数简介 子进程创建及执行函数有三个: (1)fork();(2)exec();(3)system(); 下面分别做详细介绍.(1)fork() 函数定 ...
随机推荐
- Linux内核分析第五周学习总结:扒开系统调用的三层皮(下)
韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.给MenuO ...
- java 封装httpclient 的get 和post 请求
import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util. ...
- jquery动态创建页面元素
jquery用$()方法动态创建一个页面元素,例如: var $div=$("<div title='动态创建页面元素'>欢迎创建一个新的div</div>" ...
- 写一个ajax程序就是如此简单
写一个ajax程序就是如此简单 ajax介绍: 1:AJAX全称为Asynchronous JavaScript and XML(异步JavaScript和XML),指一种创建交互式网页应用的网页开发 ...
- Exception异常规范
把异常的栈轨迹以String形式返回 /** * 把异常的栈轨迹以String形式返回,而不是直接打印到console * @author King * @time 2015-04-29 * @ret ...
- pip使用
安装指定版本的插件: pip install -v matplotlib==1.5.1 #安装matplotlib version 1.5.1
- oracle创建用户、授予权限及删除用户
创建用户 oracle对表空间 USERS 无权限 alter user 用户名 quota unlimited on users; //创建临时表空间 create temporary ta ...
- 【转】HTTP状态码(HTTP Status Code)
原文链接:http://www.chaoji.com/features/httpstatus.aspx 一些常见的状态码为: 200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - ...
- 恢复SQLSERVER被误删除的数据(转——收藏)
恢复SQLSERVER被误删除的数据 摘自:http://www.cnblogs.com/lyhabc/p/3683147.html 曾经想实现Log Explorer for SQL Server的 ...
- SpringMVC与ajax相关知识练习与存档
参考文章(亲测有效): SpringMVC+ajax返回JSON串 jquery ajax例子返回值详解 AJAX $.ajax({url:路径,date:数据,}) //get请求(指定回调函数,参 ...