一、什么是进程
(一)linux下查看进程
ps aux , ps axj ,pa -ef , pstree , top
(二)进程的概念
进程是程序的一次动态执行,包括创建、调度、消亡
进程是程序执行和资源(内存)管理的最小单位 (因为每个进程都有一个0~4G的虚拟内存)(虚拟内存的存在是为了实现多任务,安全)
(三)进程和程序的区别
进程(./a.out)是一个动态的概念,它是程序执行的过程,包括创建调度、消亡
程序(a.out)是静态的,是保存在磁盘是的一些指令的有序集合,没有任何要执行的概念
(四)如何区分不同的进程
依靠PID(进程号)区分
(五)进程分类
1、交互进程
由shell控制和运行,既可以在前台运行,又可以在后台运行
2、批处理进程
不属于任何终端,它被提交到一个队列中以便顺序执行
3、守护进程
(1)特点
在后台运行,与终端无关。
系统启动时开始运行,系统关闭时停止运行
(2)进程和终端的关系
进程的控制终端相同,具有亲缘关系
(3)守护进程的创建步骤
a.创建子进程,父进程退出
b.在子进程中创建新的会话
c.改变当前路径为根目录
d.重设文件掩码
e.关闭文件描述符
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
#include <stdlib.h>
#include <time.h>
#include<fcntl.h>
#include <sys/stat.h>
#include<string.h>
int main(int argc, const char *argv[])
{
//1.创建子进程,让父进程先退出
pid_t pid = fork();
if(pid < 0)
{
perror("fork error");
return -1;
}
else if(pid > 0)
{
//父进程退出
exit(0);
}
//2.在子进程中创建新会话
int ret = setsid();
//3。改变当前目录为根目录
chdir("/");
//4。修改文件掩码
umask(0);
//5。关闭文件描述符
int i=0;
for(int i = 0; i < getdtablesize();i++)
{
close(i);
}
//写日志
int fw = open("daemon.log",O_RDWR | O_CREAT,0777);
if(fw < 0)
{
perror("open error");
return -1;
}
time_t tm;
char buf[20];
while(1)
{
memset(&tm,0,sizeof(time_t));
//获取从1970-1-1到现在的秒数
time(&tm);
char *ptr = ctime(&tm);
strcpy(buf,ptr);
ret = write(fw,buf,sizeof(buf));
//ret = write(fw,"hello",5);
if(ret > 0)
{
printf("wrtie ok!\n");
}
sleep(1);
}
return 0;
}
(六)进程运行的状态
1、运行状态
(1)运行态
(2)等待态
(3)停止态
(4)僵尸态
(5)死亡太
2、
(七)进程的模式
1、用户模式
2、内核模式
3、相关调用
nice:按用户指定好的优先级运行程序
renice:改变正在运行的进程的优先级
(八)进程相关的系统调用
1、创建进程
pit_t fork();
2、获取自己的PID
pid_t gatpid();
3、获取父进程的PID
pid_t getppid();
4、退出进程
_exit() (不刷新缓冲区)
exit() (刷新缓冲区)
return( 刷新缓冲区)
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
printf("1111\n");
pid_t pid = fork();
//父进程
if(pid >0)
{
/*
while(1)
{
//打印自己的pid
printf("i am parent,pid=%d\n",getpid());
sleep(1);
}
*/
int stat;
// wait(NULL);//阻塞等待函数 避免僵尸进程
while(1)
{
int ret = waitpid(-1,&stat,WNOHANG);
printf("ret = %d\n",ret);
if(ret >0)
{
if(WIFEXITED(stat))
{
printf("子进程正常退出%d\n",WEXITSTATUS(stat));
}
else if(WIFSIGNALED(stat))
{
printf("异常退出%d\n",WTERMSIG(stat));
}
}
sleep(1);
printf("i am parent\n");
}
}
//子进程
if(0 == pid)
{
/*
if(i >2)
{
printf("exit--------");
//退出进程 exit ,_exit ,return
//exit(0);//刷新缓冲区
// _exit(0);//不刷新缓冲区,不执行上面的printf
}
*/
//打印父进程,自己进程pid
// sleep(1);
printf("i am child,ppid=%d pid=%d\n",getppid(),getpid());
}
return 0;
}
(九)孤儿进程
1、概念
父进程先于子进程退出,子进程会被systemd进程收养,子进程会变成后台
(十)僵尸进程
1、概念
子进程先退出,父进程没有及时回收子进程的资源(PCB结构体)(没有及时查看子进程的退出状态
2、避免僵尸进程
子进程退出时及时回收子进程的资源,调用wait , waitpid
(十一)exec函数族
提供了一种在进程中启动另一个程序的方法
二、进程间通信的方式
(一)传统的unix通信方式
1、无名管道
(1)相关概念
只能用于具有亲缘关系的进程之间的通信
半双工的通信方式,有固定的读端fd[0]和写端fd[1]
管道可以看成是特殊的文件(内核中的一块内存),可以用文件IO对其读写
管道通信基于文件描述符
(2)创建
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/wait.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");
close(fd[0]);
close(fd[1]);
return -1;
}
else if(pid > 0)
{
/*
//父进程 往管道里写
//关闭无名管道读端
close(fd[0]);
char buf[30]={'\0'};
//从标准输入读到buf
fgets(buf,sizeof(buf),stdin);
printf("w:");
//写进管道
write(fd[1],buf,sizeof(buf));
sleep(1);
*/
//父进程
// close(fd[0]);
int stat;
int ret = wait(&stat);
if(ret < 0)
{
perror("waitpid error");
close(fd[1]);
return -1;
}
else
{
if(WIFSIGNALED(stat))
{
printf("子进程同过信号退出%d\n",WTERMSIG(stat));
}
if(WIFEXITED(stat))
{
printf("正常退出%d\n",WEXITSTATUS(stat));
}
}
}
else
{
//子进程 写
//关闭无名管道读端
// close(fd[0]);
printf("w:");
char buf[30]={'\0'};
//从标准输入读到buf
fgets(buf,sizeof(buf),stdin);
//写进管道
write(fd[1],buf,sizeof(buf));
close(fd[1]);
/*
//子进程 从管道里读出来
//关闭无名管道写端
close(fd[1]);
char buf[30] = {'\0'};
printf("r:");
//从无名管道读到buf
read(fd[0],buf,sizeof(buf));
printf("%s\n",buf);
*/
}
/*
//关闭文件描述符
close(fd[0]);
close(fd[1]);
*/
return 0;
}
(3)注意
当管道中无数据,读操作会阻塞
写数据时,管道缓冲区有空闲,写进程就一种试图写入,如果不读走缓冲区中的数据,写操作会阻塞
只有在读端存在时,向管道写数据才有意义,否则会管道破裂
2、有名管道
(1)相关概念
可以使互不相关的两个进程相互通信,通过名字来指明路径,并在文件系统中可见
进程通过文件IO来操作有名管道
有名管道遵循先进先出规则
不支持lseek()存在
(2)创建
//创建有名管道(管道本质是个文件)
int ret = mkfifo("f1.fifo",0777);
if(ret < 0 )
{
perror("f1.fifo error");
return -1;
}
(3)注意
只有读端或者写端存在时,系统会阻塞,直到另一个进程(包括自己)以另一种方式打开管道
3、信号
(1)相关概念
信号在软件层次上对中断机制的一种模拟,是一种异步通信方式
信号可以直接进行用户空间和内核空间之间的交互,内核进程可以利用它来通知用户进程发生了哪些系统事件
如果进程当前并未执行状态,则该信号就有内核保存起来,只到进程执行在传递
(2)信号响应
忽略,捕捉,执行缺省操作
(3)列出所有信号
kill -l
(4)安装信号
(5)信号相关的系统调用
时钟函数:alarm
挂起过程:pause
发送信号给进程或进程组:kill
进程向自己发送信号:raise
(二)systemV提供的通信方式
1、共享内存
(1)相关概念
systemVT提供的IPC机制主要有消息队列、共享内存、信号量三个,和文件一样,IPC在使用前必须先创建,每种IPC都有特定的生产者、所有者和访问权限。使用ipcs命令查看当前系统正在使用的IPC工具。
共享内存是一种最为高效的通信方式,进程可以直接读写内存,不协议拷贝。
内核专门留了一个内存块,可以由进程将其映射到自己的私有空间。
2、共享内存的创建
(1)创建并代开共享内存 shmget
(2)映射共享内存 shmat
(3)撤销共享内存 shmdt
(4)删除共享内存 shmctl
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
void sigFunc(int signum)
{
if(signum == SIGUSR1)
{
printf("");
}
}
int main()
{
//1。创建并打开共享内存
//获取key值
key_t key = ftok(".",23);
if(key < 0)
{
perror("ftok error");
return -1;
}
printf("key:%d\n",key);
//获取ID值
int shmId = shmget(key,100,IPC_CREAT | 0777);
if(shmId < 0)
{
perror("shmget error");
return -1;
}
printf("shmId : %d\n",shmId);
//2。影射
void *pAddr = shmat(shmId,NULL,0);
if((void *)-1 == pAddr)
{
perror("shmat error");
//返回之前删除共享内存
shmctl(shmId,IPC_RMID,NULL);
return -1;
}
//获取该进程的pid,写入共享内存
*(int *)pAddr = getpid();
//操作
//安装信号
signal(SIGUSR1,sigFunc);
while(1)
{
//挂起该进程直到W写完发信号
pause();
//W入quit退出
if(0 == strcmp((char *)pAddr,"quit"))
{
break;
}
printf("r:%s\n",(char *)pAddr);
}
//取消映射
int ret = shmdt(pAddr);
if(ret < 0)
{
perror("shmdt error");
shmctl(shmId,IPC_RMID,NULL);
return -1;
}
return 0;
}
2、消息队列
/*通过消息队列发消息,msgd_r收消息*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define N 30
typedef struct msgbuf
{
long mtype;
char mtext[N];
}Msg;
int main()
{
//获取key值
key_t key = ftok(".",8);
if(key < 0)
{
perror("ftok error");
return -1;
}
//获取ID值
int msgId = msgget(key,IPC_CREAT |0666);
if(msgId < 0)
{
perror("msgget error");
return -1;
}
//添加消息
//定义一个结构体变量
Msg msg = {0};
int i = 0;
for(i = 0; i < 3 ;i++)
{
printf("type:");
scanf("%ld",&msg.mtype);
printf("text:");
scanf("%s",msg.mtext);
msgsnd(msgId,(void *)&msg,sizeof(msg.mtext),0);
}
//删除消息队列
// msgctl(msgId,IPC_RMID,NULL);
return 0;
}
3、信号量
(三)通信的相关概念
1、通信的模式
单工、半双工、双工、同步、异步
2、通信协议
双方约定好的规定
三、什么是线程
(一)线程的相关概念
1、线程是系统调度的最小单位
由于进程的地址空间是私有的,因此进程上下文切换时,系统开销比较大
为了提高系统的性能,许多操作系统规范里引用了轻量级的进程的概念,也被称为线程
在同一个进程中创建的线程共享该进程的地址空间
linux里同样用task_struct来描述一个线程。线程和进程都参与统一的调度
2、一个进程中的多个线程共享以下资源
可执行的指令、静态数据、进程中打开的文件描述符、信号处理函数、当前工作目录、用户ID、用户组ID
(二)线程的创建
1、头文件
#include <pthread.h>
2、函数原型
int pthread_create(pthread_t * thread,const pthread_atty_t *attr, void *(*routine)(void *),void * arg)
3、参数
thread:创建的线程
attr:指定线程的属性,NULL表示使用缺省属性
routine:线程执行函数 返回值为void *类型,参数为void * 类型的函数
arg:传递给线程执行函数的参数
4、返回值
成功:0
失败:-1
注意:线程之间也存在资源竞争的关系
(三)线程等待函数
1、头文件
#include <pthread.h>
2、函数原型
int pthread_join(pthread_t thread,void **value_ptr)
3、函数参数
thread :要等待的线程
value_ptr:指针*value_ptr指向线程返回的参数
4、返回值
成功:0
失败:-1
(四)将线程设置成为游离状态
1、头文件
#include <pthread.h>
2、函数原型
int pthraed_detach(pthread_t thread)
3、函数参数
pthread :要设置的线程
4、返回值
成功:0
失败:-1
(五)线程退出函数
1、头文件
#include<pthread.h>
2、函数原型
int pthread_exit(void *value_ptr)
3、函数参数
value_ptr:线程退出时返回的值
4、返回值
成功:0
失败:-1
(六)多线程
1、多线程共享一个进程地址空间
优点:线程间很容易进行通信(通过全局变量实现数据共享和交换)
缺点:多个线程同时访问共享内存时需要引入同步和互斥机制
1、同步
(1)概念
同步指的是多个任务(线程)按照约定的顺序相互配合完成一件事情
由信号量来决定线程是继续还是阻塞
(2)初始化信号量
头文件
#include <semaphore.h>
函数原型
int sem_init(sem_t *sem,int pshared,unsigned int value)
函数参数:
sem:初始化的信号量
pshared:信号量共享范围(0:线程间使用 非0:进程间使用)
value:信号量初值
返回值:
成功:0
失败:-1
P/V操作:(参数为信号量sem)
sem_wait( ):P操作 -1
sem_wait在信号量大于0时,它将这个信号量的值减1,若信号量的值为0,sem_wait将会阻塞
sem_post( ):V操作 +1
sem_post相当与V操作,它将信号量的值加1,同时唤醒等待的进程
注意:也可以保护临界资源的完整性,而且实现同步
2、互斥
(1)概念
引入互斥锁的目的是用来保证共享数据操作(临界资源)的完整性
互斥锁主要用来保护临界资源
每个临界资源都要有一个互斥锁来保护,任何时刻最多只能有一个线程能访问该资源
线程必须先获取互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止
(2)创建互斥锁步骤
a.创建锁
类型 锁名
pthread_mutex_t myMutex;
b.初始化锁
头文件:
#include <pthread.h>
函数原型:
int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexarr_t *attr)
函数参数:
mutex:互斥锁
attr:互斥锁属性,NULL表示缺省属性
返回值:
成功:0
失败:-1
c.上锁
头文件:
#include <pthread.h>
函数原型:
int pthread_mutex_lock(pthread_mutex_t *mutex)
函数参数:
mutex :互斥锁
返回值:
成功:0
失败:-1
d.解锁
头文件:
#include <pthread.h>
函数原型:
int pthread_mutex_unlock(pthread_mutex_t *mutex)
函数参数:
mutex :互斥锁
返回值:
成功:0
失败:-1
e.销毁锁
头文件:
#include<pthread.h>
函数原型:
int pthread_mutex_destroy(pthread_mutex_t *mutex)
函数参数:
mutex:要销毁的锁
返回值:
成功:0
失败:-1