博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux进程
阅读量:35194 次
发布时间:2020-01-31

本文共 6179 字,大约阅读时间需要 20 分钟。

atexit函数多次注册的时候先注册的后执行,就像压栈。
用_exit终止进程是并不执行atexit注册的进程终止处理函数。
进程中的虚拟地址空间:
进程隔离;多进程同时运行
什么进程:
1、进程是动态过程,不是静态实物
进程就是程序一次运行过程,所有进程都有生命周期,
2、进程控制块PCB(process control block),就是内核中用来管理进程的数据结构。
PID(process ID):唯一的表示进程
API函数:getpid、getppid、getuid、geteuid、getgid、getegid
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
多进程调度:宏观并行,微观串行
创建子进程:fork
#include <unistd.h>
pid_t fork(void);
fork函数调用一次会返回两次,返回值等于0的就是我们就是的子进程,而返回大于0的就是父进程。
测试代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
pid_t p1 = -1;
p1 = fork();//会返回两次
if(p1 == 0)
{
printf("\n子进程pid = %d\n",getpid());//一定子进程
printf("在子进程里面,父进程的ID = %d\n",getppid());
}
if(p1 > 0)
{
printf("\n父进程pid = %d\n",getpid());//一定是父进程
printf("在父进程里面 p1 = %d\n",p1);
}
if(p1 < 0)
{
//fork出错
}
//printf("Fenton 子进程和父进程都运行了这段程序pid = %d\n",getpid());
return 0;
}
这里会出现在子进程中用getppid函数会出问题,因为在子进程运行的时候父进程已经死掉了,所有获取的有问题。
父子进程对文件的操作:在子进程和父进程里面对同一文件操作,对应fd的文件指针是关联的向O_APPEND之后的的样子。有时候看到的只有一个,有点想分别写,原因是程序台简短了;父进程和子进程独自打开一个文件,O_APPEND可以把父子进程独自打开的fd文件指针关联起来实现分别写。测试代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd1 = -1;
int fd2 = -1;
pid_t pid = -1;
pid = fork();
if(pid == 0)
{
fd1 = open("1.txt",O_RDWR | O_APPEND);
if(fd1 < 0)
{
perror("open");
return 0;
}
printf("在子进程里面,父进程的ID = %d\n",getppid());
write(fd1,"Fenton",5);
sleep(1);
}
if(pid > 0)
{
fd1 = open("1.txt",O_RDWR|O_APPEND);
if(fd1 < 0)
{
perror("open");
return 0;
}
printf("在父进程里面 pid = %d\n",pid);
write(fd1,"aaaaa",5);
sleep(1);
}
if(pid < 0)
{
//fork出错
}
close(fd1);
//close(fd2);
return 0;
}
父进程在没有fork之前自己多事情对子进程没有影响,但是父进程fork之后在自己的if做的事情对子进程就没有影响了,本质原因就是在fork内部复制了父进程的PCB生成了一个新的子进程,并且fork返回是子进程已经完全和父进程脱离并且独立被OS调度执行。
进程由fork开始,终究进程也会消亡。linux中malloc和open之后没有free和close,在进程结束之后会自动回收。但是回收的时候没有回收干净,只是回收工作的时候的内存和IO,但是没有会后进程本身的内存(8kb,也就是task_struct和栈内存),所以每一个进程都需要一个帮他回收的东西,这个东西就是父进程。
僵尸进程:
1、子进程先于父进程结束,子进程结束,父进程并不一定理解帮子进程“收尸”,在这段之间,就成为僵尸进程。
2、子进程除task_struct和栈外其余内存空间已清理
3、父进程可以使用wait或waitpid以显式回收子进程的剩余待回收内存资源并且获取子进程退出状态。
4、父进程也可以不适用wait或者waitpid回收子进程,此时父进程结束时一样回收进程(为了防止父进程忘记显式调用wait来收回内存而造成的内存泄漏)
孤儿进程:
1、父进程先于子进程结束,就变成孤儿进程。
2、linux系统规定,所有孤儿进程都自动成为一个特殊进程(进程1,也就是init进程)的子进程。
wait函数:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
WIFEXITED用来判断子程序是否正常退出(return exit _exit)
WIFSIGNALED用来判断子进程是否非正常终止(被信号所终止)
WEXITSTATUS用来得到子程序正常终止的返回值(返回无符号数)
waitpid函数和wait一样,重点就是差别。
waitpid就是等待回收指定pid的进程,可以工作在阻塞和非阻塞两种模式。
ret = waitpid(-1, &status, 0);
-1表示不等待某个特定的PID,0表示默认的方式(阻塞)。ret表示返回值
ret = waitpid(pid, &status, 0);阻塞等待回收后PID为pid的这个子进程。
ret = waitpid(pid, &status, 1);非阻塞等待回收后PID为pid的这个子进程。只要一调用,就马上返回。只在返回值里面标志。
exec族函数:可以直接把一个编译好的可执行程序直接加载运行
所以父子程序都是单独编写编译。
#include <unistd.h>
extern char **environ;
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[]);
execl和execv函数值最基本的exec族函数,区别是传参的格式不同,execl是把参数列表(必须以NULL结束)一次排列而成,l就是list的缩写。execv是事先把参数列表放在一个字符串数组里面,然后再传参。
execlp和execvp这两个在上面基础上加了p,较上面上个说,上面两个执行需要指定可执行的全路径(execl没有找到path这个文件则会报错),而加了p可以是file(也可以也是path,只是兼容了file,加了p的首先去找file,找到则执行,如果没找到则回去环境变量PATH所指的目录下去找,找到执行,找不到就报错)。
execle和execvpe,这两个函数比较上面,函数原型中的参数列表也多了一个字符串数组envp形参,e就是envirment,区别就是执行可执行程序,会多传一个环境变量给执行程序。
测试代码如下:#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid = -1;
pid_t ret = -1;
int stat = -1;
pid = fork();
if(pid == 0)
{
//char *const arg[] = {"ls","-a","-l",NULL};
//execv("/bin/ls",arg);//执行ls命令
execl("./hello.o","aaa",NULL);
return 0;
}
else if(pid > 0)
{
printf("在父进程里面,子进程的pid = %d\n",pid);
}
else if(pid < 0)
{
//fork出错
return -1;
}
return 0;
}
execlp和execvp,不加了p的需要全路径+文件名字,找不到就报错,加了p就到PATH指定的路径下面去找文件。优先环境变量PATH中去找,找不到再来当前文件下面找。
execle和execvpe:main函数的原型不止是int main(int argc, char **argv),还可以是int main(int argc, char **argv, char **env),env就是就是给main函数传递的环境变量,第三个是字符串数组,内容是字符串数组,若是没有传递第三个参数,则会从父进程继承了一份环境变量(OS默认)。
进程的状态:就绪态、运行态、僵尸态、等待态、停止态。
system函数 = fork+exec
原子操作:整个操作一旦来时就会不被打断的执行完,原子操作的好处就是不会被打断(不会引来竞争状态);坏处是自己单独连续占中CPU时间太长,影响实时性,应该尽量避免不必要的原子操作,就算不得不使用原子操作,也应该尽量减少原子操作的时间。
进程之间的关系:
1、无关系
2、父子进程
3、进程组
4、会话
慢慢看守护进程
kill命令 用法 kill -信号编码 进程ID
kill -9 xxx
1、daemon就是守护进程的意思,简称d(进程名字后面带d的基本就是守护进程)
2、长期运行
3、与控制台脱离,不依赖于任何终端
4、服务器,服务器程序一般都实现为守护进程。
syslogd,系统日志守护进程,提供syslog功能
cron,用来实现时间管理的,在linux定时执行程序的功能就要用到。
测试代码如下:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
void cread_daemon(void);
int main(void)
{
cread_daemon();
while(1)
{
printf("Fenton\n");
sleep(1);
}
return 0;
}
//这个函数的作用就是把改进守护进程
void cread_daemon(void)
{
pid_t pid = -1;
pid = fork();
if(pid <0)
{
perror("fork");
exit(-1);
}
else if(pid > 0)
{
exit(0);//父进程直接退出
}
//这下面就是子进程的了
pid = setsid();
if(pid <0)
{
perror("setsid");
exit(-1);
}
chdir("/");//设置当前进程工作目录为根目录
umask(0);//设置为0,确保进程对文件有最大的操作权限
//关闭所有文件描述符,这里需要动态获取,获取当前系统允许对打的文件描述虎
for(int i = 0; i < sysconf(_SC_OPEN_MAX); i++)
{
close(i);
}
//把012定位到/dev/null
open("/dev/null",O_RDWR);
open("/dev/null",O_RDWR);
open("/dev/null",O_RDWR);
}
使用syslog来记录文件信息
测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
int main(void)
{
openlog("a.out", LOG_PID | LOG_CONS, LOG_USER);
syslog(LOG_INFO, "this is my loginfo\n");
closelog();
exit(0);
}
在守护进程中有一个syslogd,这个进程就是负责日志文件的写入和维护。
怎么能让程序不能被多次运行,在守护进程中,是长时间运行的,不退出。因此./a.out
多次运行就是有多个进程,这并不是我们想要的。有时候我们希望程序是单利运行,意思就是当我们运行的程序没有运行过,就运行,若是之前已经有一个程序的进程在运行,本次运行直接退出(提示程序在运行)。最常用的做法就是在执行的开始之初判断特定的文件是否存在,若存在则标明进程已经在运行了。
这个文件特定的文件要古怪一点,确保不会凑巧真的在电脑中存在的。
接下来浅谈一下进程间的通信
同一个进程在一个地址空间之中,同一个进程的不同模块之间很多时候都是通过全局变量,也可以通过函数形参传递;
不同进程处于不同的地址空间,因此要互相通信很难。99%不需要考虑进程间通行,因为大部分程序都是单进程的(可以通过多线程的方式解决并发问题),复杂大型的程序,因此设计的需要就被设计成多进程通信,常见的GUI程序,服务器等
结论:IPC技术在一般的中小型程序要办用不到,,在大型程序中才会用到。
linux提供的多种进程通信机制:
1、无名管道和有名管道
2、SystemV
3、Socke域套字
4、信号
管道:管道是单向通信的,是半双工的。只能在父子间通行
朱有鹏老师视频学习笔记

