进程的属性,建立子进程,信号,进程的控制,进程的调度
进程的属性
进程的组成元素
* 进程的上下文
* 进程的当前目录
* 进程的权限
* 进程访问的文件或目录
* 进程分得的内存,等其他系统资源
内核使用进程来控制对CPU和其他系统资源的访问。内核的调度器为每个进程分一个极短的时间段,来供进程执行其指令,该时间段称为时间片。即每个进程轮流执行一个时间片的时长。
进程的标号
能够唯一标识一个进程的非零的正整数称为PID(process ID),建立该进程的进程的PID是该进程的PPID(parent ID),即父进程号。所有的进程往上进行追溯,最终都会停在一个叫init的进程,这个进程是内核自举后第一个启动的进程。
下表列出的函数用来得到对应的属性,在使用他们之前必须先,包含头文件unistd.h和sys/types.h两个头文件。
函数 属性 注释
pid_t getpid (void); PID
pid_t getppid (void); PPID
uid_t getuid (void); UID 登录用的ID
uid_t geteuid (void); 有效用户ID 文件的UID而不是执行者的UID,当程序设置了setUID或setGID时,UID和有效用户ID就变得不一样了
gid_t getgid (void); GID 登录时的用户所在的组的ID
gid_t getegid (void); 有效组ID
例如:
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(void)
{
printf("PID : %4d\n", getpid());
printf("PPID : %4d\n", getppid());
printf("UID : %4d\n", getuid());
printf("eUID : %4d\n", geteuid());
printf("GID : %4d\n", getgid());
printf("eGID : %4d\n", getegid());
return 0;
}
运行结果为:
PID : 6200
PPID : 6107
UID : 500
eUID : 500
GID : 500
eGID : 500
当然,结果在不同的机器上会有不同,在这里,UID和eUID相同,是因为我执行的是一个属于UID为500的文件。
用户信息和组的信息
#include <unistd.h>
char * getlogin (void); /*返回执行该程序用户登录的用户名,失败返回NULL*/
#include <pwd.h>
struct passwd * getpwnam (const char * name);/*得到用户信息,*/
struct passwd {
char *pw_name; /* user name */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* real name */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
例如:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
int main(void)
{
char * name;
struct passwd * msg;
if (NULL == (name = getlogin()))
{
perror("getlogin error");
exit(EXIT_FAILURE);
}
if (NULL == (msg = getpwnam(name)))
{
perror("getpwnam error");
exit(EXIT_FAILURE);
}
printf("user name : %s\n", msg->pw_name);
printf("user ID : %d\n", msg->pw_uid);
printf("group ID : %d\n", msg->pw_gid);
printf("real name : %s\n", msg->pw_gecos);
printf("home directory : %s\n", msg->pw_dir);
printf("shell : %s\n", msg->pw_shell);
return 0;
}
输出为:
user name : gin
user ID : 500
group ID : 500
real name : hades gin
home directory : /home/gin
shell : /bin/bash
进程时间信息
#include <sys/times.h>
clock_t times (struct tms * buf); /*返回系统自自举后到目前的滴答数,buf保存了当前进程的时间信息。*/
struct tms {
clock_t tms_utime; /* user time */
clock_t tms_stime; /* system time */
clock_t tms_cutime; /* user time of children */
clock_t tms_cstime; /* system time of children */
};
例如:
#include <stdlib.h>
#include <stdio.h>
#include <sys/times.h>
#include <limits.h>
#include <time.h>
#include <unistd.h>
int main(void)
{
long i = 0;
clock_t start, end;
struct tms tmmsg;
start = times(&tmmsg);
while (i++ < LONG_MAX);
end = times(&tmmsg);
printf("spend %f seconds\n",
(float)(end - start) / sysconf(_SC_CLK_TCK));
printf("user time : %f\n",
(float)(tmmsg.tms_utime / sysconf(_SC_CLK_TCK)));
printf("system time : %f\n",
(float)(tmmsg.tms_stime / sysconf(_SC_CLK_TCK)));
return 0;
}
输出结果为:
spend 6.240000 seconds
user time : 6.000000
system time : 0.000000
进程的资源信息
struct rusage {
struct timeval ru_utime; /* user time used */
struct timeval ru_stime; /* system time used */
long ru_maxrss; /* maximum resident set size */
long ru_ixrss; /* integral shared memory size */
long ru_idrss; /* integral unshared data size */
long ru_isrss; /* integral unshared stack size */
long ru_minflt; /* page reclaims */
long ru_majflt; /* page faults */
long ru_nswap; /* swaps */
long ru_inblock; /* block input operations */
long ru_oublock; /* block output operations */
long ru_msgsnd; /* messages sent */
long ru_msgrcv; /* messages received */
long ru_nsignals; /* signals received */
long ru_nvcsw; /* voluntary context switches */
long ru_nivcsw; /* involuntary context switches */
};
利用该结构能够得到更多有用的信息,不过,在这里,真正能够个使用的只有ru_utime,ru_stime,ru_minflt,ru_majflt,ru_swap;其中ru_mainflt是指要访问的数据不在寄存器或者高速缓存上,需要到内存中读取,但没有磁盘上读取,ru_majflt数据在内存中也没有找到,必须要到磁盘中寻找。而ru_swap保存了因为major fault而必须从磁盘中读取的页数。
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
int getrusage(int who, struct rusage * usage);
其中who的取值有两个,要么是RUSAGE_SELF用来表示他自己,或者RUSAGE_CHILDREN表示其子进程的信息。成功返回0,否则返回-1;
进程组和会话
一个进程组是一个相关进程的集合,这些相关进程通常是在一个管道中的命令序列,在进程中,所有的进程都具有相同的进程组号,即PGID,使用进程组的目的是为了方便的进行作业控制,例如,当你在执行ls -l | grep str时按下Ctrl-C键会将这个进程组杀掉,而不是单单的某一个进程。
会话由一个或多个进程组构成,会话领导进程是创建会话的进程,每一个会话都有唯一的标识号,称为会话ID,他只是会话领导进程的PID。
创建进程
使用system()函数
他通过将system传递给/bin/sh -c来执行string所指定的命令。接着整个命令行(/bin/s -c string)又传递给系统调用execve,如果没有找到/bin/sh,则返回127.出现错误,则返回-1,否则返回string的代码,若string为NULL,则返回一个非零的值。
fork系统调用
使用fork进行创建子进程,则新的进程是父进程的完全拷贝。但是,子进程没有继承父进程的超时设置。父进程创建的文件锁,或者未决信号。
如果调用成功,就向父进程返回子进程的PID,并向子进程返回0。如果失败返回-1。并将不会创建子进程。
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
exec函数族
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *filename, char *const argv[],
char *const envp[]);
exec用被执行的程序完全替换了调用进程的映像,他会启动一个新的程序来替换原有的进程,因此,进程的PID将不会被改变。
其中,execve的调用类似main函数的 int main(int argc, char *argv[], char *envp[])形式。argv是以','间隔,并以NULL结束的字符数组。
在这些函数中,path用来指明路径,当他是哟个目录的时候,则会像shell一样搜索$PATH,寻找执行文件。带 l的函数中,参数不需要以字符数组的形式出现,而是将其每一项作为参数传入函数。例如:
execl("/bin/ls","/bin/ls","-l",NULL);
使用带v的数组,则必须先构造一个字符数组,就向上面说的一样。在带参数envp的函数中,可以通过下面的函数进行环境变量的设置;
#include <stdlib.h>
int putenv(const char * string);
char * grtenv(const cahr * name);
使用popen函数
#include <stdio.h>
FILE * popen(const char * command, const char * type);
int pclose(FILE * stream);
popen调用管道,并创建通向标准输入或者从command指定的程序或脚本的标准输出来的管道,但不能两者同时使用。typw的取值为w,r,此时的读和写操作是相对于command的来说的。
控制进程
等待进程
当使用fork或exec创建了一个新的进程之后,为了收集进程退出的状态,并避免出现僵进程,父进程应该等待子进程终止,一个僵进程是在父进程有机会用wait或者waitpid收集他的退出状态之前就终止的子进程,父进程通过使用wait函数之一检索内核的进程表,取得退出状态来收集子进程的退出状态,之所以称其为僵进程是应为,他虽然死掉了,但仍然存在于进程表中,子进程退出后,分配给他的资源和内存被释放,但在内核的进程表中仍能看到该进程,内核在父进程收回其退出状态之前一直保留他。
一个孤儿是一个父进程在调用wait或者waitpid之前就退出的子进程,这是,init就成为了该进程的父进程来收集其退出的状态。
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
其中,pid的取值范围:
-1 等待任何PGID等于PID的绝对值的子进程
1 等待任何子进程
0 等待任何OGID等于调用进程的子进程
>0 等待PID为pid的子进程
option规定了他的行为:
WNOHANG 导致waitpid在没有子进程退出时,立即返回
WUNTRACED 应该因为存在没有报告状态的进程而返回
两个值相 或 取得两种行为
进程的终止
`return
`exit
`_exit
`abotr
`收到信号终止
使用kill杀死进程
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
进程的属性
进程的组成元素
* 进程的上下文
* 进程的当前目录
* 进程的权限
* 进程访问的文件或目录
* 进程分得的内存,等其他系统资源
内核使用进程来控制对CPU和其他系统资源的访问。内核的调度器为每个进程分一个极短的时间段,来供进程执行其指令,该时间段称为时间片。即每个进程轮流执行一个时间片的时长。
进程的标号
能够唯一标识一个进程的非零的正整数称为PID(process ID),建立该进程的进程的PID是该进程的PPID(parent ID),即父进程号。所有的进程往上进行追溯,最终都会停在一个叫init的进程,这个进程是内核自举后第一个启动的进程。
下表列出的函数用来得到对应的属性,在使用他们之前必须先,包含头文件unistd.h和sys/types.h两个头文件。
函数 属性 注释
pid_t getpid (void); PID
pid_t getppid (void); PPID
uid_t getuid (void); UID 登录用的ID
uid_t geteuid (void); 有效用户ID 文件的UID而不是执行者的UID,当程序设置了setUID或setGID时,UID和有效用户ID就变得不一样了
gid_t getgid (void); GID 登录时的用户所在的组的ID
gid_t getegid (void); 有效组ID
例如:
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(void)
{
printf("PID : %4d\n", getpid());
printf("PPID : %4d\n", getppid());
printf("UID : %4d\n", getuid());
printf("eUID : %4d\n", geteuid());
printf("GID : %4d\n", getgid());
printf("eGID : %4d\n", getegid());
return 0;
}
运行结果为:
PID : 6200
PPID : 6107
UID : 500
eUID : 500
GID : 500
eGID : 500
当然,结果在不同的机器上会有不同,在这里,UID和eUID相同,是因为我执行的是一个属于UID为500的文件。
用户信息和组的信息
#include <unistd.h>
char * getlogin (void); /*返回执行该程序用户登录的用户名,失败返回NULL*/
#include <pwd.h>
struct passwd * getpwnam (const char * name);/*得到用户信息,*/
struct passwd {
char *pw_name; /* user name */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* real name */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
例如:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
int main(void)
{
char * name;
struct passwd * msg;
if (NULL == (name = getlogin()))
{
perror("getlogin error");
exit(EXIT_FAILURE);
}
if (NULL == (msg = getpwnam(name)))
{
perror("getpwnam error");
exit(EXIT_FAILURE);
}
printf("user name : %s\n", msg->pw_name);
printf("user ID : %d\n", msg->pw_uid);
printf("group ID : %d\n", msg->pw_gid);
printf("real name : %s\n", msg->pw_gecos);
printf("home directory : %s\n", msg->pw_dir);
printf("shell : %s\n", msg->pw_shell);
return 0;
}
输出为:
user name : gin
user ID : 500
group ID : 500
real name : hades gin
home directory : /home/gin
shell : /bin/bash
进程时间信息
#include <sys/times.h>
clock_t times (struct tms * buf); /*返回系统自自举后到目前的滴答数,buf保存了当前进程的时间信息。*/
struct tms {
clock_t tms_utime; /* user time */
clock_t tms_stime; /* system time */
clock_t tms_cutime; /* user time of children */
clock_t tms_cstime; /* system time of children */
};
例如:
#include <stdlib.h>
#include <stdio.h>
#include <sys/times.h>
#include <limits.h>
#include <time.h>
#include <unistd.h>
int main(void)
{
long i = 0;
clock_t start, end;
struct tms tmmsg;
start = times(&tmmsg);
while (i++ < LONG_MAX);
end = times(&tmmsg);
printf("spend %f seconds\n",
(float)(end - start) / sysconf(_SC_CLK_TCK));
printf("user time : %f\n",
(float)(tmmsg.tms_utime / sysconf(_SC_CLK_TCK)));
printf("system time : %f\n",
(float)(tmmsg.tms_stime / sysconf(_SC_CLK_TCK)));
return 0;
}
输出结果为:
spend 6.240000 seconds
user time : 6.000000
system time : 0.000000
进程的资源信息
struct rusage {
struct timeval ru_utime; /* user time used */
struct timeval ru_stime; /* system time used */
long ru_maxrss; /* maximum resident set size */
long ru_ixrss; /* integral shared memory size */
long ru_idrss; /* integral unshared data size */
long ru_isrss; /* integral unshared stack size */
long ru_minflt; /* page reclaims */
long ru_majflt; /* page faults */
long ru_nswap; /* swaps */
long ru_inblock; /* block input operations */
long ru_oublock; /* block output operations */
long ru_msgsnd; /* messages sent */
long ru_msgrcv; /* messages received */
long ru_nsignals; /* signals received */
long ru_nvcsw; /* voluntary context switches */
long ru_nivcsw; /* involuntary context switches */
};
利用该结构能够得到更多有用的信息,不过,在这里,真正能够个使用的只有ru_utime,ru_stime,ru_minflt,ru_majflt,ru_swap;其中ru_mainflt是指要访问的数据不在寄存器或者高速缓存上,需要到内存中读取,但没有磁盘上读取,ru_majflt数据在内存中也没有找到,必须要到磁盘中寻找。而ru_swap保存了因为major fault而必须从磁盘中读取的页数。
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
int getrusage(int who, struct rusage * usage);
其中who的取值有两个,要么是RUSAGE_SELF用来表示他自己,或者RUSAGE_CHILDREN表示其子进程的信息。成功返回0,否则返回-1;
进程组和会话
一个进程组是一个相关进程的集合,这些相关进程通常是在一个管道中的命令序列,在进程中,所有的进程都具有相同的进程组号,即PGID,使用进程组的目的是为了方便的进行作业控制,例如,当你在执行ls -l | grep str时按下Ctrl-C键会将这个进程组杀掉,而不是单单的某一个进程。
会话由一个或多个进程组构成,会话领导进程是创建会话的进程,每一个会话都有唯一的标识号,称为会话ID,他只是会话领导进程的PID。
创建进程
使用system()函数
他通过将system传递给/bin/sh -c来执行string所指定的命令。接着整个命令行(/bin/s -c string)又传递给系统调用execve,如果没有找到/bin/sh,则返回127.出现错误,则返回-1,否则返回string的代码,若string为NULL,则返回一个非零的值。
fork系统调用
使用fork进行创建子进程,则新的进程是父进程的完全拷贝。但是,子进程没有继承父进程的超时设置。父进程创建的文件锁,或者未决信号。
如果调用成功,就向父进程返回子进程的PID,并向子进程返回0。如果失败返回-1。并将不会创建子进程。
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
exec函数族
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *filename, char *const argv[],
char *const envp[]);
exec用被执行的程序完全替换了调用进程的映像,他会启动一个新的程序来替换原有的进程,因此,进程的PID将不会被改变。
其中,execve的调用类似main函数的 int main(int argc, char *argv[], char *envp[])形式。argv是以','间隔,并以NULL结束的字符数组。
在这些函数中,path用来指明路径,当他是哟个目录的时候,则会像shell一样搜索$PATH,寻找执行文件。带 l的函数中,参数不需要以字符数组的形式出现,而是将其每一项作为参数传入函数。例如:
execl("/bin/ls","/bin/ls","-l",NULL);
使用带v的数组,则必须先构造一个字符数组,就向上面说的一样。在带参数envp的函数中,可以通过下面的函数进行环境变量的设置;
#include <stdlib.h>
int putenv(const char * string);
char * grtenv(const cahr * name);
使用popen函数
#include <stdio.h>
FILE * popen(const char * command, const char * type);
int pclose(FILE * stream);
popen调用管道,并创建通向标准输入或者从command指定的程序或脚本的标准输出来的管道,但不能两者同时使用。typw的取值为w,r,此时的读和写操作是相对于command的来说的。
控制进程
等待进程
当使用fork或exec创建了一个新的进程之后,为了收集进程退出的状态,并避免出现僵进程,父进程应该等待子进程终止,一个僵进程是在父进程有机会用wait或者waitpid收集他的退出状态之前就终止的子进程,父进程通过使用wait函数之一检索内核的进程表,取得退出状态来收集子进程的退出状态,之所以称其为僵进程是应为,他虽然死掉了,但仍然存在于进程表中,子进程退出后,分配给他的资源和内存被释放,但在内核的进程表中仍能看到该进程,内核在父进程收回其退出状态之前一直保留他。
一个孤儿是一个父进程在调用wait或者waitpid之前就退出的子进程,这是,init就成为了该进程的父进程来收集其退出的状态。
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
其中,pid的取值范围:
-1 等待任何PGID等于PID的绝对值的子进程
1 等待任何子进程
0 等待任何OGID等于调用进程的子进程
>0 等待PID为pid的子进程
option规定了他的行为:
WNOHANG 导致waitpid在没有子进程退出时,立即返回
WUNTRACED 应该因为存在没有报告状态的进程而返回
两个值相 或 取得两种行为
进程的终止
`return
`exit
`_exit
`abotr
`收到信号终止
使用kill杀死进程
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);