一、进程标识符。
1、进程中重要的ID值。
#include <unistd.h>
pid_t getpid(void);进程ID
pid_t getppid(void);父进程ID
uid_t getuid(void);用户ID
uid_t geteuid(void);有效用户ID
gid_t getgid(void);组ID
gid_t getegid(void); 有效组ID
对一般进程而言,用户ID与有效用户ID的一样的
二、进程操作。
1、创建一个进程。
#include<unistd.h>
pid_t fork(void);
子进程复制了父进程的数据段,堆栈段。共享了代码段。没有共享空间。
2、fork函数的出错情况。
系统中已经有太多的进程存在。调用fork函数的用户的进程太多。
3、创建一个共享空间的子进程。
# include<unistd.h>
pid_t vfork(void);
子进程和父进程完全共享地址空间。包括代码段,数据段和堆栈段。
子进程一定比父进程先运行。
注意在函数内部调用vfork函数的问题。
4、退出进程。
退出函数深入内核注销掉进程的内核数据结构,并且释放进程的资源。
可以利用exit函数检查进程出错信息。
在头文件中定义#include<errno.h> 在程序中退出时exit(errno);
exit函数与内核函数的关系:exit是一个标准的函数库。与_exit函数不同的是 exit 会用善后的工作。如清理用户的I/O的缓冲区,写入磁盘文件中。而_exit就不会这样,它只是进入内核释放用户进程地址空间,用户空间的缓冲区内容都将丢失。
5、设置进程所有者。
@改变进程的实际用户ID和有效用户ID。用int setuid(uid_t uid);
@只有根用户或者是该用户的用户ID等于进程的实际用户ID或者保存用户ID。
@还有只修改有交用户ID的函数。int seteuid(uid_t uid);
setgid(gid_t gid);
setegid(gid_t gid);
6、调试多进程
在gdb中设置set follow-fork-mode [parent | child]
如果在选parent 时就是跟踪父进程。
三、执行一个新程序。
1、利用exec函数,不是创建一个进程,而是只是在子进程中的代码段和数据段已经被替换了。
int execl(const char * pathname,const char *arg0);
int execle(const char *pathname.const char
int execve();int execlp();
int execvp();
"l"表示List并且以NULL结尾。
“V”:表示命令行参数要以二维数组的形式提供给新程序。这个数组的每一行是一个命令行参数。
“e”表示传递给新进程的环境变量表。
“p”表示第一个参数不是完整的路径名,而是一个程序名。
@exec只会改变有效用户ID和有效组ID。
2、执行解释器文件。
执行解释器文件有两种,一:二进制可执行文件,二:为文本文件.例如:(#!/bin/sh )
3、在程序中执行shell命令。
#include<stdlibl.h>
int system(const char *cmdstring);
例如:system("ls > temp.txt")这样就是表示执行Ls查看当前的目录并保存到temp.tex中.
4、实现system函数。
system函数实现的流程分两步走:首先调用system函数的进程创建出一个子进程,并调用wait函数等待子进程执行完毕;然后这个子进程再调用exec函数来加载shell运行cmdstring中指定的命令。
- #include <sys/wait.h>
- #include<unistd.h>
- int system(const char* cmdstring)
- {
- pid_t pid;
- int status;
- if(cmdstring==NULL)
- return 1;
- pid= fork();
- if(pid<0)
- status = -1;
- else if(pid==0)
- {
- execl("/bin/sh","sh","-c",cmdstring ,NULL);
- _exit(127); 如果执行失败,子进程退出。
- }
- if(waitpid(pid,&status,0)==-1)
- status=-1;
- return status;
- }
四、关系操作。
1、等待进程退出。
#include <sys/wait.h>
pid_t wait(int *statloc);
调用wait函数的进程会阻塞,直到该进程的任意一个子进程结束,wait函数会取得结束的子进程的信息并且返回该子进程的进程ID。结束信息会保存在statloc中。如果没有子进程则立即出错返回,返回值为-1;
状态 | 判断宏 | 取值宏 |
正常结束 | WIFEXITED(STATTUS) | WEXITSTATUS(STATUS) |
异常结束 | WIFSIGNALED(STATUS) | WTERMSIG(STATUS) |
进程暂停 | WIFSTOPPED(STATUS) | WSTOPSIG(STATUS) |
2、等待指定进程。
#include <sys/wait.h>
pid_t waitpid(pid-_t pid, int *status,int options);
pid=-1时,等待任意进程。
pid >0时,等待进程ID和pid相等的子进程。
pid=0时,等待组Id和pid相等的子进程。
pid<-1时,等待组ID等于pid绝对值的组内的任意子进程。
options有3个参数可选。
WCONTINUED :当子进程在暂停后继续执行,且状态尚未报告,则返回其状态。
WNOHANG :当所等待进程尚未结束运行时不阻塞,waitpid函数直接返回。
WUNTRACED :当子进程暂停时,并且其状态处暂停以来还未报告过,则返回其状态。
与wait不同:
waitpid可以指定一个子进程;可以不阻塞等待一个子进程;支持作业控制。
3、僵尸进程的概念。
当子进程退出时,他退出的信息就在内核中,子进程的ID也在系统的进程列表中,这时的进程被称为僵尸进程。
僵尸进程是有坏处的。因为他占用了进程号。只有调用wait函数才可以解决。
4、产生一个僵尸进程
当父进程调用fork产生一个子进程,而没有调用wait来获得子进程退出的信息,就会产生一个僵尸进程。
用ps -ax 可以查找,当STAT为S时,就是僵尸进程。
5、避免僵尸进程的产生。
当父进程比子进程退出先,子进程就会变成孤儿进程。孤儿进程会被init进程领养的。而init进程总是有调用wait函数。
6、输出进程统计信息。
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/time.h>
#include<source.h>
pid_t wait3(int *statloc,int options,struct rusage * rusage);
pid_t wait4(pid_t pid, int *statloc,int options,struct rusage *rusage);