转载地址:http://ugtnmu.baihongyu.com/

你可能感兴趣的文章
一笔画问题【数据结构-图论】
查看>>
红黑树
查看>>
安装多个gcc
查看>>
Linux0.01内核根目录Makefile注释
查看>>
【CSDN2012年度博客之星】需要您的一票,感谢大家的支持
查看>>
PHP对于浮点型的数据需要用不同的方法去解决
查看>>
Tokyo Cabinet 安装
查看>>
Flink在美团的应用与实践听课笔记
查看>>
Java多线程的11种创建方式以及纠正网上流传很久的一个谬误
查看>>
JDK源码研究Jstack,JMap,threaddump,dumpheap的原理
查看>>
Java使用字节码和汇编语言同步分析volatile,synchronized的底层实现
查看>>
javac编译原理和javac命令行的使用
查看>>
Unity使用UnityWebRequest实现本地日志上传到web服务器
查看>>
Unity使用RenderTexture实现裁切3D模型
查看>>
美术和程序吵架,原来是资源序列化格式设置不统一
查看>>
Unity iOS接SDK,定制UnityAppController
查看>>
Unity iOS接SDK前先要了解的知识(Objective-C)
查看>>
python遇到了‘module‘ object has no attribute ‘socket‘问题,大概率是这个原因
查看>>
记一次iOS闪退问题的定位:NSLog闪退
查看>>
Unity打开照相机与打开本地相册然后在Unity中显示照片(Android与iOS)
查看>>