-
进程: 进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元。
(ctrl +z——》切到后台)
(fg ——》切到前台)
(ps - elf ——>查看运行的所有进程)
程序是放到磁盘的可执行文件
进程是指程序执行的实例
-
程序与进程的关系
(1)进程是动态的,程序是静态的:程序是有序代码的集合;进程是程序的执行。通常进程不可在计算机之间迁移;而程序通常对应着文件、静态和可以复制
(2)进程是暂时的,程序使长久的:进程是一个状态变化的过程,程序可长久保存
(3)进程与程序组成不同:进程的组成包括程序、数据和进程控制块(即进程状态信息)
(4)进程与程序的对应关系:通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序。 -
进程的生命周期
(1)创建: 每个进程都是由其父进程创建,进程可以创建子进程,子进程又可以创建子进程的子进程
(2)运行: 多个进程可以同时存在,进程间可以通信
(3)撤销: 进程可以被撤销,从而结束一个进程的运行
针对运行——》运行分为三个状态:
(1)执行状态:进程正在占用CPU——》例如正在运行的5ms
(2)就绪状态:进程已具备一切条件,正在等待分配CPU的处理时间片——》例如正在执行qq, 那么qq音乐就在就绪状态。
(3)等待状态:进程不能使用CPU,若等待事件发生则可将其唤醒——》scanf
执行状态——就绪状态 时间片
相互切换
-
Linux下的进程地址空间
Linux中的进程包含3个段,分别为“数据段”、“代码段”和“堆栈段”。
(1)“数据段”存放的是全局变量、常数以及动态数据分配的数据空间;
(2)“代码段”存放的是程序代码的数据。
(3)“堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。 -
进程ID
进程ID(PID):标识进程的唯一数字
父进程的ID(PPID)
启动进程的用户ID(UID) -
进程互斥
进程互斥是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止
8.临界资源
操作系统中将一次只允许一个进程访问的资源称为临界资源
9.临界区
进程中访问临界资源的那段程序代码称为临界区,为实现对临界资源的互斥访问,应保证诸进程互斥地进入各自的临界区
10.进程同步
一组并发进程按一定的顺序执行的过程称为进程间的同步
具有同步关系一组并发进程称为合作进程,
合作进程间互相发送的信号称为消息或事件
-
进程调度
概念:
按一定算法,从一组待运行的进程中选出一个来占有CPU运行。
调度方式:
• 抢占式
• 非抢占式 -
调度的算法
(1)先来先服务调度算法
(2)短进程优先调度算法
(3)高优先级优先调度算法
(4)时间片轮转法
第一:getpid(获取id号)(LINUX1——》lesson2——》getpid.c)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("%d\n", getpid());
printf("%d\n", getppid());
while (1);
return 0;
}
第二:fork(创建子进程)(LINUX1——》lesson2——》fork.c)
注意点: fork的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值:
返回值:
0: 子进程
子进程ID(大于0):父进程
-1: 出错
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid = fork();
if (-1 == pid) //错误处理
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程
{
printf("This is Child, pid is %d, Parent id is %d!\n", getpid(), getppid());
printf("Child : pid = %d\n", pid);
}
else //子进程和父进程运行顺序随机,子进程进程号比父进程进程号大一
{
printf("This is Parent, pid is %d!\n", getpid());
printf("Parent : pid : %d\n", pid);
}
printf("helloworld!\n");
return 0;
}
第三:count(验证父子进程)(LINUX1——》lesson2——》count.c)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
int count = 0;
pid = fork();
if (-1 == pid)
{
perror("fork");
}
else if (0 == pid)
{
count++;
printf("%d %p\n", count, &count);
}
else
{
count++;
printf("%d %p\n", count, &count);
}
return 0;
}
(1)运行完后, 父子进程中的count都是1
原因:子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享。(子进程复制父进程的地址空间)
v在子进程中对count进行加1的操作,并没有影响到父进程中的count值,父进程中的count值仍然为0
(2)写实拷贝: 只有在子进程写的时候才真正拷贝父进程的地址空间
(3)fork 之前打开的文件都是父进程的
fork ——》父进程打开一个文件, 子进程继承其文件描述符。
第四:vfork(LINUX1——》lesson2——》vfork.c)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid; //pid_t 代表int 型, 所有的_t,差不都都是。
int count = 0;
pid = vfork(); //子进程共享父进程的数据(堆栈、数据段), 运行完后count的值分别为1,2 这个与fork有区别
if (-1 == pid)
{
perror("vfork");
exit(1);
}
else if (0 == pid) //一定是子进程先运行,一定是子进程运行完毕,父进程再运行
{
count++;
printf("This is Child %d %d!\n", count, getpid());
//sleep(3);
exit(1); //vfork子进程要指定退出方式
}
else
{
count++;
printf("This is Parent %d %d!\n", count, getpid());
}
return 0;
}
第五:exec.c(函数族)(LINUX1——》lesson2——》exec.c)
注意:exec用被执行的程序替换调用它的程序。
区别:
fork创建一个新的进程,产生一个新的PID。
exec启动一个新程序,替换原有的进程,因此进程的PID不会改变
代码一:(test.c)
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
printf("test pid %d\n", getpid());
for (i = 1; i < argc; i++)
{
printf("%s\n", argv[i]);
}
return 0;
}
代码二:(exec.c)
#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);
}
else if (0 == pid) //子进程
{
printf(“Child pid %d\n”, getpid());
execl("/home/168/linux/process/test", “./test”, “aaaaa”, “bbbbb”, “cccc”, NULL); //完整路径,以空指针NULL结束
}
else
{
printf(“this is parent!\n”);
}
return 0
}
注意点:
(1)execl后,进程号相同,但是地址空间变了。
(2)调用execl 后, 子程序不用exit(1)退出了, execl 后面写的代码都没有用了,不执行了, 例如sleep(5)——》没有用。
第六:execv(数组形式)(LINUX1——》lesson2——》exec.c)
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
char *string[] = {"./test", “aaaaa”, “bbbbb”, “cccc”, NULL};
pid = vfork();
if (-1 == pid)
{
perror("vfork");
exit(1);
}
else if (0 == pid) //子进程
{
printf("Child pid %d\n", getpid());
//execl("/home/168/linux/process/test", "./test", "aaaaa", "bbbbb", "cccc", NULL);
execv("/home/168/linux/process/test", string);
}
else
{
printf("this is parent!\n");
}
return 0;
}
与上一个代码执行的效果一样
第七:wait(进程等待)(LINUX1——》lesson2——》wait.c)
作用:阻塞该进程,直到其某个子进程退出。
(1)父进程不仅需要创建子进程,还要回收子进程
孤儿进程:(父进程死了, 子进程没有死)
僵尸进程 :(子进程运行完死了, 但是父进程没有回收他)
wait(参数)——》&status作用
(1)等待子程序结束 :父进程一定在子进程结束后才能死
(2)回收子程序
(3)用到fork 的时候, 一定要用wait
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork();
if (-1 == pid)
{
perror("fork");
exit(1);
}
else if (0 == pid)
{
sleep(3);
printf("This is child!\n");
exit(200); //退出进程,正常退出
}
else
{
int status;
printf("This is parent!\n");
//wait(&status); //1、等待子进程结束 2、回收子进程
waitpid(pid, &status, 0); // 作用和上一行一样
if (WIFEXITED(status)) //判断子进程是否正常退出
{
printf("子进程正常退出 %d!\n", WEXITSTATUS(status)); //如果正常退出就获取子进程退出状态 不正常退出:比如sleep时间长一点,然后用kill杀死子程序, 就没有下面的printf了。
}
}
return 0;
}