进程的基本概念:
首先要了解一下程序的概念,什么是程序?其实我们编写的a.c就是一个c程序,程序是静态的,可以永久保存。
然后要了解一下进程,我们在用gcc a.c -o a;编译时产生的二进制文件a然后./a 这就是进程,进程是动态的,不能永久保存。所以 进程是一个程序正在执行的实例。每个这样的实例都有自己的地址空间和执行状态。
Linux下可执行的程序有哪些?
可执行目标文件
经链接器链接后可直接执行的文件称为可执行目标文件。内核一般支持几种特定格式的可执行文件。ELF格式是Linux系统中普遍使用的一种标准的可执行文件格式。
可执行脚本
可执行脚本是一个特殊的文本文件,它能够指示内核启动一个解释器去执行后续的内容。这个解释器必须是可执行目标文件。一般情况下,脚本的解释器是Shell,但内核也会查看脚本文件第一行,如果前两个字符是#!,它就会将第一行的剩余部分解析为启动解释器的命令。例如,一个Shell脚本的第一行通常如下:
#!/bin/sh这样内核将会启动/bin/sh作为脚本的解释器。
内核怎么区分不同进程
进程有一个PID(Process ID,进程标识),用以区分各个不同的进程。内核记录进程的PID与状态,并根据这些信息来分配系统资源(如内存等)。
当内核产生一个新的PID,生成对应的用于管理的数据结构,并为运行程序代码分配了必要的资源,一个新的进程就产生了。
获得PID
每个进程都有一个ID(ID是一个正整数),唯一标识了系统中的这个进程。
获得调用进程(the calling process)的ID:
#include
pid_t getpid(void);
每个进程都有一个创建它的父进程(Parent Process),可以通过getppid()获得父进程的ID:
#include
pid_t getppid(void);
fork()之后,不能确定是父进程还是子进程获得CPU。
危害:这种bugs很难被发现。
措施:如果需要确保特定的执行顺序,需要采用某种同步(synchronization)技术(semaphores,file locks...)。
进程的终止:
通常由8种方式使进程终止(terminate)
5种正常终止:
从main函数返回
调用exit
调用_exit或_Exit
最后一个线程从其启动例程(start routine)返回
最后一个线程调用pthread_exit
3种异常终止:
调用abort
接到一个信号并终止
最后一个线程对取消请求做出响应
不管进程如何终止,最后都会执行内核中的同一段代码:用于关闭所有打开的文件描述符,释放内存等。
/*****************************************************
> File name: pid.c
> Author: Mr.YUAN
> 日期: 2017-12-06 10:33
*****************************************************/
#include
#include
#include
#include
int main()
{
pid_t pid;
pid = fork();
if(-1 == pid)
{
perror("pid");
exit(1);
}
else if(0 == pid)
{
printf("childpid = %d , parent pid = %d\n",getpid(),getppid());
}
else
{
printf("parent pid = %d\n",getpid());
}
return 0;
}
ecec函数族
Linux系统中有一系列的函数可以将一个进程的执行流程从一个可执行程序转移到另一个可执行程序,也就是装载并运行一个程序。这些函数通常被称为exec函数族:
#include
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv []);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg, ...);
int execvp(const char *file, char *const argv[]);
注意:调用exec并不创建新进程,只是用一个全新的程序替换了当前进程的正文段、数据、堆和栈。
/*****************************************************
> File name: vfork.c
> Author: Mr.YUAN
> 日期: 2017-12-06 11:45
*****************************************************/
#include
#include
#include
#include
int main()
{
pid_t pid;
int count = 0;
char *argv[] = {"ls","NULL"};
pid = vfork();
if(-1 == pid)
{
perror("vfork");
exit(1);
}
else if(0 == pid)
{
printf("child pid = %d\n",getpid());
execl("/bin/ls",*argv);
}
else
{
printf("parent pid = %d\n",getpid());
}
return 0;
}
监控子程序:
父进程创建子进程后,如何知道子进程什么时候终止?如何知道子进程怎么终止(正常or异常)?
措施
wait()或waitpid()...
#include
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
返回值:若成功返回进程ID,若出错返回-1。
调用wait或waitpid的进程可能发生的情况有:
如果所有子进程都还在运行,则阻塞(Block)。
如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。
如果它没有任何子进程,则立即出错返回。
wait 和waitypid的区别:
在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞。
waitpid并不等待在其调用之后的第一个终止的子进程。它有若干个选项,可以控制它所等待的进程。
如果一个子进程已经终止,并且是一个僵死进程,wait立即返回并取得该子进程的状态,否则wait使其调用者阻塞直到一个子进程终止。如果调用者阻塞并且它有多个子进程,则在其一个子进程终止时,wait就立即返回。因为wait返回终止子进程的ID,所以总能了解到是哪一个子进程终止了。
注:僵死进程(zombie),一个已经终止、但是其父进程尚未对其进行善后处理(获得终止子进程的有关信息,释放它仍占用的资)的进程被称为僵死进程。
/*****************************************************
> File name: wait.c
> Author: Mr.YUAN
> 日期: 2017-12-06 16:15
*****************************************************/
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
int status;
pid = fork();
if(-1 == pid)
{
perror("fork");
exit(1);
}
else if(0 == fork)
{
sleep(2);
printf("child process\n");
exit(6);
}
else
{
printf("parent process\n");
wait(NULL);
}
return 0;
}
父子进程之间可以共享文件:
/*****************************************************
> File name: fork.c
> Author: Mr.YUAN
> 日期: 2017-12-06 14:39
*****************************************************/
#include
#include
#include
#include
#include
#include
#include
void childwrite()
{
int fd;
int ret;
char buf[100] = {0};
fd = open("b.txt",O_WRONLY | O_EXCL,0666 | O_CREAT);
if(-1 == fd)
{
perror("open");
exit(1);
}
while(1)
{
scanf("%s",buf);
ret = write(fd,buf,strlen(buf));
if(-1 == ret)
{
perror("write");
exit(1);
}
if(!strncmp(buf,"end",3));
{
break;
}
memset(buf,0,sizeof(buf));
}
}
void parentread()
{
int fd;
int ret;
char buf[100] = {0};
fd = open("b.txt",O_RDONLY);
if(-1 == fd)
{
perror("open");
exit(1);
}
while(1)
{
ret = read(fd,buf,sizeof(buf));
if(-1 == ret)
{
perror("read");
exit(1);
}
else if(ret != 0)
{
if(strncmp(buf,"end",3) == 0)
break;
printf("%s",buf);
}
memset(buf,0,sizeof(buf));
}
}
int main()
{
pid_t pid;
pid = fork();
if(-1 == pid)
{
perror("fork");
exit(1);
}
else if(0 == pid)
{
childwrite();
}
else
{
parentread();
}
return 0;
}