1、 进程的基本概念
进程的分类
交互进程
批处理进程
守护进程:一般在后台运行,一般由操作系统在开机时通过脚本自动激活启动或由超级管理用户root来启动进程的属性
进程ID:进程的唯一数值,用来区分进程
启动进程的用户ID(UID)和归属的组(GID)
进程状态:运行(R)、休眠(S)、僵尸(Z)
进程执行优先级
进程所连接的终端名
进程资源占用进程监视工具:ps
一般用法:ps -aux|more //用管道和more命令连接起来分页查看
其他用法:ps aux|grep 程序名 //提取指定程序的进程查询进程工具:pgrep
ps 参数选项 程序名
常用参数如下:
-1: 列出程序名和进程ID
-o: 进程起始的ID
-n: 进程终止的IDLinux进程的三态
就绪状态:若进程已被分配到除CPU以外所有必要的资源,只要获得处理器便可立即执行
执行状态:进程已获得处理器,其进程正在处理器上执行
阻塞状态:当正在执行的进程,由于等待某个事件发生而无法执行时,便处于阻塞状态Linux进程调度算法
FCFS算法:也叫FIFO算法,先来先处理
时间轮片算法:对FIFO算法的改进,周期性的进行进程切换
STCF算法:短任务优先算法,核心是所有程序都有一个优先级,短任务的优先级比长任务的高,OS总是安排优先级高的进程先运行
2、 进程创建
2.1 getgid函数和getpid()函数
函数详解
表头文件#include <unistd.h>#include <sys/types.h>定义函数gid_t getgid(void)函数说明用来获得执行目前进程的组识别码返回值组识别码(进程的ID)pid_t getpid(void)
获取当前进程ID
2.2 getppid函数
函数详解
表头文件#include <unistd.h>定义函数pid_t getppid(void)函数说明获得目前进程的父进程识别码返回值目前进程的父进程识别码
综合案例
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main(int argc,char *argv[])
{printf("My parent 'pid=%d'\n",getppid());printf("My 'pid=%d'\n",getpid());return 0;
}
运行结果
linux@ubuntu:~/test$ gcc get_dome.c -o get_dome -Wall
linux@ubuntu:~/test$ ./get_dome
My parent 'pid=14743'
My 'pid=14846'
2.3 fork函数(创建子进程)
函数详解
表头函数#include <sys/types.h>#include <unistd.h>定义函数pid_t fork(void)函数说明创建子进程,子进程完全复制父进程的资源,复制出的子进程有自己的task_struct结构和PID,复制父进程其他所有的资源现在Linux中采用写时复制技术,即并不会立即复制,若后来确实发生了写入,父子进程的数据不一致,于是产生了复制动作,若直接调用exec执行另一个可执行文件,那将不会进行复制父子进程谁先运行不确定,进行是是同步的返回值在父进程中返回子进程号,在子进程中返回0,错误返回-1
综合案例
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc,char *argv[])
{pid_t child;int i = 0;/*创建子进程*/if((child = fork()) == -1){printf("fork ERRO\n");exit(1);}else if(child == 0){printf("my is child:%d\n",getpid());i = 18;printf("child i exit with %d\n",i);}else{printf("my is father:%d\n",getpid());i = 41;sleep(1);printf("father i exit with %d\n",i);}printf("father and child have %d\n",getpid());return 0;
}
运行结果
linux@ubuntu:~/test$ gcc fork_dome.c -o fork_dome -Wall
linux@ubuntu:~/test$ ./fork_dome
my is father:15057 //父进程执行打印,给i赋值,然后睡眠
my is child:15058 //子进程执行打印
child i exit with 18 //子进程给i赋值,打印
father and child have 15058 //子进程出了if判断后的打印
father i exit with 41 //父进程睡眠完了,虽然子进程给i另外赋值了,但fork创建的进程有自己独立的空间,所以子进程i的赋值不影响父进程
father and child have 15057
linux@ubuntu:~/test$
2.4 vfork函数
函数详解
表头函数#include <unistd.h>定义函数pid_t vfork(void)函数说明创建子进程,与fork不同的是,进程是调用进程的一个副本,共享父进程的地址空间,它会保证子进程先运行,子进程运行的时候会暂停父进程的执行,直到子进程调用子进程调用exec族,或者exit()结束,父进程才会继续执行返回值成功在父进程会返回新建立的子进程代码(PID),而在新建立的子进程中返回0。如果失败直接返回-1
综合案例
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc,char *argv[])
{pid_t child;int i = 0;/*创建子进程*/if((child = vfork()) == -1){printf("fork ERRO\n");exit(1);}else if(child == 0){printf("my is child:%d\n",getpid());i = 18;sleep(1);printf("child i exit with %d\n",i);exit(1);}else{printf("my is father:%d\n",getpid());sleep(2);printf("father i exit with %d\n",i); //i的值会因为子进程的中的改变而改变,在fork中会是0}printf("father and child have %d\n",getpid());return 0;
}
运行结果
linux@ubuntu:~/test$ gcc vfork_dome.c -o vfork_dome -Wall
linux@ubuntu:~/test$ ./vfork_dome
my is child:15154 //先执行子进程,父进程挂起
child i exit with 18
my is father:15153
father i exit with 18 //父进程会因为子进程改变变量而改变
father and child have 15153
2.5 启动进程:exec族
int execl(const char *path,const char *arg,...);
int execlp(const char *file,const char *arg,...);
int execle(const char *path,const char *arg,...,char *const envp[]);
int execv(const char *path,char * const argv[]);
int execvp(const char *file,char * const argv[]);
int execve(const char *path,char * const argv[],char *const envp[]); //真正意义上的系统调用,其他都是再次基础上封装
2.5.1 execl函数
函数详解
头文件#include <unistd.h>定义函数int execl(const char *path,const char *arg,...)函数说明path 函数所代表的文件路径 接下的代表执行该文件传送过去的参数返回值执行成功函数不会返回,执行失败则返回-1
2.5.2 execlp函数
函数详解
头文件#include <unistd.h>定义函数int execlp(const char *file,const char *arg,...)函数说明从PATH环境变量所指目录中查找符合参数file的文件名 接下来的代表执行该文件传送过去的参数返回值成功不会返回 执行失败则直接返回-1
综合案例
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main(int argc,char *argv[])
{pid_t child;if((child = fork()) < 0){printf("vfork error!\n");exit(-1);}if(child == 0){printf("my is child\n");if(-1 == execlp("./a","hello","nihao",NULL)){printf("execlp error!\n");}}else{printf("my is father,a.c comletion\n");}return 0;
}
运行结果
linux@ubuntu:~/test$ gcc a.c -o a -Wall
linux@ubuntu:~/test$ ./exec_dome
my is father,a.c comletion
my is child
linux@ubuntu:~/test$ 我是a程序,将打印传入的参数.
0:hello
1:nihao
2.5.3 execve函数(真正意义上的系统调用)
表头文件#include <unistd.h>定义函数int execve(const char *path,char * const argv[],char *const envp[])函数说明filename 文件路径包含文件名参数利用数组指针来传递给执行文件 末尾为(char*)0传递给执行文件新的环境变量,末尾为0返回值成功不会返回 执行失败则直接返回-1
2.5.4 execvp函数
表头文件#include <unistd.h>定义函数int execvp(const char *file,char * const argv[])函数说明从PATH环境变量所指目录中查找符合参数file的文件名argv数组 存储可执行文件执行时需要的参数返回值成功不会返回 执行失败则直接返回-1
2.6 system函数
函数详解
头文件#include <stdlib.h>定义函数int system(const char * string)函数说明用于执行shell命令system()会调用fork()产生子进程,由子进程调用/bin/sh-c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程在system()调用期间,SIGCHLD信号会被暂时搁置,SIGINT和SIGOUIT信号则会被忽略返回值当在调用/bin/sh时失败返回127,其他原因失败返回-1,若string为空指针,返回非零值
综合案例
#include <stdlib.h>int main(int argc,char *argv[])
{system("ls -al");return 0;
}
运行结果
linux@ubuntu:~/test$ gcc system_dome.c -o system_dome
linux@ubuntu:~/test$ ./system_dome
total 92
drwxrwxr-x 2 linux linux 4096 9月 27 16:09 .
drwxr-xr-x 32 linux linux 4096 9月 27 14:23 ..
-rwxrwxr-x 1 linux linux 7193 9月 27 14:22 a
-rw-rw-r-- 1 linux linux 197 9月 27 14:22 a.c
-rwxrwxr-x 1 linux linux 7273 9月 27 14:20 exec_dome
-rw-rw-r-- 1 linux linux 382 9月 27 14:23 exec_dome.c
-rwxrwxr-x 1 linux linux 7348 9月 27 13:23 fork_dome
-rw-rw-r-- 1 linux linux 543 9月 27 13:23 fork_dome.c
-rwxrwxr-x 1 linux linux 7241 9月 27 12:58 get_dome
-rw-rw-r-- 1 linux linux 188 9月 27 12:59 get_dome.c
-rwxrwxr-x 1 linux linux 7167 9月 27 16:09 system_dome
-rw-rw-r-- 1 linux linux 87 9月 27 16:09 system_dome.c
-rw------- 1 linux linux 12288 9月 27 16:09 .system_dome.c.swp
-rwxrwxr-x 1 linux linux 7350 9月 27 13:52 vfork_dome
-rw-rw-r-- 1 linux linux 557 9月 27 13:53 vfork_dome.c
2.7 wait函数和waitpid函数
函数详解
头文件#include <sys/types.h>#include <sys/wait.h>定义函数pid_t wait(int *status)函数说明会暂停目前进程的执行,直到有信号到来或子进程结束为止。status 返回子进程的结束状态值,可设置为NULL通过WEXITSTATUS(status)来获取结束值通过WIFEXITED(status) 判断是否为正常结束,正常结束为非0值返回值执行成功返回值子进程识别码(PID),错误返回-1
综合案例
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc,char *argv[])
{pid_t pid;int status,i;if(fork() == 0){printf("tis is the child process .pid=%d\n",getpid());exit(5);}else{sleep(1);printf("this is the parent process.wait for child...\n");pid=wait(&status);i = WEXITSTATUS(status);printf("child's pid=%d.eixt status=%d\n",pid,i);}return 0;
}
运行结果
linux@ubuntu:~/test$ gcc wait_dome.c -o wait_dome
linux@ubuntu:~/test$ ./wait_dome
tis is the child process .pid=24163
this is the parent process.wait for child...
child's pid=24163.eixt status=5
函数详解
头文件#include <sys/types.h>#include <sys/wait.h>定义函数pid_t waitpid(pid_t pid,int *status,int options)函数说明用于等待指定ID子进程中断或结束pid 欲等待的子进程识别码pid<-1:等待进程组识别码为pid绝对值的任何进程pid=-1:等待任何子进程,相当于wait函数pid=0: 等待进程组识别与目前进程相同的任何子进程pid>0: 等待任何子进程识别码为pid的子进程status 返回子进程的结束状态值options 为0或下面数值的OR运算(|)组合WNOHANG: 没有任何已经结束的子进程则马上返回,不予等待WUNTRACED: 如果子进程进入暂停执行状态则马上返回,但结束状态不予理会返回值执行成功返回子进程识别码(PID),错误返回-1
2.8 exit函数和_exit函数
表头文件#include <stdlib.h>定义函数void exit(int status)函数说明用于正常结束目前进程的执行,并把参数status返回给父进程而所有的缓冲区数据会自动写回并关闭未关闭的文件
返回值无
_exit同exit函数
不同点:传递SIGCHLD信号给父进程,不处理标准I/O缓冲区
3、 守护进程
3.1 守护进程的基本概念
进程组:一个或多个进程的集合,每个进程都有一个进程组ID,这个ID就是进程组长的ID
会话期:一个或多个进程组的集合,每个会话有唯一一个会话首进程,会话ID为会话进程ID
控制终端:每一个会话可以有一个单独的控制终端,与控制终端连接的会话首进程就是控制进程创建守护进程过程中用到的一个关键函数——setsid#include <unistd.h>pid_t setsid(void);用于创建一个新的会话期,实现以下效果:摆脱原会话的控制,该进程变成新会话期的首进程摆脱原进程组,称为一个新进程组的组长摆脱终端控制
3.2 创建守护进程的一般步骤
(1) 通过fork函数创建子进程,父进程通过exit函数退出
(2) 在子进程中调用setsid函数,创建新的会话
(3) 再次通过fork函数创建一个子进程并让父进程退出
(4) 在子进程中调用chdir函数,让根目录“/”称为子进程的工作目录
(5) 在子进程中调用umask函数,设置进程的文件权限掩码为0
(6) 在子进程中关闭任何不需要的文件描述符
(7) 守护进程退出处理(结束)
利用库函数deamon创建守护进程
#include <unistd.h>
int daemon(int nochdir,int noclose);
综合案例
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>static int flag = 1;
void create_deamon(); //守护进程创建函数
void handler(int); //接受信号结束句柄int main(int argc,char *argv[])
{time_t t;int fd;create_deamon();struct sigaction act;act.sa_handler = handler;sigemptyset(&act.sa_mask);act.sa_flags = 0;if(sigaction(SIGQUIT,&act,NULL)){printf("sigaction error.\n");exit(0);}while(flag){fd = open("/home/linux/test/dae.log",O_WRONLY|O_CREAT|O_APPEND,0644);if(fd == -1){printf("open error\n");}t = time(0);char *buf = asctime(localtime(&t));write(fd,buf,strlen(buf));close(fd);sleep(10);}return 0;
}void handler(int sig)
{printf("I got a signal %d\nI'm quitting.\n",sig);flag = 0;
}void create_deamon(){ //守护进程创建函数pid_t pid;pid = fork();if(pid == -1){printf("fork error\n");exit(1);}else if(pid){exit(0);}if(-1 == setsid()){printf("setsid error\n");exit(1);}pid = fork();if(pid == -1){printf("fork error\n");exit(1);}else if(pid){exit(0);}chdir("/");int i;for(i = 0;i < 3;++i){close(i);}umask(0);return;
}
运行结果
linux@ubuntu:~/test$ gcc deamon_dome.c -o deamon_dome -Wall
linux@ubuntu:~/test$ ./deamon_dome
linux@ubuntu:~/test$ cat dae.log
Sun Sep 29 13:22:50 2024
Sun Sep 29 13:23:00 2024
Sun Sep 29 13:23:10 2024
linux@ubuntu:~/test$ ps -ef|grep 'deamon_dome'
linux 24622 1 0 13:22 ? 00:00:00 ./deamon_dome