进程基础
来源:互联网 发布:幼儿园美工室制度 编辑:程序博客网 时间:2024/06/10 16:54
学习笔记,小白可以相互学习,大佬看到能告诉咱理解不对的地方就好了。
1.进程相关概念
进程是一个独立的可调读的任务
进程是一个程序执行一次的过程
进程是程序执行和资源管理的最小单位
进程和程序的区别
程序是静态的,它是保存在磁盘上的指令的有序集合,没有任何执行的概念
进程是一动态的概念,它是程序执行的过程,包括创建,调度和消亡。
linux中的进程包含了1.2.3.,程序只有1.2.
进程1.正文段程序1.2.3.2.用户数据段1.2. 3.系统数据段 进程不仅仅包含了程序的指令和数据,而且包含了程序计数器值,CPU的所有寄存器值记忆存储临时数据的进程堆栈。
2.Linux下的进程
2.1结构
主要的进程标识:进程号,(getpid()得到进程号) 父进程号,(getppid()得到父进程号)
PID唯一地标识一个进程
2.2进程类型
交互类型:该类进程由shell控制和运行。交互进程可以在前台或者后台运行。
批处理进程:该类进程不属于某个终端,他被提交到一个队列中以便顺序执行。
守护进程:该类进程在后台运行,他一般在Linux启动时开始运行,系统关闭才结束。
2.3进程运行状态
运行态(R态):此时进程正在运行,或者准备运行。
等待态:此时程序在等待一个事件的发生或某种系统资源。
可中断(S)
不可中断(D)
停止态:此时进程被中止(T)。
死亡态:这是一个已终止的进程,但还在进程向量数组中占有一个task_struct结构
3.调度进程
ps:查看系统中的进程(ps -elf查看优先级)
top:动态显示系统中的进程
nice:按用户指定的优先级运行进程(数字越小,优先级越高,例如nice -5 ./a.out)
renice:改变正在运行进程的优先级
kill:结束进程(包含后台进程)。(kill - l查看kill的信号,9号信号和2号信号都是结束信号)
bg:将挂起的进程在后台执行(ctrl + Z可以把一个进程挂起,bg + 作业号)
fg:把后台运行的进程放到前台运行(fg + 作业号)
4.函数
4.1fork()进程创建
头文件#include<sys/types.h>
#include<unistd.h>函数原型pid_t fork(void);函数返回值0:子进程
子进程PID:父进程
-1:出错
fork()函数用于创建一个子进程,子进程是几乎拷贝了父进程的全部内容。
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <unistd.h>int main(){printf("nihao\n");pid_t pid;if (0 > (pid = fork())) {perror("fork");return -1;}else if (pid == 0) { //子进程printf("child: pid = %d, ppid = %d\n", getpid(), getppid());}else { //父进程//sleep(1);printf("parent: child_pid = %d, pid = %d\n", pid, getpid());}printf("hello world!\n");return 0;}
4.2 exec函数族
exec函数族提供了一种在进程中启动另外一个程序执行的方法。他可以根据指定的文件名或者目录名找到可执行文件,并用它来取代原调用进程数据段,代码段和堆栈段。在执行完后,原调用进程的内容除了进程号,其他全被替换。
可执行文件可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
头文件#include<unistd.h>原型int execv(const char *path,const *arg, argv[]); int execle(const char *path,const *arg,..., char *const envp[]); int execve(const char *path,char *const argv[],char *const envp ); int execl(const char *path,const *arg, ...); int execlp(const char *file,const *arg, ...); int execvp(const char *file,char *const argv[]);返回值-1;出错
p:系统会自动从环境变量“#PATH”所包含的路径中进行查找。(p为path,代表shell搜索路径)
l: list便是逐个列举的方式
v: vertor表示将所有参数构造成指针数组传递
e: 在envp[]中传递当前进程所使用的环境变量
例子:
execl:
#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(){ pid_t pid; if(0 > (pid = fork())) { perror("fork"); return -1;; } else if( pid == 0 ) { if( 0 > execl("/bin/ls","ls","-l",NULL))//第一个参数是文件路径 //if( 0 > ("./a.out","./a.out",NULL)) { perror("excel"); return -1; } } else { printf("hello\n"); } return 0;}
execv:
#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(){ char *arg[] = {"ls","-l",NULL}; pid_t pid; if(0 > (pid = fork())) { perror("fork"); return -1;; } else if( pid == 0 ) { if( 0 > execv("/bin/ls",arg))//就是把execl中命令的参数写入了数组arg //if( 0 > ("./a.out","./a.out",NULL)) { perror("excev"); return -1; } } else { printf("hello\n"); } return 0;}
execle:
#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(int argc,char *argv[],char *envp[])//main函数的第三个参数{ pid_t pid; if(0 > (pid = fork())) { perror("fork"); return -1;; } else if( pid == 0 ) { if( 0 > execle("/usr/bin/env","env",NULL,envp))//env命令查看当前系统环境,which查看文件路径,例如which 1.c { perror("excele"); return -1; } } else { printf("hello\n"); } return 0;}
execve:
#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(int argc,char *argv[],char *envp[]){ char *arg[] = {"env",NULL}; pid_t pid; if(0 > (pid = fork())) { perror("fork"); return -1;; } else if( pid == 0 ) { if( 0 > execve("/usr/bin/env",arg,envp)) { perror("exceve"); return -1; } } else { printf("hello\n"); } return 0;}
execlp:
#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(int argc,char *argv[]){ pid_t pid; if(0 > (pid = fork())) { perror("fork"); return -1;; } else if( pid == 0 ) { if( 0 > execlp("env","env",NULL))//第一个参数不用写全具体路径,只写文件名,他会自动搜索 { perror("excelp"); return -1; } } else { printf("hello\n"); } return 0;}
execvp:
#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(int argc,char *argv[]){ char *arg[] = {"env",NULL}; pid_t pid; if(0 > (pid = fork())) { perror("fork"); return -1;; } else if( pid == 0 ) { if( 0 > execvp("env",arg)) { perror("excevp"); return -1; } } else { printf("hello\n"); } return 0;}
4.3exit和_exit
头文件exit: #include<stdlib.h> _exit: #includ<unistd.h>原型exit: void exit(int status) _exit: void _exit(int status)函数传入值status是一个整型参数,可以利用这个参数传递进程结束时的状态。通常用0表示正常
结束:其他的数字表示出现了错误,进程非
正常结束。在实际编程中可用wait系统调用
接收子进程的返回值,进行相应处理。
exit和_exit的区别
_exit()函数作用最为简单,直接使进程终止运行,清除其使用的内存空间,并且销毁其在内核的各种数据结构。
exit()会把文件缓冲区的内容写回文件(就是刷新缓存)。
#include<stdlib.h>#include<unistd.h>int main(){printf("hello\n");//exit(0);//只会直接打印hello_exit(0);//会打印hello worldprintf("world\n");return 0;}
wait函数,调用该函数使进程阻塞,直到任意一个子进程结束或者是该进程接收到了一个信号为止。如果该进程没有子进程或者子进程已经结束,wait函数会立即返回。
waitpid函数,功能和wait函数类似。可以指定等待某个子进程结束以及等待的方式(非阻塞或者阻塞)。
#include<sys/wait.h>原型pid_t wait(int *status)参数status是一个整型指针,指向的对象用来保存子
进程退出时的状态。status若为空则忽略子进程
退出时的状态,不为空则表示子进程退出时的
状态。子进程结束的状态可用特定的宏测定,
WIFEXITED(status)判断子进程是否正常结束,
WEXIT(status)当子进程退出时,得到子进程的
退出值返回值成功:子进程的进程号
失败:-1
#include<sys/wait.h>原型pid_t waitpid(pid_t pid,int *status,int options)参数pidpid>0,只等待进程ID等于pid的子进程,不管其他子进程是否已经结束退出了,
只要指定的子进程还没结束,waitpid就会一直等下去。 pid=-1,等待任意一个子进程结束,此时和wait的作用一样 pid=0,等待其组ID等于调用进程的组ID的任意一子进程 pid<-1,等待其组ID等于pid的绝对值得任意一子进程参数status同wait参数optionsWNOHANG:若由pid指定的子进程并不立即可用,则waitpid不阻塞,此时返回0 WUNTRACED:若某实现支持作业控制,则由pid指定的任意一子进程状态已
暂停,且其状态自暂停以来还未报告过,则返回其状态。 0:同wait,阻塞父进程,等待子进程退出返回值正常:结束的子进程的进程号使用WNOHANG,且没有子进程结束时:0 调用出粗:-1
#include<stdio.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<stdlib.h>int main(){ pid_t pid; if(0 > (pid = fork())) { perror("fork"); return -1; } else if( 0 == pid) { printf("child pid = %d\n",getpid()); sleep(5); printf("child!\n"); } else { printf("parent\n"); pid = wait(NULL); if(0 > pid) { perror("wait"); exit(-1); } printf("wait :pid = %d\n",pid); } return 0;}
#include<stdio.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<stdlib.h>int main(){ int status; pid_t pid; if(0 > (pid = fork())) { perror("fork"); return -1; } else if( 0 == pid) { printf("child pid = %d\n",getpid()); sleep(5); printf("child!\n"); exit(5); } else { printf("parent\n"); pid = wait(&status);//得到子进程的结束状态,作用与前面一样pid = waitpid(-1,NULL,0) if(0 > pid) { perror("wait"); exit(-1); } printf("wait :pid = %d\n",pid); if(WIFEXITED(status))//判断子进程是否正常结束 { printf("child process exit normol\n"); printf("child exit:%d\n",WEXITSTATUS(status));//当子进程正常退出时,得到子进程的退出值 } else { printf("child process exit unnormol\n"); } } return 0;}
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <strings.h>#include <sys/types.h>#include <sys/wait.h>int main(){int status;pid_t pid;if (0 > (pid = fork())) {perror("fork");exit(-1);}else if (pid == 0) { //子进程printf("child: pid = %d\n", getpid());sleep(5);printf("child\n");exit(0);}else { //父进程printf("parent\n");pid = waitpid(-1, NULL, WNOHANG);if (pid < 0) {perror("wait");exit(-1);}else if (pid > 0)printf("waitpid: pid = %d\n", pid);else printf("No child process exit\n");}return 0;}
5. 守护进程
守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。守护进程常常在系统启动时开始运行,在系统关闭时终止。
在Linux中,每一个系统与用户进行交流的界面称为终端。从终端开始运行的进程都会依附于这个终端,当控制终端被关闭时,相应的进程也会被自动关闭,
但是守护进程可以突破这种限制。如果想让某个进程不受终端变化的影响,必须把它变成守护进程。
ps -axj:查看系统中的守护进程
ps -ef | grep ./a.out(进程名):可查看特定进程的信息,可用kill命令结束守护进程
Linux守护进程编写步骤:
- 创建子进程,父进程退出(第一步完成,子进程就在形式上做到了与控制终端的脱离)
- 在子进程中创建新会话
- 改变当前目录为根目录
- 重设文件权限掩码
- 关闭文件描述符
#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/stat.h>#include<sys/types.h>#include<fcntl.h>#include<string.h>#include<time.h>void init_daemon()//创建守护进程{ /**1.创建子进程,父进程退出******************/ pid_t pid; if(0 > (pid = fork())) { perror("fork"); exit(-1); } else if(pid > 0) { exit(0); } /***2.在子进程中创建会话********************/ if(0 > setsid()) { perror("setsid"); exit(-1); } /***3.改变当前目录为根目录********************/ if( 0 > chdir("/tmp")) { perror("chdir"); exit(-1); } /****4.重设文件掩码***********************/ umask(0); /****5.关闭文件描述符**********************/ int num = getdtablesize(); int fd; for(fd = 0; fd < num; fd++) { close(fd); }}int main(int argc,char *argv[]){ init_daemon(); /**创建一个文件,每隔一秒向其中写入当前时间加行号****/ int fd; if( 0 > (fd = open(argv[1],O_WRONLY | O_CREAT | O_APPEND,0666))) { perror("open"); exit (-1); } write(fd,"hello world\n",sizeof("hello world")); int l = 0,ret; char a[100]; time_t t; struct tm *tm; while(1) { char buf[100]; time(&t); tm = localtime(&t); sprintf(buf,"%2d, %4d-%2d-%2d %2d:%2d:%2d\n",++l,tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); write(fd,buf,24); sleep(1); } close(fd); /* int l = 0; char buf[100]; FILE *fp = fopen(argv[1],"ab+"); if(NULL == fp) { perror("fopen"); exit (-1); } while(NULL != fgets(buf,sizeof(buf),fp)) { if(buf[strlen(buf)-1] == '\n') { l++; } } time_t t; struct tm *tm; while(1) { time(&t); tm = localtime(&t); sprintf(buf,"%2d, %4d-%2d-%2d %2d:%2d:%2d\n",++l,tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); fputs(buf,fp); fflush(fp); sleep(1); } fclose(fp); */ return 0;}
- 进程基础
- 进程基础
- 进程基础
- 进程基础
- 进程基础
- APUE读书笔记--进程基础
- Linux守护进程基础
- Linux进程基础
- perl 多进程基础
- Linux进程基础
- Linux进程基础
- linux进程控制基础
- Linux进程基础
- 【Linux基础】进程结构
- 【Linux基础】进程属性
- 【Linux基础】进程管理
- 【Linux基础】进程模式
- Linux进程基础
- 阿里云ecs更换系统盘(公用镜像-lnmp环境)
- 前端vue怎么实现文件上传
- hdu 6070 Dirt Ratio
- mysql详讲
- Linux-视频监控系统(8)-项目小结
- 进程基础
- 格式化字符串
- HDF5配置
- 数据连接池的工作机制
- python文件打开方式详解——a、a+、r+、w+区别
- 【Android API】理解Window和WindowManager
- ajax 调试 方法
- HttpURLConnection 请求网络
- 请将下列构造函数补充完整,使得程序的运行结果是5