进程管理
程序和进程
1.1程序
程序(program)是存放在磁盘文件中的可执行文件。
1.2进程和进程ID
程序的执行实例被称为进程(process)。本书的每一页几乎都会使用这一术语。某些操作系统用任务表示正被执行的程序。
每个linux进程都一定有一个唯一的数字标识符,称为进程ID(process ID)。进程ID总是一非负整数。
linux下的进程结构
Linux系统是一个多进程的系统,进程之间具有并行性、互不干扰的特点。
linux中进程包含PCB(进程控制块)、程序以及程序所操纵的数据结构集,可分为“代码段”、“数据段”和“堆栈段”。
PCB 数据段、代码段、堆栈段
进程状态
进程状态转换图
init进程
它是一个普通的用户进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户特权运行
获取进程标识
fork系统调用
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
返回:子进程中为0,父进程中为子进程I D,出错为-1
由fork创建的新进程被称为子进程( child process)。
该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是子进程的进程ID。
一般来说,在f o r k之后是父进程先执行还是子进程先执行是不确定的。这取决于内核所使用的调度算法。
使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:
进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
子进程与父进程的区别在于:
1、父进程设置的锁,子进程不继承
2、各自的进程ID和父进程ID不同
3、子进程的未决告警被清除;
4、子进程的未决信号集设置为空集。
exec替换一个进程映象
intexecl(const char *path, const char *arg, ...);
intexeclp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg , ..., char * const envp[]);
intexecv(const char *path, char *const argv[]);
intexecvp(const char *file, char *const argv[]);
path参数表示你要启动程序的名称包括路径名
arg参数表示启动程序所带的参数
int main()
{
printf("Running ps with execlp\n");
execlp("ps","ps","-ax",0);
printf("Done.\n");
exit(0);
}
system启动新的进程
int system(const char *string);
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("PID : %d\n",(int)getpid());
printf("Running ps with system\n");
system("ps -ef");
printf("Done.\n");
exit(EXIT_SUCCESS);
}
避免僵尸进程
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生),所以这种信号也是内核向父进程发的异步通知。父进程可以忽略该信号,或者提供一个该信号发生时即被调用执行的函数(信号处理程序)。对于这种信号的系统默认动作是忽略它。
wait函数用于使父进程阻塞,直到一个子进程结束或者该进程接收到一个指定信号为止。
调用wait或waitpid的进程可能会:
wait和waitpid函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int * status) ;
两个函数返回:若成功则为子进程I D号,若出错则为-1.
Status选项,为空时,代表任意状态结束的子进程,若不为空,则代表指定状态结束的子进程。
waitpid函数说明
pid_t waitpid(pid_t pid, int * status, int options) ;
对于waitpid的p i d参数的解释与其值有关:
wait和waitpid的区别
exit和_exit
exit和_exit用于中止进程;
_exit的作用:直接使进程停止运行,清除其使用的内存空间,并清除其在内核中的数据结构;
exit与_exit函数不同,exit函数在调用exit系统之前要检查文件打开情况把文件缓冲区的内容写回文件中去。如调用printf()函数。
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//************************************fork()创建子进程实验******************************************
//2013.10.15.
//孤儿进程:老爸死了,,儿子就变成孤儿进程,被init进程给收了,init进程就是他的父进程(1)。ps -aux | grep main (查看进程信息)
//************************************fork()创建子进程实验******************************************
int main()
{
int pid = fork();
if (pid > 0)
{
sleep(1); //老爸存活一秒就死了
}
if (pid == 0)
{
while(1)
{
printf("ppid: %d\n", getppid());
sleep(2);
}
}
return 0;
}
//************************************fork()创建子进程实验******************************************
//2013.10.15.
//僵尸进程:儿子死了,老爸很忙(R),没空收尸,儿子就变成僵尸进程(Z)。ps -aux | grep main (查看进程信息)
//************************************fork()创建子进程实验******************************************
int main()
{
int pid = fork();
if (pid > 0)
{
while(1)
;
}
if (pid == 0)
{
}
return 0;
}
//************************************fork()创建子进程实验******************************************
//2013.10.15.陈顺明
//父进程关闭文件,子进程还是能够写入文件的。
//父进程和子进程能够同时对文件进行写操作。
//************************************fork()创建子进程实验******************************************
int main()
{
char acBuf[1024];
int fd = open("/mnt/aa", O_WRONLY | O_CREAT | S_IRWXU);
int pid = fork();
if (pid >0)
{
lseek(fd, 0, SEEK_SET);
write(fd, "fffffffffffff", strlen("fffffffffffff"));
close(fd);
}
else if (pid == 0)
{
// sleep(1);
write(fd, "Hello!", strlen("Hello!"));
close(fd);
}
return 0;
}
//************************************fork()创建子进程实验******************************************
//2013.10.15.
//父进程与子进程数据的逻辑地址是一样的,指向的物理地址不一样。
//执行fork(),创建一个空白的子进程PCB,给PCB赋值代码段(把父进程代码拷贝给子进程);数据段(拷贝的是父进程数据的逻辑地址)
//子进程代码段程序入口为fork()那里(父进程入口为main())
//最开始数据逻辑地址指向的物理地址是一样的,改变变量的时候,内核会找个新的内存空间,
//把新的数据拷备(写实拷贝:copy-on-right)到新的物理地址。
//就造成相同的逻辑地址指向的物理地址不一样
//************************************fork()创建子进程实验******************************************
int main()
{
int iCount = 0;
int pid = fork();
if (pid >0)//父进程这里返回的pid是子进程的pid>0
{
printf("iCount:%d\n", iCount+100); //结果为100
printf("iCount Adress:%p\n", &iCount);
}
else if (pid == 0)//子进程
{
printf("iCount:%d\n", iCount-100); //结果为-100
printf("iCount Adress:%p\n", &iCount); //父进程与子进程数据的逻辑地址是一样的
}
return 0;
}
//************************************fork()创建子进程实验******************************************
//2013.10.15.
//调用一次fork()返回两次(一个为子进程pid>0)(fork()里面执行父进程内容),还有一个返回0(for()里面执行子进程内容))
//************************************fork()创建子进程实验******************************************
int main()
{
printf("main进程\n");
printf("pid:%d\n", getpid());
printf("parent pid:%d\n", getppid());
printf("main进程\n");
int pid = fork();
if (pid >0) //父进程这里返回的pid是子进程的pid
{
printf("父进程\n");
printf("pid:%d|||%d\n", getpid(),pid); //(pid和ppid)与(main的pid、ppid)一样
printf("parent pid:%d\n", getppid());
printf("父进程\n");
}
else if (pid == 0) //子进程
{
printf("子进程\n");
printf("pid:%d\n", getpid()); //(ppid)与((main或父进程)的pid)一样
printf("parent pid:%d\n", getppid());
printf("子进程\n");
}
return 0;
}
//###########避免产生僵尸进程###########避免产生僵尸进程###########避免产生僵尸进程###########
//###########避免产生僵尸进程###########避免产生僵尸进程###########避免产生僵尸进程###########
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
//*****************************************waitpid()**********************************
//2013.10.16.
//pid_t waitpid(pid_t pid, int *status, int options);
//参数:pid_t pid:进程ID int *status:退出状态 int options:等待选项
// pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。
// pid > 0 等待其进程I D与p i d相等的子进程。
// pid == 0 等待其组I D等于调用进程的组I D的任一子进程。
// pid < -1 等待其组I D等于p i d的绝对值的任一子进程。
//options:
//0 阻塞状态(默认就是阻塞状态)
// WNOHANG 如果没有任何已经结束的子进程则马上返回(不阻塞),不予以等待。(返回的退出码不对)
// WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
//int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
//*****************************************waitpid()**********************************
int main()
{
int pid = fork();
if (pid > 0)
{
int status;
printf("wait...\n");
waitpid(-1, &status, WNOHANG); //如果没有任何已经结束的子进程则马上返回,(造成返回的退出码不对)
printf("done...\n");
printf("child exit code:%d\n", WEXITSTATUS(status));
}
else if(pid == 0)
{
sleep(2);
exit(100); //退出代码只能0~255,超过范围就是不确定的!
}
else
{
perror("fork error!");
exit(2);
}
return 0;
}
//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&wait()和waitpid()区别&&&&&&&&&&&&&&&&&&&&&&&&&&
//wait()必须阻塞;waitpid()第三个参数option可以设置是否要阻塞,如果不阻塞返回的退出码不对
//wait()是基于waitpid()实现的,wait()是waitpid的一个特例
//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&wait()和waitpid()区别&&&&&&&&&&&&&&&&&&&&&&&&&&
//*****************************************wait()**********************************
//2013.10.16.
//pid_t wait(int *status);返回子进程退出码
//int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
//*****************************************wait()**********************************
int main()
{
int pid = fork();
if (pid > 0)
{
int status;
printf("wait...\n");
wait(&status);
printf("done...\n");
printf("child exit code:%d\n", WEXITSTATUS(status));//如果WIFEXITED非零,返回子进程退出码。。这里返回值为100
}
else if(pid == 0)
{
sleep(2);
exit(100); //退出代码只能0~255,超过范围就是不确定的!
}
else
{
perror("fork error!");
exit(2);
}
return 0;
}
//这些宏在sys/wait.h头文件里定义
// WIFEXITED(stat_val) 如果子进程正常结束,返回一个非零值
// WEXITSTATUS(stat_val) 如果WIFEXITED非零,返回子进程退出码*****
// WIFSIGNALED(stat_val) 子进程因未捕获信号而终止,返回非零值
// WTERMSIG(stat_val) 如果WIFSIGNALED非零,返回信号代码
// WIFSTOPPED(stat_val) 如果子进程终止,返回一个非零值
// WSTOPSIG(stat_val) 如果WIFSTOPPED非零,返回一个信号代码
//**************** exit和_exit的不同,代码实现*****************************
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("Hello World!"); //不加\n
_exit(0); //打印不出来
return 0;
}
int main()
{
printf("Hello World!"); //不加\n
exit(0); //能打印出来
return 0;
}
// exit和_exit用于中止进程;
// _exit的作用:直接使进程停止运行,清除其使用的内存空间,并清除其在内核中的数据结构。(系统调用)
// exit函数在调用exit系统之前要检查文件打开情况把文件缓冲区的内容写回文件中去。如调用printf()函数。(是个C函数)