1.进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元
2.进程与程序的概念: 程序是放到磁盘的可执行文件;
进程是指程序执行的实例。
3.区别:1.进程是动态的,程序是静态的:程序是有序代码的集合;进程是程序的执行。通
常进程不可在计算机之间迁移;而程序通常对应
着文件、静态和可以复制;
2.进程是暂时的,程序使长久的:进程是一个状态变化的过程,程序可长久保存;
3.进程与程序组成不同:进程的组成包括程序、数据和进程控制块(即进程状态信
息);
4.进程与程序的对应关系:通过多次执行,一个程序可对应多个进程;通过调用关
系,一个进程可包括多个程序。
4.进程的生命周期: 1.创建: 每个进程都是由其父进程创建,进程可以创建子进程,子进
程又可以创建子进程的子进程;
2.运行: 多个进程可以同时存在,进程间可以通信;
3.撤销: 进程可以被撤销,从而结束一个进程的运行。
5.进程的状态:有三种,分别是:
1) 执行状态:进程正在占用CPU;
2)就绪状态:进程已具备一切条件,正在等待分配CPU的处理时间片;
3}等待状态:进程不能使用CPU,若等待事件发生则可将其唤醒。
Linux系统是一个多进程的系统,它的进程之间具有并行性、互不干扰等特点。即使一个进程发生异常,它也不会影响到系统中的其他进程。
Linux 下进程包含三个段: 数据段”、“代码段”和“堆栈段”。
“数据段”存放的是全局变量、常数以及动态数据分配的数据空间;
“代码段”存放的是程序代码的数据。
“堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。
在linux下,查看当前进程的口令是 ps -elf
如下:
其中 PID 就是进程的ID, 它是标识进程的唯一数字;
进程的父进程的ID就是PPID;
启动进程的用户ID是UID;
下面介绍几个进程的专业名词:
a.进程互斥是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其
他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。
b.操作系统中将一次只允许一个进程访问的资源称为临界资源。(进程中访问临界资源的那段程
序代码称为临界区,为实现对临界资源的互斥访问,应保证诸进程互斥地进入各自的界区)。
c.一组并发进程按一定的顺序执行的过程称为进程间的同步。
具有同步关系一组并发进程称为合作进程,
合作进程间互相发送的信号称为消息或事件。
d.进程调度:按一定算法,从一组待运行的进程中选出一个来占有CPU运行。
调度方式:• 抢占式
• 非抢占式
调度算法:先来先服务调度算法
短进程优先调度算法
高优先级优先调度算法
时间片轮转法
e.死锁:多个进程因竞争资源而形成一种僵局,若无外力作用,这些进程都将永远不能再向前推进
下面介绍如何获取进程的ID:
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void) //获取本进程ID。
pid_t getppid(void) // 获取父进程ID
源代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid : %d\n", getpid());
printf("ppid : %d\n", getppid());
while(1);
return 0;
}
【注】:加while死循环是为了让这个进程不结束,才可以获取id。
此时用ps查看如下:
此时若想停止此进程用kill命令:
下面就来介绍如何创建一个进程(好比我们编译结束后产生的hello可执行二进制文件,我们 运行: ./hello之后跑起来的程序,即可视为一个进程)
#include <unistd.h>
pid_t fork(void)
功能:创建子进程
fork它被调用一次,却返回两次,它可能有三种不同的返回值:
0: 子进程
子进程ID(大于0):父进程
-1: 出错
值得注意的是fork( )创建子进程是大多数情况是先返回0,再返回子进程的ID。
测试代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
int a = 0;
pid = fork(); //创建子进程
if(-1 == pid) //失败返回-1
{
perror("fork");
}
else if(0 == pid) //子进程
{
a++;
printf("this is a childprocess pid : %d, ppid : %d\n", getpid(), getppid());
printf("%d\n", pid);
printf("a : %d\n", a);
printf("&a is %p\n", &a);
}
else //父进程
{
printf("this is a parentprocess pid : %d\n", getpid());
printf("%d\n", pid);
printf("a : %d\n", a);
printf("&a is %p\n", &a);
}
return 0;
}
从中可以看出,子进程和父进程中的a结果不一样,但是地址却是一样的,这是为什么呢?
子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享。
所以在子进程中对a进行加1的操作,并没有影响到父进程中的a值,父进程中的a值仍然为0。
下面介绍另个创建子进程的函数:vfork( )
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void)
该函数创建子进程,共享父进程的地址空间!
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int a = 0;
pid_t pid;
pid = vfork();
if(pid == -1)
{
perror("vfork");
exit(1);
}
else if(0 == pid)
{
a++;
printf("child process a = %d\n", a);
exit(0);
}
else
{
a++;
printf("parent process a = %d\n", a);
}
return 0;
}
值得注意的是vfork创建的子进程必须显示调用exit(0);来结束,否则子进程不能结束!
还有一条:子进程先运行父进程后运行。这点必须和fork区分开。
fork与vfork的区别:
fork |
vfork |
子进程拷贝父进程的数据 |
子进程与父进程共享数据 |
父、子进程的执行次序不确定 |
子进程先运行,父进程后运行 |
exec函数组:包含了几组不通过函数,用被执行的程序替换调用它的程序。
因为exec启动一个新程序,替换原有的进程,因此进程的PID不会改变。
下面介绍execl与execv
execl( )函数原型如下:
Int execl(const char *path, const char *arg,….)
path字符串代表文件的绝对路径,接下来的参数代表表传递过去的argv[0],argv[1]…..,最后一个必须是NULL结束
用于被执行的c代码:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
for(i = 1; i < argc; i++)
{
printf("%s\n", argv[i]);
}
return 0;
}
接着要编译生成可执行二
接着要编译生成可执行二进制文件;
测试代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid = vfork();
if(-1 == pid)
{
perror("vfork");
exit(1);
} execl
else if(0 == pid)
{
execl("/mnt/hgfs/allshare/163/jin程0815/test", "./test", "hello", "world", "123", NULL);
}
else
{
printf("this is parent!\n");
}
return 0;
}
可以看出孤儿进程出现了,这是因为在vfork中子进程遇到execl( ),父进程就不用等子进程结束,遇到execl父进程也开始运行了,至于父和子谁先输出,这就看概率了。
接下来的execv( )函数跟execl( )差不多,下面来看代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
char *argv[] = {"./test", "hello", "world", "123", NULL};
pid = vfork();
if(-1 == pid)
{
perror("vfork");
exit(1);
}
else if(0 == pid)
{
execv("/mnt/hgfs/allshare/163/jin程0815/test", argv);
}
else
{
printf("this is parent!\n");
}
return 0;
}
为了防止刚才那种情况出现,所以这里介绍另一个函数wait,
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait (int * status)
功能:阻塞该进程,直到其某个子进程退出。
测试代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid = fork();
if(-1 == pid)
{
perror("fork");
}
else if(0 == pid)
{
sleep(2);
printf("this is a childprocess !\n");
}
else
{
int status;
printf("this is a parentprocess !\n");
wait(&status);
if(WIFEXITED(status))
{
printf("child process exit normally %d\n", WEXITSTATUS(status));
}
}
return 0;
}
wait是任意一个进程结束父进程就结束,
所以接下来升级一下:waitpid
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid (pid_t pid, int * status, int options)
功能:
会暂时停止目前进程的执行,直到有信号来到或子进程结束
options一般为0;
代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork(); //创建子进程
if (-1 == pid) //失败返回-1
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程
{
sleep(1);
printf("This is ChildProcess pid!\n");
exit(100);
}
else //父进程
{
int status;
printf("This is ParentProcess!\n");
//wait(&status); //1、等待子进程结束(阻塞) 2、回收子进程资源
waitpid(pid, &status, 0);
if (WIFEXITED(status)) //判断子进程是否正常退出
{
printf("Child Exit Normally! %d\n", WEXITSTATUS(status)); //获取子进程退出状态
}
}
return 0;
}
结果跟上面一样。
下面介绍kill, 杀死进程(实际上是传递信号)
首先随便写一个死循环,获取它的PID:
#include <stdio.h>
int main()
{
printf("%d\n", getpid());
while(1)
{
;
}
return 0;
}
实现kill函数代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
int main()
{
int id;
scanf("%d", &id);
kill(id, 9);
return 0;
}
这时看一下死循环进程那边:
死翘翘咯!
其实还有更简单的杀死进程的方法,那就是raise 函数:
用法贼简单:
#include <stdio.h>
int main()
{
printf("%d\n", getpid());
raise(9);
while(1)
{
;
}
return 0;
}
这算不算是自杀
最后介绍一下signal函数:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
他的意思就是当遇到信号signum,就调用函数 handler
这里明确一点:我目前说的信号 1,2 ,3 啊之类的不是数字1,2,3,是kill的选项:
如下:
Ctrl + c 对于进程来讲就是这里面的2;
Ctrl + z 就是9;
下面来看代码:
#include <stdio.h>
#include <signal.h>
void print(int num)
{
printf("helloworld!\n");
}
int main()
{
signal(2, print);
while(1);
return 0;
}
运行程序后我来个ctrl + c 就是信号2 就输出一个helloworld
输入口令 kill -2 26652 【注】:26652是signal的PID
另一边为:
signal后面不调用函数换成SIG_IGN就能忽略信号
#include <stdio.h>
#include <signal.h>
int main()
{
signal(2, SIG_IGN);
while(1);
return 0;
}
这时候我按ctrl + c是没用的:
这时候我按ctrl + z 即为信号9 才能将这程序结束掉:
那么问题来了 我能不能忽略信号9呢?(ctrl + z 就是信号9)
代码实践一下咯!
#include <stdio.h>
#include <signal.h>
int main()
{
signal(9, SIG_IGN);
while(1);
return 0;
}
这时我按下ctrl + z,猜猜看结果怎么样:
进程还是gg了,所以我们可以看出信号9 是不能被忽略的,他想杀谁谁也逃不掉,哈哈哈!
对了信号19也不能忽略,可以理解为这两个信号级别最高。