多任务的两种形态
进程与线程的区别
进程之间是相互隔离的,而线程是共享进程的空间
进程包含线程,线程是进程的最小的执行单位,每一个进程中至少包含一个线程(主线程)
从操作系统角度看,进程是资源分配的最小单位;线程是调度(执行)的基本单位(就好比进程是一个地区批的地,开成了一个店,这个就是进程,而线程就是在这个店里面工作的人员,必须要有一个运行的人员便是主线程)
进程有独立的地址空间,线程共享进程的内存地址空间
Linux下的进程管理
Linux系统是一个多进程的系统,进程之间具有并行性、互不干扰的特点。
linux中进程包含PCB(进程控制块)、程序以及程序所操纵的数据结构集,可分为“代码段”、“数据段”和“堆栈段”。
进程状态
运行状态R(TASK_RUNNING)
可中断睡眠状态S(TASK_INTERRUPTIBLE)
不可中断睡眠状态D(TASK_UNINTERRUPTIBLE)
暂停状态T(TASK_STOPPED或TASK_TRACED)
僵尸状态Z(TASK_ZOMBIE)
退出状态X(TASK_DEAD)
init进程
进程ID为1通常是init进程,在自举过程结束时由内核调用。 init进程绝不会终止。 它是一个普通的用户进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户特权运行。
获取进程标识
#include <sys/types.h> #include <unistd.h>
pid_t getpid(void); 返回:调用进程的进程I D
pid_t getppid(void); 返回:调用进程的父进程I D
uid_t getuid(void); 返回:调用进程的实际用户I D
uid_t geteuid(void); 返回:调用进程的有效用户I D
gid_t getpgid(void); 返回:调用进程的实际组I D
gid_t getegid(void); 返回:调用进程的有效组I D
fork系统调用
头文件: #include <sys/types.h> #include <unistd.h>
返回:子进程中为0,父进程中为子进程I D,出错为-1
该函数主要作用是在进程中创建新的线程,且创建的线程中的代码与主线程中的代码一致,但是若直接在其中的主线程创建好几个则会创建出来2的n次方个线程
孤儿进程
父进程先于子进程结束,该进程称为孤儿进程
托孤--父进程结束子进程交互XX进程托管
int pid = fork(); //创建子进程 fork(); 这样创建的话成2的n次方
if (pid < 0)
{
perror("pid error:");
}
else if (pid == 0) //以下代码为子进程逻辑
{
printf("子进程:pid = %d,ppid = %d\n", getpid(), getppid());
for (int i = 0; i < 10; i++)
{
printf("子进程运行中\n");
sleep(1);
}
printf("子进程运行结束\n");
}
else
{
/*int status = 0;
int cid = wait(&status);
printf("已结束的子进程id:%d\n", cid);*/
printf("父进程:pid = %d,ppid = %d\n", getpid(), getppid());
for (int i = 0; i < 5; i++)
{
printf("父进程运行中\n");
sleep(1);
}
printf("父进程运行结束\n");
}
僵尸进程
子进程先于父进程结束,但子进程资源没有被释放,依然被占用内存,从而产生内存泄露风险
int pid = fork(); //创建子进程 fork(); 这样创建的话成2的n次方
if (pid < 0)
{
perror("pid error:");
}
else if (pid == 0) //以下代码为子进程逻辑
{
printf("子进程:pid = %d,ppid = %d\n", getpid(), getppid());
for (int i = 0; i < 5; i++)
{
printf("子进程运行中\n");
sleep(1);
}
printf("子进程运行结束\n");
}
else
{
/*int status = 0;
int cid = wait(&status);
printf("已结束的子进程id:%d\n", cid);*/
printf("父进程:pid = %d,ppid = %d\n", getpid(), getppid());
for (int i = 0; i < 10; i++)
{
printf("父进程运行中\n");
sleep(1);
}
printf("父进程运行结束\n");
}
wait和waitpid函数
wait函数
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int * status) ; 两个函数返回:若成功则为子进程I D号,若出错则为-1. Status选项,为空时,代表任意状态结束的子进程,若不为空,则代表指定状态结束的子进程。
阻塞(如果其所有子进程都还在运行)。 带子进程的终止状态立即返回(如果一个子进程已终止,正等待父进程存取其终止状态)。 出错立即返回(如果它没有任何子进程)。
void test1()
{
int pid = fork(); //创建子进程 fork(); 这样创建的话成2的n次方
if (pid < 0)
{
perror("pid error:");
}
else if (pid == 0) //以下代码为子进程逻辑
{
printf("子进程:pid = %d,ppid = %d\n", getpid(), getppid());
for (int i = 0; i < 5; i++)
{
printf("子进程运行中\n");
sleep(1);
}
printf("子进程运行结束\n");
}
else
{
int status = 0;
int cid = wait(&status);
printf("已结束的子进程id:%d,%d\n", cid, WEXITSTATUS(status));
printf("父进程:pid = %d,ppid = %d\n", getpid(), getppid());
for (int i = 0; i < 10; i++)
{
printf("父进程运行中\n");
sleep(1);
}
printf("父进程运行结束\n");
}
}
该函数是先让子进程的内容运行完毕之后在运行主线程的内容
waitpid函数
pid_t waitpid(pid_t pid, int * status, int options) ;
对于waitpid的p i d参数的解释与其值有关:
pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。
pid > 0 等待其进程I D与p i d相等的子进程。
pid == 0 等待其组I D等于调用进程的组I D的任一子进程。
pid < -1 等待其组I D等于p i d的绝对值的任一子进程。
void test2()
{
//创建第一个子进程
int pid1 = fork();
if (pid1 < 0)
{
perror("pid error:");
}
else if (pid1 == 0)
{
for (int i = 0; i < 3; i++)
{
printf("子进程1正在运行\n");
sleep(1);
}
printf("子进程1运行结束\n");
}
else
{
//创建第二个子进程
int pid2 = fork();
if (pid2 == 0)
{
for (int i = 0; i < 5; i++)
{
printf("子进程2正在运行\n");
sleep(1);
}
printf("子进程2运行结束\n");
exit(123);
}
else
{
int status = 0;
while (1)
{
int cid = waitpid(pid2, &status, WNOHANG);
if (cid > 0)
{
printf("进程结束%d\n", WEXITSTATUS(status));
}
}
}
}
}
主线程和子线程可以同时运行,但是只检测一次因此可以放在循环当中检测