- 浏览: 1658347 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (405)
- C/C++ (16)
- Linux (60)
- Algorithm (41)
- ACM (8)
- Ruby (39)
- Ruby on Rails (6)
- FP (2)
- Java SE (39)
- Java EE (6)
- Spring (11)
- Hibernate (1)
- Struts (1)
- Ajax (5)
- php (2)
- Data/Web Mining (20)
- Search Engine (19)
- NLP (2)
- Machine Learning (23)
- R (0)
- Database (10)
- Data Structure (6)
- Design Pattern (16)
- Hadoop (2)
- Browser (0)
- Firefox plugin/XPCOM (8)
- Eclise development (5)
- Architecture (1)
- Server (1)
- Cache (6)
- Code Generation (3)
- Open Source Tool (5)
- Develope Tools (5)
- 读书笔记 (7)
- 备忘 (4)
- 情感 (4)
- Others (20)
- python (0)
最新评论
-
532870393:
请问下,这本书是基于Hadoop1还是Hadoop2?
Hadoop in Action简单笔记(一) -
dongbiying:
不懂呀。。
十大常用数据结构 -
bing_it:
...
使用Spring MVC HandlerExceptionResolver处理异常 -
一别梦心:
按照上面的执行,文件确实是更新了,但是还是找不到kernel, ...
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法 -
dsjt:
楼主spring 什么版本,我的3.1 ,xml中配置 < ...
使用Spring MVC HandlerExceptionResolver处理异常
进程管理(一)
和文件一样,进程是Unix系统最基本的抽象之一。
1、进程ID:
每一个进程都有一个唯一的标示,进程ID。虽然进程ID是唯一的,但进程终止后,id会被其他进程重用。
许多UNIX都提供了延迟重用的功能,以防止新进程被误认为是旧进程。
有一些特殊的进程:
id为0的进程--idle进程或者叫做swapper,通常是一个调度进程。
id为1的进程--内核booting之后执行的第一个进程。init进程一般执行的是init程序。
Linux通常尝试执行以下init程序:
1、/sbin/init: 偏向、最有可能是init程序的地方。
2、/etc/init: 另一个很有可能是init程序的地方。
3、/bin/init: 有可能是init进程的地方。
4、/bin/sh:如果内核找不到init进程,就执行该bourne shell。
init进程是一个用户级进程,但是需要执行者有超级用户权限。
2、获得进程ID和父进程的ID:
pid_t是个抽象类型,在linux中pid_t一般是一个int类型,在<sys/types.h>定义。
但把pid_t当做int类型,不具有可移植性。
例子:
我们可以把pid_t比较安全的当做int类型,虽然这违反了抽象类型的意图和可移植性。
2、创建一个进程fork:
一个已经存在的进程可以通过fork创建其他进程:
新创建的进程被称为子进程,这个函数被调用一次但是被返回两次。在子进程返回0,父进程返回子进程的t_pid。
之所以在父进程返回子进程的id,是由于父进程可以有多个子进程,并且没有提供获得所有子进程的方法。在子进程
中返回0,是因为子进程只有一个父进程,并且可以通过getppid获得。
fork被调用之后,父进程和子进程都开始执行fork之后的程序语句。子进程是父进程的一个拷贝,拷贝了父进程的数据
空间,堆,栈,它们共享text段。
当前fork的实现并不是拷贝父进程的数据、堆、栈,而是使用了copy-on-write技术,这是因为fork之后通常会调用exec。
如果它们修改了这些区域,那么内核就会把相应的那部分内存进行拷贝。
子进程和父进程有以下不同:
1)进程id不同
2) fork的返回值不同
2)子进程的父进程id设置为父进程的id,它们的父进程不同
3)子进程的资源统计归为0
4)任何pending的signals被清空,不会被子进程继承
5)任何获得的文件锁都不会被子进程继承。
相同的:
1)打开文件
2)real user ID,real group ID,effective user ID,effective group ID
3)进程的group ID
4)Session ID
5)控制终端
6)set-user-ID和set-group-ID
7)当前工作目录
8)Root目录
9)文件mode创建的掩码
10)信号掩码和dipositions
11)打开文件的close-on-exec flag
12)环境变量
13)附加进去的共享内存段
14)内存映像
15)资源限制
如果失败返回-1。
例子:
fork经常和exec在一起使用:
使用fork的场景:
1)当一个进程想复制自己以便父子进程可以同时执行不同部分的代码。比如一个网络的服务器,
父进程等待从客户端发来的请求,当请求到来时,父进程调用fork,让新创建的子进程处理请求,
父进程继续等待客户端发来的请求。
2)当一个进程想执行不同的程序。比如shell,子进程在fork返回之后执行了exec。
3、vfork:
在copy-on-write技术使用之前,Unix的设计者认为fork之后执行exec浪费了地址空间的拷贝,BSD开发者
实现了vfork系统调用:
vfork和fork行为一样,除了子进程要立即调用exec函数或者执行exit退出。vfork系统调用避免了地址
空间和页表的拷贝,通过挂起父进程直到子进程终止或者执行一个二进制的进程映像。
vfork的例子:
输出:pid=2903,glob=7,var=89.在子进程里面增加变量会反映到父进程中,因为他们共享同一进程空间。
4、终止进程:
POSIX和C89都定义了终止当前进程的标准函数:
调用exit会执行一些关闭操作步骤,然后指示内核终止进程。
status表示进程终止的状态。EXIT_SUCESS和EXIT_FAILURE被定义为一种可移植的方式来表示成功和失败。
在终止之前要做一些关闭的步骤:
1)调用任何注册在atexit()和on_exit()的方法,和注册的顺序相反。
2)flush所有打开的I/O流
3)删除进程由tmpfile()函数创建的临时文件。
执行完这些步骤之后,调用_exit(),让内核来处理剩余的终止操作:
当进程终止后,内核清空了进程申请的所有资源。
程序可以直接调用_exit,但是很多程序需要执行一些清理操作,比如flush标准输出流。但是vfork用户必须
使用_exit终止,因为父子进程共享一个地址空间,exit执行一些I/O清理工作可能把父进程的文件描述流关闭,
导致父进程I/O失败。
5、atexit和on_exit:
1、atexit:
注册在进程终止之前回到的函数:
如果进程通过exit或者从main返回终止,则会调用注册到atexit的方法。如果进程调用exec函数,注册函数则
被清空(因为这些函数不在新的进程空间存在)。如果信号终止了进程,则注册的函数不会被调用。
被注册的函数按照逆序执行,如果被注册的函数执行了exit,则会导致无穷递归,如果想提前终止需要使用
_exit。atexit支持至少ATEXT_MAX个注册函数,这个值可以通过sysconf得到。
atexit例子:
2、on_exit:
on_exit和atexit等价,Linux glibc实现了它:
但是注册的签名函数不同,原型是:
status是传到exit的或者从main返回的值。args是传到on_exit的第二个参数。Solaris已经不再支持on_exit,
所以最好使用atexit().
6、等待子进程终止:
当一个进程终止之后,内核向父进程发送一个SIGCHLD。默认这个信号被忽略,进程可以通过singnal()或者
sigaction()系统调用来处理这个信号。父进程希望得到子进程终止的更多信息,比如返回值,甚至显示的等
待这个事件的到来,这就是wait或者waitpid,它们可以做:
1)阻塞,如果子进程仍然在执行。
2)立即返回,包含子进程的终止状态,如果一个子进程终止,等待它的终止状态被获取。
3)返回错误,如果它没有子进程。
这两个函数的不同之处:
1)wait会阻塞调用者,直到一个子进程终止,而waitpid有一个设置不阻塞的选项。
2)waitpid不是等待第一个终止的子进程,他有一些选项来控制进程的等待。
如果一个进程终止,其父进程没有等待他终止,它就成为僵尸进程,僵尸进程的父进程会置为init,
进程,init周期性的调用wait来回收僵尸。
子进程的结束状态会保存在statloc指针中,如果不关心结束状态,可以直接传一个NULL。
POSIX指定了通过一系列宏来获得终止的状态。
WIFEXITED:如果子进程正常终止,返回true。可以通过WEXITSTATUS得到参数的低8位。
WIFSIGNALED:如果是信号导致子进程不正常终止,返回true。可以通过WTERMSIG返回信号的号。
一些UNIX实现定义了WCOREDUMP宏,如果进程dump core来响应信号。
WIFSTOPPED:如果进程被停止,返回true,通过WSTOPSIG来获得导致子进程停止的信号。
WIFCONTINUED:如果状态是由已经被continued子进程返回,返回true。
例子:
waitpid比wait功能更加强大。
pid参数指定了要等待的进程id:
< -1:等待任意一个绝对值和进程group id相等的进程。
-1:等待任意一个子进程,和wait一样
0:等待和当前进程相同组的任一子进程。
> 0: 等待该进程id的子进程
option 可以通过OR连接下列选项:
WNOHANG:不阻塞,如果没有匹配的子进程也直接返回。
WUNTRACED:如果被设置,WIFSTOPPED也会被设置。允许更一般的作业控制。
WCONTINUED:如果被设置,WIFCONTINUED也被设置。和WUNTRACED一起,
对于实现一个shell很有用。
例子:
7、exec函数:
当进程调用exec时,当前进程的镜像被由path标定的程序加载到内存中代替。下面是exec一族函数:
区别:1)前四个参数path代表路径名,后两个代表文件名(不包含路径信息)。
如果文件名中包含反斜线,作为一个路径名
如果否则从PATH环境变量中找可执行的文件。
如果execlp或者execvp找到了可以执行的文件,但是不是机器可执行的,那么就假设这是一个shell脚本,
调用/bin/sh执行该shell脚本。
2)execl、execle、execlp使用的是参数列表,需要以NULL终止,而其他的几个带v的是一个数组参数,
参数数组也要以NULL终止。
3)execle、execve传递环境列表到新的程序中。
这里面只有execve是系统调用,其他都是函数。
例子:
下面一个例子,要使用vi打开以及文件编辑:
成功调用execl之后,改变的不仅是地址空间和进程映像,而且还改变进程一下属性:
1)任何pending signals都被丢弃。
2)任何要捕获的信号都置成默认的行为。因为信号处理函数不在该进程的地址空间了。
3)任何的内存锁都被释放。
4)很多线程属性被设置为默认值。
5)任何和进程内存相关,包括内存映射文件,都被丢弃。
6)任何在用户空间存在的包括C语言库,比如atexit的行为被丢弃。
没有改变的进程属性:
进程pid,父进程id,优先级,进程所属的用户和组。
execvp例子:
失败返回-1,并设置errno:
1)E2BIG:参数列表或者环境变量envp太长。
2)EACCESS:进程没有搜索path的权限,path不是一个普通文件,目标文件不可执行,文件系统被mounted为不可读。
3)EFAULT:所给的指针不合法。
4)EIO:底层的I/O发生错误。
5)EISDIR:path或者解释器是一个目录。
6)ELOOP:解析path的时候遇到太多的软链。
7)EMFILE:调用进程超过了打开文件的限制。
8)ENFILE:系统打开文件数目超过限制。
9)ENOENT:path不存在,或者以来的共享库不存在。
10)ENOEXEC:目标path是一个不合法的二进制文件或者是不同的机器架构。
11)ENOMEM:没有足够的内核内存来执行新的程序
12)ENOTDIR:path不是一个目录.
13)ETXTBSY:目标文件正被其他的进程写
8、Launching and Waiting for a New Process:
ANSI C和POSIX都定义了一个接口,他结合了创建一个进程并且等待它的结束:
system一般用于执行一个简单的工具或者shell脚本。
成功返回命令的执行状态,如果command是NULL,则返回非0整数。
在执行command命令式,SIGCHILD被阻塞,SIGINT和SIGQUIT被忽略。忽略SIGINT和SIGQUIT有几个含义,
尤其system在一个循环里被执行,这时你需要保证程序检查子进程的状态。
使用fork、waitpid简单实现system:
参考:
1、《Linux system programming》
2、《Unix system programming》
3、《Advanced Programming in the Unix Environment》
和文件一样,进程是Unix系统最基本的抽象之一。
1、进程ID:
每一个进程都有一个唯一的标示,进程ID。虽然进程ID是唯一的,但进程终止后,id会被其他进程重用。
许多UNIX都提供了延迟重用的功能,以防止新进程被误认为是旧进程。
有一些特殊的进程:
id为0的进程--idle进程或者叫做swapper,通常是一个调度进程。
id为1的进程--内核booting之后执行的第一个进程。init进程一般执行的是init程序。
Linux通常尝试执行以下init程序:
1、/sbin/init: 偏向、最有可能是init程序的地方。
2、/etc/init: 另一个很有可能是init程序的地方。
3、/bin/init: 有可能是init进程的地方。
4、/bin/sh:如果内核找不到init进程,就执行该bourne shell。
init进程是一个用户级进程,但是需要执行者有超级用户权限。
2、获得进程ID和父进程的ID:
#include <sys/types.h> #include <unistd.h> pid_t getpid(void); pit_t getppid(void);
pid_t是个抽象类型,在linux中pid_t一般是一个int类型,在<sys/types.h>定义。
但把pid_t当做int类型,不具有可移植性。
例子:
printf("My pid=%d\n",getpid()); printf("Parent's pid=%d\n",getppid());
我们可以把pid_t比较安全的当做int类型,虽然这违反了抽象类型的意图和可移植性。
2、创建一个进程fork:
一个已经存在的进程可以通过fork创建其他进程:
#include <unistd.h> pid_t fork(void);
新创建的进程被称为子进程,这个函数被调用一次但是被返回两次。在子进程返回0,父进程返回子进程的t_pid。
之所以在父进程返回子进程的id,是由于父进程可以有多个子进程,并且没有提供获得所有子进程的方法。在子进程
中返回0,是因为子进程只有一个父进程,并且可以通过getppid获得。
fork被调用之后,父进程和子进程都开始执行fork之后的程序语句。子进程是父进程的一个拷贝,拷贝了父进程的数据
空间,堆,栈,它们共享text段。
当前fork的实现并不是拷贝父进程的数据、堆、栈,而是使用了copy-on-write技术,这是因为fork之后通常会调用exec。
如果它们修改了这些区域,那么内核就会把相应的那部分内存进行拷贝。
子进程和父进程有以下不同:
1)进程id不同
2) fork的返回值不同
2)子进程的父进程id设置为父进程的id,它们的父进程不同
3)子进程的资源统计归为0
4)任何pending的signals被清空,不会被子进程继承
5)任何获得的文件锁都不会被子进程继承。
相同的:
1)打开文件
2)real user ID,real group ID,effective user ID,effective group ID
3)进程的group ID
4)Session ID
5)控制终端
6)set-user-ID和set-group-ID
7)当前工作目录
8)Root目录
9)文件mode创建的掩码
10)信号掩码和dipositions
11)打开文件的close-on-exec flag
12)环境变量
13)附加进去的共享内存段
14)内存映像
15)资源限制
如果失败返回-1。
例子:
pid_t pid; pid = fork(); if(pid > 0){ printf("I am the parent of pid=%d\n",pid); }else if(!pid){ printf("I am the baby!\n"); }else if(pid == -1){ perror("fork"); }
fork经常和exec在一起使用:
pid_t pid; pid = fork(); if(pid == -1) perror("fork"); if(!pid){ const char *args[] = {"windlass",NULL}; int ret; ret = execv("/bin/windlass",args); if(ret == -1){ perror("execv"); exit(EXIT_FAILURE); } }
使用fork的场景:
1)当一个进程想复制自己以便父子进程可以同时执行不同部分的代码。比如一个网络的服务器,
父进程等待从客户端发来的请求,当请求到来时,父进程调用fork,让新创建的子进程处理请求,
父进程继续等待客户端发来的请求。
2)当一个进程想执行不同的程序。比如shell,子进程在fork返回之后执行了exec。
3、vfork:
在copy-on-write技术使用之前,Unix的设计者认为fork之后执行exec浪费了地址空间的拷贝,BSD开发者
实现了vfork系统调用:
#include <sys/types.h> #include <unistd.h> pid_t vfork(void);
vfork和fork行为一样,除了子进程要立即调用exec函数或者执行exit退出。vfork系统调用避免了地址
空间和页表的拷贝,通过挂起父进程直到子进程终止或者执行一个二进制的进程映像。
vfork的例子:
#include <sys/types.h> #include <unistd.h> int glob = 6; int main(){ int var; pid_t pid; var = 88; printf("before vfork\n"); if((pid = vfork()) < 0){ perror("vfork"); return 1; }else if(pid == 0){ glob++; var++; _exit(0); } printf("pid=%d",glob=%d,var=%d\n",getpid(),glob,var); exit(0); }
输出:pid=2903,glob=7,var=89.在子进程里面增加变量会反映到父进程中,因为他们共享同一进程空间。
4、终止进程:
POSIX和C89都定义了终止当前进程的标准函数:
#include <stdlib.h> void exit(int status);
调用exit会执行一些关闭操作步骤,然后指示内核终止进程。
status表示进程终止的状态。EXIT_SUCESS和EXIT_FAILURE被定义为一种可移植的方式来表示成功和失败。
在终止之前要做一些关闭的步骤:
1)调用任何注册在atexit()和on_exit()的方法,和注册的顺序相反。
2)flush所有打开的I/O流
3)删除进程由tmpfile()函数创建的临时文件。
执行完这些步骤之后,调用_exit(),让内核来处理剩余的终止操作:
#include <unistd.h> void _exit(int status);
当进程终止后,内核清空了进程申请的所有资源。
程序可以直接调用_exit,但是很多程序需要执行一些清理操作,比如flush标准输出流。但是vfork用户必须
使用_exit终止,因为父子进程共享一个地址空间,exit执行一些I/O清理工作可能把父进程的文件描述流关闭,
导致父进程I/O失败。
5、atexit和on_exit:
1、atexit:
注册在进程终止之前回到的函数:
#include <stdlib.h> int atexit(void (*function)(void));
如果进程通过exit或者从main返回终止,则会调用注册到atexit的方法。如果进程调用exec函数,注册函数则
被清空(因为这些函数不在新的进程空间存在)。如果信号终止了进程,则注册的函数不会被调用。
被注册的函数按照逆序执行,如果被注册的函数执行了exit,则会导致无穷递归,如果想提前终止需要使用
_exit。atexit支持至少ATEXT_MAX个注册函数,这个值可以通过sysconf得到。
long atexit_max; atexit_max = sysconf(_SC_ATEXIT_MAX); printf("atexit_max=%ld\n", atexit_max);
atexit例子:
#include <stdio.h> #include <stdlib.h> void out(void){ println("atexit() succeed!\n"); } int main(){ if(atexit(out)) fprintf(stderr,"atexit() failed!\n"); return 0; }
2、on_exit:
on_exit和atexit等价,Linux glibc实现了它:
#include <stdlib.h> int on_exit (void (*function)(int , void *), void *arg);
但是注册的签名函数不同,原型是:
void my_func(int status,void *args);
status是传到exit的或者从main返回的值。args是传到on_exit的第二个参数。Solaris已经不再支持on_exit,
所以最好使用atexit().
6、等待子进程终止:
当一个进程终止之后,内核向父进程发送一个SIGCHLD。默认这个信号被忽略,进程可以通过singnal()或者
sigaction()系统调用来处理这个信号。父进程希望得到子进程终止的更多信息,比如返回值,甚至显示的等
待这个事件的到来,这就是wait或者waitpid,它们可以做:
1)阻塞,如果子进程仍然在执行。
2)立即返回,包含子进程的终止状态,如果一个子进程终止,等待它的终止状态被获取。
3)返回错误,如果它没有子进程。
#include <sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options);
这两个函数的不同之处:
1)wait会阻塞调用者,直到一个子进程终止,而waitpid有一个设置不阻塞的选项。
2)waitpid不是等待第一个终止的子进程,他有一些选项来控制进程的等待。
如果一个进程终止,其父进程没有等待他终止,它就成为僵尸进程,僵尸进程的父进程会置为init,
进程,init周期性的调用wait来回收僵尸。
子进程的结束状态会保存在statloc指针中,如果不关心结束状态,可以直接传一个NULL。
POSIX指定了通过一系列宏来获得终止的状态。
#include <sys/wait.h> int WIFEXITED(status); int WIFSIGNALED(status); int WIFSTOPPED(status); int WIFCONTINUED(status); int WEXITSTATUS(status); int WTERMSIG(status); int WSTOPSIG(status); int WCOREDUMP(status);
WIFEXITED:如果子进程正常终止,返回true。可以通过WEXITSTATUS得到参数的低8位。
WIFSIGNALED:如果是信号导致子进程不正常终止,返回true。可以通过WTERMSIG返回信号的号。
一些UNIX实现定义了WCOREDUMP宏,如果进程dump core来响应信号。
WIFSTOPPED:如果进程被停止,返回true,通过WSTOPSIG来获得导致子进程停止的信号。
WIFCONTINUED:如果状态是由已经被continued子进程返回,返回true。
例子:
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> int main(void){ int status; pid_t pid; if(! fork()){ return 1; } pid = wait(&status); if(pid == -1) perror("wait"); printf("pid=%d\n",pid); if(WIFEXITED(status)) printf("Normal termination with exit status=%d\n",WEXITSTATUS(status)); if(WIFSIGNALED(status)) printf("Killed by signal=%d%s\n",WTERMSIG(status),WCOREDUMP(status)); if(WIFSTOPPED(status)) printf("Stopped by signal=%d\n",WSTOPSIG(status)); if(WIFCONTINUED(status)) printf("Continued\n"); return 0; }
waitpid比wait功能更加强大。
pid参数指定了要等待的进程id:
< -1:等待任意一个绝对值和进程group id相等的进程。
-1:等待任意一个子进程,和wait一样
0:等待和当前进程相同组的任一子进程。
> 0: 等待该进程id的子进程
option 可以通过OR连接下列选项:
WNOHANG:不阻塞,如果没有匹配的子进程也直接返回。
WUNTRACED:如果被设置,WIFSTOPPED也会被设置。允许更一般的作业控制。
WCONTINUED:如果被设置,WIFCONTINUED也被设置。和WUNTRACED一起,
对于实现一个shell很有用。
例子:
int status; pid_t pid; pid = waitpid (1742, &status, WNOHANG); if (pid == -1) perror ("waitpid"); else { printf ("pid=%d\n", pid); if (WIFEXITED (status)) printf ("Normal termination with exit status=%d\n",WEXITSTATUS (status)); if (WIFSIGNALED (status)) printf ("Killed by signal=%d%s\n",WTERMSIG (status),WCOREDUMP (status) ? " (dumped core)" : ""); }
7、exec函数:
当进程调用exec时,当前进程的镜像被由path标定的程序加载到内存中代替。下面是exec一族函数:
#include <unistd.h> int execl(const char *path,const char *arg,...); int execv(const char *path,char * const argv[]); int execle(const char *path,const char *arg,...); int execve(const char *path,char *const argv[], char * const envp[]); int execlp(const char *path,const char *arg,...); int execvp(const char *path, char *const argv);
区别:1)前四个参数path代表路径名,后两个代表文件名(不包含路径信息)。
如果文件名中包含反斜线,作为一个路径名
如果否则从PATH环境变量中找可执行的文件。
如果execlp或者execvp找到了可以执行的文件,但是不是机器可执行的,那么就假设这是一个shell脚本,
调用/bin/sh执行该shell脚本。
2)execl、execle、execlp使用的是参数列表,需要以NULL终止,而其他的几个带v的是一个数组参数,
参数数组也要以NULL终止。
3)execle、execve传递环境列表到新的程序中。
这里面只有execve是系统调用,其他都是函数。
例子:
int ret; ret = execl("/bin/vi","vi",NULL); if(ret == -1) perror("execl");
下面一个例子,要使用vi打开以及文件编辑:
int ret; ret = execl("/bin/vi","vi","/home/fuliang/books.txt",NULL); if(ret == -1) perror("execl");
成功调用execl之后,改变的不仅是地址空间和进程映像,而且还改变进程一下属性:
1)任何pending signals都被丢弃。
2)任何要捕获的信号都置成默认的行为。因为信号处理函数不在该进程的地址空间了。
3)任何的内存锁都被释放。
4)很多线程属性被设置为默认值。
5)任何和进程内存相关,包括内存映射文件,都被丢弃。
6)任何在用户空间存在的包括C语言库,比如atexit的行为被丢弃。
没有改变的进程属性:
进程pid,父进程id,优先级,进程所属的用户和组。
execvp例子:
const char *args[] = {"vi","/home/fuliang/books.txt",NULL}; int ret; ret = execvp("vi",args); if( ret == -1 ) perror("execvp");
失败返回-1,并设置errno:
1)E2BIG:参数列表或者环境变量envp太长。
2)EACCESS:进程没有搜索path的权限,path不是一个普通文件,目标文件不可执行,文件系统被mounted为不可读。
3)EFAULT:所给的指针不合法。
4)EIO:底层的I/O发生错误。
5)EISDIR:path或者解释器是一个目录。
6)ELOOP:解析path的时候遇到太多的软链。
7)EMFILE:调用进程超过了打开文件的限制。
8)ENFILE:系统打开文件数目超过限制。
9)ENOENT:path不存在,或者以来的共享库不存在。
10)ENOEXEC:目标path是一个不合法的二进制文件或者是不同的机器架构。
11)ENOMEM:没有足够的内核内存来执行新的程序
12)ENOTDIR:path不是一个目录.
13)ETXTBSY:目标文件正被其他的进程写
8、Launching and Waiting for a New Process:
ANSI C和POSIX都定义了一个接口,他结合了创建一个进程并且等待它的结束:
#include <stdlib.h> int system(const char *command);
system一般用于执行一个简单的工具或者shell脚本。
成功返回命令的执行状态,如果command是NULL,则返回非0整数。
在执行command命令式,SIGCHILD被阻塞,SIGINT和SIGQUIT被忽略。忽略SIGINT和SIGQUIT有几个含义,
尤其system在一个循环里被执行,这时你需要保证程序检查子进程的状态。
do{ int ret; ret = system("ls -l"); if(WIFSIGNALED(ret) && WTFERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT)) break; }while(1);
使用fork、waitpid简单实现system:
int my_system(const char *cmd){ int status; pid_pid; pid = fork(); if(pid == -1); return -1; else if(pid == 0){ const char *argv[4]; argv[0] = "sh"; argv[1] = "-c"; argv[2] = cmd; argv[3] = NULL; execv("/bin/sh",argv); exit(-1); } if( waitpid(pid, &status,0) == -1) return -1; else if(WIFEXITED(status)) return WEXITSTATUS(status); return -1; }
参考:
1、《Linux system programming》
2、《Unix system programming》
3、《Advanced Programming in the Unix Environment》
发表评论
-
【zz】Vim的分屏
2012-04-26 11:13 1603分屏启动Vim 使用大写 ... -
bash的几种for循环
2012-04-26 11:13 18581、罗列式 for VARIABLE in 1 2 3 ... -
【zz】几个简单选项让你的Readline(Bash)更好用
2011-10-10 23:08 2429Reddit上的一位仁兄贴出了他的readline手册学习成果 ... -
pipe in -exec
2011-09-27 22:35 1433一个简单的例子: 将一堆的.txt文件,合并成一个文件,让每个 ... -
[zz]Unix调试的瑞士军刀:lsof
2011-08-31 10:33 1507原文见:http://heikezhi.com/2011/06 ... -
[zz]关于xargs,你可能不知道的
2011-08-29 23:05 2483如果你曾经花了许多时间在Unix命令行上,那么或许你已经见过x ... -
使用scala.sys.process包和系统交互
2011-07-19 00:00 6501在Java中我们可以使用Runtime.getRuntime( ... -
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法
2011-06-23 22:17 14482在笔记本安装Ubuntu11.04增强功能失败 引用 fuli ... -
awk getline
2011-06-02 23:58 5848awk getline开始一直用做按行读取文件。 getlin ... -
[zz]服务器性能评估
2011-04-29 14:17 4032工作这么久了,主 ... -
[zz]Top命令VIRT,RES,SHR,DATA的含义
2011-04-10 15:50 4133VIRT:virtual memory usage ... -
[zz]一些shell命令
2011-04-10 15:35 15191.显示消耗内存/CPU最多的10个进程 ps aux | ... -
Open VPN免密码配置
2011-03-03 22:55 3589公司VPN的帐号密码使用的是个超长的随机数,每次输入很麻烦,如 ... -
Linux下使用RTX腾讯通
2011-03-03 22:46 10795在公司为了使用RTX,专门安装了一个XP的虚拟机,但是这个也不 ... -
[zz]linux 常用命令总结:
2010-12-11 21:30 1700linux 常用命令总结: 一。 通用命令: 1. dat ... -
Ubuntu10.10解决Empathy无法链接MSN的问题
2010-10-21 16:36 37581.在Empathy中删除无法登录的MSN账户,并关闭Empa ... -
[zz]Vim的分屏功能
2010-10-21 13:09 1708分屏启动Vim 使用大写 ... -
Ubuntu10.10 64bit使用Eclipse插件subclipse问题
2010-10-20 20:32 1974升级到ubuntu10.10什么坑都碰到了,郁闷啊。发现sub ... -
scim输入法,Eclipse代码提示之后失去焦点的问题
2010-10-20 13:36 3095突然发现Eclipse,每次代码提示之后无法打字,感觉是文本域 ... -
Linux下使用谷歌输入法
2010-10-18 23:01 3149Linux的中文输入法一直太烂,scim终于出来对google ...
相关推荐
### Linux系统编程学习笔记 #### 一、IO **1.1 标准I/O (stdio)** - **fopen/fclose**: `fopen` 用于打开或创建一个文件,并返回一个指向该文件的 `FILE *` 类型的指针。`fclose` 用于关闭一个已经打开的文件。...
Linux系统编程是一门专注于Linux操作系统上应用程序和工具开发的学科。它包含了广泛的领域,如文件操作、进程管理、内存管理以及网络编程等。开发者在这一领域中,从基础的文件系统交互到复杂的网络通信和多线程编程...
Linux系统编程笔记涉及到的内容广泛,涵盖了从基础的出错处理到进程管理,从内存管理到进程间通信,以及守护进程设计等多个层面的知识。下面详细说明各个部分的知识点: 1. 常见出错处理 - abort函数用于异常终止...
【Linux 系统编程与内核驱动开发笔记】 在深入探讨Linux系统编程和内核驱动开发之前,我们首先要理解Linux操作系统的基本概念。Linux是一种自由、开放源码的类Unix操作系统,广泛应用于服务器、桌面环境以及各种...
根据提供的文件信息,我们可以推断出这是一份关于Linux编程学习笔记的PDF资料。下面将对这份资料可能涉及的关键知识点进行详细的阐述。 ### Linux编程基础知识 #### 1. Linux操作系统概述 - **定义与特点**:Linux...
1. **进程管理**:在Linux和UNIX系统中,进程是程序执行的实例。手册会介绍如何创建、控制、终止进程,包括fork()、exec()家族函数的使用,以及wait()和waitpid()函数来监控子进程的状态。 2. **文件I/O**:了解...
总的来说,"Linux系统编程及网络编程笔记"会详细解析上述各个方面,包括理论知识、实际示例和最佳实践,旨在帮助学习者深入理解Linux环境下的系统编程和网络编程,为开发高效、安全的系统级应用程序和网络服务打下...
Linux操作系统是全球众多程序员...总之,Linux操作系统的学习笔记覆盖了从系统安装到文件管理,从用户权限到进程调度,从设备管理到网络控制等多个层面的知识点,是学习Linux操作系统和提升系统管理能力的重要资源。
### Linux系统编程笔记知识点概述 #### 一、常见出错处理 在Linux系统编程中,正确处理各种可能发生的错误对于程序的稳定性和可靠性至关重要。以下是一些常用的错误处理方法: ##### 1. `abort` - **定义**:`#...
进程是程序的运行实例,管理系统资源和执行程序代码。Unix/Linux提供了丰富的进程管理工具和系统调用,如`fork`用于创建新进程,`exec`系列用于执行新程序,`wait`和`signal`用于进程间通信。 ### Unix/Linux系统的...
学习Linux的进程管理对于理解和操作系统的运行至关重要。本篇笔记主要探讨了以下几个方面的知识点: 1. **进程状态**: - **可运行态(TASK_RUNNING)**:进程正在CPU上执行或等待执行。 - **可中断的等待(TASK_...
IX 只能够用于单一的 Unix 系统进程间通信,而 AF_INET 是针对Internet的,因而可以允许在 远程 主机之间通信(当我们 man socket 时发现 domain 可选项是 PF_*而不是AF_*,因为 glibc 是 posix 的实现 所以用 PF代替...
《马哥的Linux学习笔记》是一份针对初学者和进阶者精心编撰的Linux教程,旨在帮助读者全面掌握Linux操作系统的核心概念、命令行操作以及系统管理技能。这份笔记以清晰明了的语言和实例解析了Linux系统的各个方面,是...
Linux学习笔记——入门资料 Linux,作为一款开源、免费的操作系统,因其稳定性和安全性而备受开发者和系统管理员的青睐。这份“Linux学习笔记”旨在帮助初学者快速掌握Linux的基础知识和操作技能,从而轻松入门。 ...
进程-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板
Linux学习基础笔记主要涵盖Linux操作系统的基本概念、安装与配置、常用命令、文件系统管理、用户权限与用户组、进程管理、网络配置以及shell脚本编程等多个方面。以下是对这些知识点的详细阐述: 1. **Linux操作...
总的来说,这份“Linux课堂学习笔记”资料包提供了一个全面的学习路径,涵盖了从基础的Linux操作系统原理,到ARM架构的硬件特性,再到实际的设备驱动编程。对于想要在Linux环境下进行嵌入式开发的学员来说,是一份...
Linux学习笔记PDF文档.pdf是一个详尽的资源,旨在帮助初学者和有经验的用户深入理解Linux操作系统。Linux作为开源的类Unix系统,以其稳定、安全和可定制性在全球范围内广泛应用于服务器、桌面环境以及嵌入式设备。这...
进程间通信简介-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板