Linux进程
进程是操作系统资源管理的最小单位。是一个动态的实体,是程序的一次执行过程。
进程是动态的,程序是静态的;
为了在同一时间内能执行更多的任务,进程内部又可划分了许多的线程。
线程在进程内部,是比进程更小的能独立运行的基本单位。线程基本上不拥有系统资源,它与同属于一个进程的其他线程共享进程拥有的全部资源。
linux下可通过命令ps或pstree查看当前系统的进程。
进程的标识
每个进程都是通过唯一 的进程ID标识的。非负数,每个进程除了进程ID外还有其它的标识符信息,都可以通过相应的函数获得。这些函数的声明在 unistd.h 头文件中。
获取进程各种标识符的函数
pid_t getpid(id) //获得进程ID
pid_t getppid(id) //获得进程父进程ID
pid_t getuid() //获得进程的实际用户ID
pid_t geteuid() //获得进程的有效用户ID
pid_t getgid() //获得进程的实际组ID
pid_t getegid(id) //获得进程的有效组ID
实际用户ID(uid):标识运行该进程的用户。
有效用户ID(euid):标识以什么用户身份来运行进程。如:普通用户A,运行了个程序,是以root身份来运行的,这个程序运行时就具有root权限。此时实际用户ID是A用户的ID,而有效用户ID是root用户ID。
实际组ID(gid):实际用户所属的组的组ID。
有效组ID(egid):有效组ID是有效用户所属的组的组ID。
linux进程状态
运行状态R(runnbale)
可中断等待状态S(sleeping)
不可中断等待状态D(uninterruptible sleep)
僵死状态Z(zombile)
停止状态T(traced or stopped)
<(高优先级进程),N(低优先级进程),L(内存锁页面,即页面不可以被换出内存),s(该进程为会话首进程),l(多线程进程),+(进程位于前台进程组)。
进程控制
主要的系统调用有
fork:用于创建一个新进程
exit:用于终止一个应用程序。
exec:用于执行一个应用程序。
wait:将父进程挂起,等待子进程终止。
getpid:获取当前进程的进程ID。
nice:改变进程的优先级。
进程的创建两种方式
一、由操作系统创建。平等的
二、由父进程创建。继承的
系统调用fork是创建一个新进程的唯一方法,除了极少数以特殊方式创建的进程。如:init
创建一个子进程之后,父进程和子进程争夺CPU,抢到的执行,另一个挂起等待。
如果想要父进程等待子进程执行完毕以后在继续执行,可以在fork操作之后调用wait或waitpid。
#inlcude<sys/types.h>
#inlcude<unistd.h>
pid_t fork(void);
fork函数调用一次返回两次,父进程返回值为新创建的子进程的进程ID 子进程I返回值为0,
fork.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
printf("Process Creation Study\n");
pid=fork();
switch(pid)
{
case 0:
printf("Child process is running ,Current Pid is %d,ParentPid is %d\n",pid,getppid());
break;
case -1:
perror("Process creation failed\n");
break;
default:
printf("Patent process is running , ChildPid is %d,Parentpid is %d\n",pid,getpid());
break;
}
exit(0);
}
fork2.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
char *msg;
int k;
printf("Process Creation Study\n");
pid=fork();
switch(pid)
{
case 0:
msg="Child process is running ";
k=3;
break;
case -1:
preeor("Process creation failed\n");
break;
default:
msg="Parent process is running ";
k=5;
break;
}
while(k>0)
{
puts(msg);
sleep(1);
k--;
}
exit(0);
}
孤儿进程
如果一个子进程的父进程先于子进程结束,子进程就成为一个孤儿进程,它有init进程收养,成为init进程的子进程。
fork3.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
pid=fork();
switch(pid)
{
case 0:
while(1)
{
printf("A background process,PID:%d\n,Parent ID:%d\n",getpid(),getppid());
sleep(3);
}
case -1:
perror("Process creation failed\n");
exit(-1);
default:
printf("I am parent process ,my pid is %d\n",getpid());
exit(0);
}
return 0;
}
vfork函数
也可以创建一个新进程,于fork的异同
1、都是调用一次返回两次。
2、使用fork创建一个子进程时,子进程只是完全复制父进程的资源。这样得到的子进程独立于父进程,有良好的并发性。而使用vfork创建的子进程时,操作系统并不将父进程的地址空间完全复制到子进程,用vfork创建的子进程共享父进程的地址空间,子进程的运行完全在父进程地址空间上。
子进程对该地址空间中任何数据的修改同样为父进程所见。
3、使用fork创建一个子进程是,是那个进程先运行取决于系统的调度算法。而vfork一个进程时,vfork保证子进程先运行,当它调用exec或exit之后,父进程才可能被调度运行。如果在调用exec或exit之前子进程要依赖父进程的某个行为,就会导致死锁。
创建守护进程
守护进程(daemon)指在后台运行的没有控制终端与之相连的进程。独立于控制终端,通常周期性的执行某种任务。linux中的大多数服务器就是用守护进程的方式实现的。在后台运行。
启动方式:
linux系统启动时从启动脚本/etc/rc.d中启动
有作业规划进程crond启动
由用户终端执行
daemon.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<sys/param.h>
#include<sys/stat.h>
#include<time.h>
#include<syslog.h>
int init_datmon(void)
{
int pid;
int i;
..................
.......
}
进程退出
(1)正常退出
在main函数中执行return。
调用exit函数。
调用_exit函数。
(2)异常退出
调用about函数。
进程收到某个信号,而该信号是程序终止。
不管那种退出方式,最终都会执行内核中的同一段代码。这段代码用来关闭进程所有已打开的文件描述符,释放它所有占用的内存和其他资源。
各种退出方式的比较:
a、exit和return的区别:exit是一个函数,有参数;而return是函数执行完后的返回。exit把控制权交给系统,而return将控制权交给调用函数。
b、exit和about的区别:exit是正常终止进程,而about是异常终止。
c、exit(int exit_code):exit中的参数exit_code为0代表进程正常终止,若为其他值表示程序执行过程中有错误发生
d、exit()和_exit()的区别:exit在头文件stdlib.h中声明,而_exit()声明在头文件unistd.h中
两个函数都能正常终止进程,但是_exit()会执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核。
进程是操作系统资源管理的最小单位。是一个动态的实体,是程序的一次执行过程。
进程是动态的,程序是静态的;
为了在同一时间内能执行更多的任务,进程内部又可划分了许多的线程。
线程在进程内部,是比进程更小的能独立运行的基本单位。线程基本上不拥有系统资源,它与同属于一个进程的其他线程共享进程拥有的全部资源。
linux下可通过命令ps或pstree查看当前系统的进程。
进程的标识
每个进程都是通过唯一 的进程ID标识的。非负数,每个进程除了进程ID外还有其它的标识符信息,都可以通过相应的函数获得。这些函数的声明在 unistd.h 头文件中。
获取进程各种标识符的函数
pid_t getpid(id) //获得进程ID
pid_t getppid(id) //获得进程父进程ID
pid_t getuid() //获得进程的实际用户ID
pid_t geteuid() //获得进程的有效用户ID
pid_t getgid() //获得进程的实际组ID
pid_t getegid(id) //获得进程的有效组ID
实际用户ID(uid):标识运行该进程的用户。
有效用户ID(euid):标识以什么用户身份来运行进程。如:普通用户A,运行了个程序,是以root身份来运行的,这个程序运行时就具有root权限。此时实际用户ID是A用户的ID,而有效用户ID是root用户ID。
实际组ID(gid):实际用户所属的组的组ID。
有效组ID(egid):有效组ID是有效用户所属的组的组ID。
linux进程状态
运行状态R(runnbale)
可中断等待状态S(sleeping)
不可中断等待状态D(uninterruptible sleep)
僵死状态Z(zombile)
停止状态T(traced or stopped)
<(高优先级进程),N(低优先级进程),L(内存锁页面,即页面不可以被换出内存),s(该进程为会话首进程),l(多线程进程),+(进程位于前台进程组)。
进程控制
主要的系统调用有
fork:用于创建一个新进程
exit:用于终止一个应用程序。
exec:用于执行一个应用程序。
wait:将父进程挂起,等待子进程终止。
getpid:获取当前进程的进程ID。
nice:改变进程的优先级。
进程的创建两种方式
一、由操作系统创建。平等的
二、由父进程创建。继承的
系统调用fork是创建一个新进程的唯一方法,除了极少数以特殊方式创建的进程。如:init
创建一个子进程之后,父进程和子进程争夺CPU,抢到的执行,另一个挂起等待。
如果想要父进程等待子进程执行完毕以后在继续执行,可以在fork操作之后调用wait或waitpid。
#inlcude<sys/types.h>
#inlcude<unistd.h>
pid_t fork(void);
fork函数调用一次返回两次,父进程返回值为新创建的子进程的进程ID 子进程I返回值为0,
fork.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
printf("Process Creation Study\n");
pid=fork();
switch(pid)
{
case 0:
printf("Child process is running ,Current Pid is %d,ParentPid is %d\n",pid,getppid());
break;
case -1:
perror("Process creation failed\n");
break;
default:
printf("Patent process is running , ChildPid is %d,Parentpid is %d\n",pid,getpid());
break;
}
exit(0);
}
fork2.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
char *msg;
int k;
printf("Process Creation Study\n");
pid=fork();
switch(pid)
{
case 0:
msg="Child process is running ";
k=3;
break;
case -1:
preeor("Process creation failed\n");
break;
default:
msg="Parent process is running ";
k=5;
break;
}
while(k>0)
{
puts(msg);
sleep(1);
k--;
}
exit(0);
}
孤儿进程
如果一个子进程的父进程先于子进程结束,子进程就成为一个孤儿进程,它有init进程收养,成为init进程的子进程。
fork3.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
pid=fork();
switch(pid)
{
case 0:
while(1)
{
printf("A background process,PID:%d\n,Parent ID:%d\n",getpid(),getppid());
sleep(3);
}
case -1:
perror("Process creation failed\n");
exit(-1);
default:
printf("I am parent process ,my pid is %d\n",getpid());
exit(0);
}
return 0;
}
vfork函数
也可以创建一个新进程,于fork的异同
1、都是调用一次返回两次。
2、使用fork创建一个子进程时,子进程只是完全复制父进程的资源。这样得到的子进程独立于父进程,有良好的并发性。而使用vfork创建的子进程时,操作系统并不将父进程的地址空间完全复制到子进程,用vfork创建的子进程共享父进程的地址空间,子进程的运行完全在父进程地址空间上。
子进程对该地址空间中任何数据的修改同样为父进程所见。
3、使用fork创建一个子进程是,是那个进程先运行取决于系统的调度算法。而vfork一个进程时,vfork保证子进程先运行,当它调用exec或exit之后,父进程才可能被调度运行。如果在调用exec或exit之前子进程要依赖父进程的某个行为,就会导致死锁。
创建守护进程
守护进程(daemon)指在后台运行的没有控制终端与之相连的进程。独立于控制终端,通常周期性的执行某种任务。linux中的大多数服务器就是用守护进程的方式实现的。在后台运行。
启动方式:
linux系统启动时从启动脚本/etc/rc.d中启动
有作业规划进程crond启动
由用户终端执行
daemon.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
#include<sys/param.h>
#include<sys/stat.h>
#include<time.h>
#include<syslog.h>
int init_datmon(void)
{
int pid;
int i;
..................
.......
}
进程退出
(1)正常退出
在main函数中执行return。
调用exit函数。
调用_exit函数。
(2)异常退出
调用about函数。
进程收到某个信号,而该信号是程序终止。
不管那种退出方式,最终都会执行内核中的同一段代码。这段代码用来关闭进程所有已打开的文件描述符,释放它所有占用的内存和其他资源。
各种退出方式的比较:
a、exit和return的区别:exit是一个函数,有参数;而return是函数执行完后的返回。exit把控制权交给系统,而return将控制权交给调用函数。
b、exit和about的区别:exit是正常终止进程,而about是异常终止。
c、exit(int exit_code):exit中的参数exit_code为0代表进程正常终止,若为其他值表示程序执行过程中有错误发生
d、exit()和_exit()的区别:exit在头文件stdlib.h中声明,而_exit()声明在头文件unistd.h中
两个函数都能正常终止进程,但是_exit()会执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核。