目 录
* 同一时刻有且只能有一个进程在运行!(针对于单核处理机环境下)
* 双核环境下可以同时有两个进程处于运行态!
一、进程的定义
(1)Tip:进程和程序的区别:
(2)进程是程序执行和资源(内存)管理的最小单位
原因:因为每一个进程都有一个0~4G的虚拟内存
二、进程的类型
三、进程运行的状态
四、进程的执行模式
五、进程相关的操作(系统调用)
同一时刻有且只能有一个进程在运行!(针对于单核处理机环境下)
双核环境下可以同时有两个进程处于运行态!
(1)创建进程
案例1:
#include<stdio.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
pid_t pid =fork();
//报错
if(pid<0)
{
perror("fork error");
}
//父进程
else if(pid>0)
{
while(1)
{
printf("i am parent\n");
sleep(1);
}
}
//子进程
else
{
while(1)
{
printf("i am child\n");
sleep(0.5);
}
}
return 0;
}
运行结果 :
* fork创建子进程后,会出现子进程和父进程争夺资源的情况,谁先运行是不确定的
案例2:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main(int argc, const char *argv[])
{
//创建子进程
printf("111111");
pid_t pid =fork();
if(pid<0)
{
perror("fork error");
}
else if(pid>0)
{
//父进程
while(1)
{
printf("i am parent\n");
sleep(1);
}
}
else
{
while(1)
{
printf("i am child\n");
sleep(1);
}
}
return 0;
}
运行结果:
linux@ubuntu:~/jincheng$ ./a.out
111111i am parent
111111i am child
i am parent
i am child
i am parent
i am child
i am parent
i am child
i am parent
i am child
i am parent
解释:出现两次111111的原因:
- printf(“1111111”)没有换行符,首先存储在缓冲区;
- 父进程和子进程中的printf有换行符,缓冲区遇到换行符会打印出来;fork之后,子进程会连同父进程的缓冲区一块拷贝过来,所以出现两次打印
案例3:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main(int argc, const char *argv[])
{
//创建子进程
pid_t pid =fork();
if(pid<0)
{
perror("fork error");
}
else if(pid>0)
{
//父进程
{
printf("i am parent\n");
}
}
else
{
sleep(3);
{
printf("i am child\n");
sleep(1);
}
}
printf("222222222\n");
while(1);
return 0;
}
运行结果:
linux@ubuntu:~/jincheng$ ./a.out
i am parent
222222222
i am child
222222222
结论:同一时刻只能有一个进程。所以parent打印完到打印22222;接着child打印完到22222
(2)两种异常进程
1、孤儿进程
概念:
父进程先于子进程退出,子进程会被systemd收养(ubuntu16.04之前init收养),变为后台进程
案例:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main(int argc, const char *argv[])
{
//创建子进程
pid_t pid =fork();
if(pid<0)
{
perror("fork error");
}
else if(pid>0)
{
//父进程
{
printf("i am parent\n");
}
}
else
{
sleep(3);
printf("i am child\n");
while(1);
}
return 0;
}
运行结果:
./a.out
linux@ubuntu:~/jincheng$ ./a.out
i am parent
ps -axj
2、僵尸进程
概念:子进程先退出,父进程没有回收子进程的资源(task_struct结构体),子进程就变成僵尸进程
注意:父进程没有退出,子进程就会一直保持僵死状态,直到父进程退出,子进程会被systemd回收
案例:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main(int argc, const char *argv[])
{
//创建子进程
pid_t pid =fork();
if(pid<0)
{
perror("fork error");
}
else if(pid>0)
{
//父进程
{
sleep(1);
printf("i am parent\n");
while(1);
}
}
else
{
printf("i am child\n");
}
return 0;
}
运行结果:
linux@ubuntu:~/jincheng$ ./a.out
i am child
i am parent
tip:
图1-孤儿进程
图2-僵尸进程
(3)进程退出
案例:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, const char *argv[])
{
//创建子进程
pid_t pid =fork();
if(pid<0)
{
perror("fork error");
}
//父进程
else if(pid>0)
{
int i=0;
while(1)
{
if(i>3)
{
printf("111111");
exit(0);//刷新缓冲区
}
i++;
printf("i am parent\n");
sleep(1);
}
}
else
{
while(1)
{
printf("i am child\n");
sleep(1);
}
}
return 0;
}
运行结果:
linux@ubuntu:~/jincheng$ i am child
i am child
i am child
i am child
i am child
i am child
i am child
i am child
i am child
i am child
i am child
Li am child
i am child
i am child
i am child
i am child
……
该进程为后台进程,kill -9 PID可将其杀死
(4)进程阻塞(回收子进程,可以避免僵尸进程)
参数中的*status是一个整形指针,用来表示子进程退出时的状态
- status为空,表示忽略子进程的退出状态
- status为其他值,表示保存子进程的退出状态
六、进程的分类之-守护进程
守护进程创建步骤:
案例:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
//(1)创建子进程,让父进程退出
pid_t pid = fork();
if(pid < 0)
{
perror("fork error");
return -1;
}
else if(pid > 0)
{
//退出
exit(0);
}
//(2)创建新的会话
int ret = setsid();
if(ret < 0)
{
perror("setsid error");
return -1;
}
//修改当前目录为根目录
chdir("/");
//修改文件权限掩码
umask(0);
//关闭文件描述符
int i;
for(i = 0; i < getdtablesize(); i++)
{
close(i);
}
3、通信相关概念
3.1 通信的模式
//写日志
//打开文件
int fw = open("daemon.log",O_WRONLY | O_CREAT,0777);
if(fw < 0)
{
perror("open error");
return -1;
}
//操作文件
while(1)
{
ret = write(fw,"hello",5);
if(ret > 0)
{
printf("write ok!\n");
}
sleep(1);
}
return 0;
}
运行:
sudo ./a.out
结果:TTY变成?控制终端变成守护进程
TTY:终端的次要装置号码,示例中的TTY列都是“?”,是表示这些进程不属于任何TTY,因为它们是由系统启动的,tty1-tty6 是本机上面的登入者程序,若为 pts/0 等等的,则表示为由网络连接进主机的程序。
七、进程间通信
-
通信方式
-
通信的三种模式
(一)无名管道
1.无名管道的相关信息
2.案例:
(1)关闭无名管道的 写端,读管道不会阻塞,返回0
#include <stdio.h>
#include <sys/types.h>
#include<unistd.h>
int main()
{
int fd[2];
//创建无名管道
int ret =pipe(fd);
if(ret<0)
{
perror("pipe error");
return -1;
}
//管道的写端
close(fd[1]);
//读管道
char buf[128]={0};
ret=read(fd[0],buf,sizeof(buf));
if(ret<0)
{
perror("read error");
return -1;
}
else if(ret ==0)
{
printf("read return 0\n");
close(fd[0]);
return 0;
}
else
{
printf("ret=%d,buf=%s\n",ret,buf);
}
close(fd[0]);
return 0;
}
(2)向无名管道写入数据,读取数据
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
//创建无名管道
int ret =pipe(fd);
if(ret<0)
{
perror("pipe error");
return -1;
}
//向管道写入数据
char buf[100]="hello world";
write(fd[1],buf,strlen(buf));
close(fd[1]);
//读管道
// char buf1[100]={0};
// ret=read(fd[0],buf1,strlen(buf));
ret=read(fd[0],buf,strlen(buf));
if(ret<0)
{
perror("read error");
close(fd[0]);
return -1;
}
else if(ret ==0)
{
printf("read return 0\n");
close(fd[0]);
return 0;
}
else
{
printf("ret=%d,buf=%s\n",ret,buf);
}
close(fd[0]);
return 0;
(3)测试管道破裂
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
int main()
{
//创建无名管道
int fd[2]={-1,-1};
int ret = pipe(fd);
if(ret <0)
{
perror("pipe error");
return -1;
}
//关闭读段
close(fd[0]);
//创建子进程
pid_t pid =fork();
if(pid < 0)
{
perror("fork error");
return -1;
}
//父进程
else if(pid > 0)
{
int status = 0;
ret = wait(&status);
if(ret > 0)
{
if(WIFEXITED(status))
{
printf("terminated normally %d\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("terminated by signal%d\n",WTERMSIG(status));
}
}
}
else
{
//子进程
char buf[100]={0};
printf("w:");
fgets(buf,sizeof(buf),stdin);
write(fd[1],buf,sizeof(buf));
exit(3);
}
return 0;
}
(二)有名管道
1.有名管道的相关概念
2.有名管道的创建
案例1:
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<stdio.h>
#include<errno.h>
int main()
{
char buf[100]={0};
//创建有名管道
int ret = mkfifo("test.fifo",0644);
if(ret < 0 && EEXIST != errno)
{
perror("mkfifo error");
return -1;
}
//打开文件
int fw = open("test.fifo",O_WRONLY);
if(fw < 0)
{
perror("open error");
return -1;
}
//操作文件
printf("w:");
fgets(buf,sizeof(buf),stdin);
write(fw,buf,sizeof(buf));
//关闭文件
close(fw);
return 0;
}
只有读端或者写端存在时,系统会阻塞,直到下一个程序的到来会打破这种阻塞
(三)信号
(四)IPC对象
(五)共享内存
查看共享内存命令:ipcs