Linux进程基础
进程指正在运行的程序,是资源分配的最小单位,可以通过“ ps " 或 "top"等命令查看正在运行的程序。线程是系统的最小调度单位,一个进程可以拥有多个线程,同一进程里的线程可以共享此进程的同一资源。
每个进程都有一个唯一的标识符,基进程ID,简称pid
进程间的通信的几种方法:
-
管道通信:有名管道 无名管道
-
信号通信: 信号的发送 信号的接受 信号的处理
-
IPC通信: 共享内存 消息队列 信号灯
-
Socket 通信
进程的三种基本状态以及转换:
1.创建进程
所有的进程都是由其他进程创建(除了pid为0号的idle进程),pid号为1的init进程是系统启动后运行的第一个进程,是所有进程的父进程,init进程会初始化一部分系统服务,创建其他进程。
创建新进程的那个进程成为父进程,新进程称为子进程,父进程和子进程拥有相同的代码段数据段,有各自独立的地址空间。采用写时拷贝技术。
创建进程常用函数定义如下:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid = fork();
if(pid < 0)
{
printf("fork is error\n");
return -1;
}
if(pid > 0)
{
printf("This is parent process,parent pid is %d\n", getpid());
}
if(pid == 0)
{
printf("This is clild process,child pid is %d,parent pid is %d\n", getpid(), getppid());
}
return 0;
}
2.exec函数族
用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序,该子进程被新的程序替换,改变地址空间,进程映像和一些属性,但pid号不变。
以下函数都是根据 execve 实现:
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
3.ps和kill命令
ps 命令:ps命令可以列出系统中当前运行的那些进程
命令格式: ps[参数]
命令功能:用来显示当前进程的状态
常用参数: aux
kill 命令:kill命令用来杀死进程
举例: kill -9(SIGKILL) PID
进程的状态:
D: 无法中断的休眠状态 (通常 IO 的进程)
R: 正在执行中
S: 静止状态
T: 暂停执行
Z: 不存在但暂时无法消除
W: 没有足够的记忆体分页可分配
<: 高优先序的行程
N: 低优先序的行程
L: 有记忆体分页分配并锁在记忆体内 (实时系统或捱 A I/O)
4.孤儿进程和僵尸进程
孤儿进程: 父进程结束以后,子进程还未结束,这个子进程就叫做孤儿进程。
僵尸进程:子进程结束以后,父进程还在运行,但是父进程不去释放进程控制块,这个进程就叫做僵尸进程。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int i = 0;
pid_t pid;
pid = fork();
if(pid < 0)
{
printf("fork is error \n");
return -1;
}
if(pid > 0)
{
printf("pid is %d\n", getpid());
}
if(pid == 0)
{
sleep(2);
printf("parent pid is %d\n", getppid());
}
return 0;
}
如上图所示,子进程中打印的父进程的进程号和父进程的进程号是不一样的,说明创建的子进程变成了孤儿进程,此进程被系统的int 进程 “领养”了。
lmg 1479 0.0 0.0 48392 4964 ? Ss 1月11 0:00 /sbin/upstart --user
lmg 10431 0.0 0.0 15984 1012 pts/20 S+ 17:08 0:00 grep --color=auto 1479
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
int i=0;
pid_t pid;
// 创建一个子进程
pid = fork();
if (pid < 0)
{
printf("fork is error \n");
return -1;
}
if (pid > 0)
{
while(1);
} //子进程,让子进程先结束
if (pid == 0)
{
printf("This is child\n");
exit(0);
}
return 0;
}
5. 守护进程
守护进程(daemon)是一类在后台运行的特殊进程, 用于执行特定的系统任务。 很多守护进程在系统引导的时候启动, 并且一直运行直到系统关闭。 另一些只在需要的时候才启动, 完成任务后就自动结束。
创建守护进程
1.使用 fork 函数创建一个新的进程, 然后让父进程使用 exit 函数直接退出(必须要的)
2.调用 setsid 函数。 (必须要的)
3.调用 chdir 函数, 将当前的工作目录改成根目录, 增强程序的健壮性。 (不是必须要的)
4.重设我们 umask 文件掩码, 增强程序的健壮性和灵活性(不是必须要的)
5.关闭文件描述符, 节省资源(不是必须要的)
6.执行我们需要执行的代码(必须要的)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(void)
{
pid_t pid;
// 步骤一: 创建一个新的进程
pid = fork();
//父进程直接退出
if (pid > 0)
{
exit(0);
}
if (pid == 0)
{
// 步骤二: 调用 setsid 函数摆脱控制终端
setsid();
// 步骤三: 更改工作目录
chdir("/");
// 步骤四: 重新设置 umask 文件源码
umask(0);
// 步骤五: 0 1 2 三个文件描述符
close(1);
close(2);
close(3);
// 步骤六: 执行我们要执行的代码
while (1)
{
}
}
return 0;
}