一、进程基础
进程和程序
程序:是保存在磁盘上的实现了某个功能的代码模块的集合,是静态的,没有任何执行的概念。
进程:是程序的一次动态执行过程,进程是程序执行和资源管理额最小单位,进程是一个独立的可以调度的任务。
程序 = 代码+数据
进程 = 代码+数据+系统资源
进程 = task_struct+4G(虚拟)
2. 进程的标志
进程号:pid是唯一的标识进程的符号 getpid();
父进程号:ppid是标识当前进程父进程的符号 getppid();
注意:pid是进程的task_struct中的一项成员
task_strcut---》是PCB的一种,是Linux系统中描述进程的一个结构体
内容:
1 标识符——》pid/tid
2 状态
3优先级
4程序计数器(pc)
5 内存指针
。。。。
3. 进程类型
交互进程:给指令就有回应的进程如 ls ps
批处理进程:是一个进程集合,维护着一个进程的队列,负责按照时间先后顺序启动队列中的进程。
shut down -h time
守护进程:周期性的执行某项任务或等待某个事件发生的进程,不依赖shell终端,生存周期较长
(从开机开始运行到关机结束)
4. 进程相关的操作指令
ps -aux:查看系统中的进程
ps -axj:查看系统中的进程(有父进程)
top:实时查看linux系统进程
pstree:查看进程树
kill:关闭一个进程 kill -9 进程号
nice:以指定优先级运行进程 nice -20 ./test
renice:改变正在运行的进程的优先级 renice 19 进程号
5. 进程的状态
运行态:runing(R)
等待态:正在等待被运行, sleep
停止态:stop T/Z/X (kill -STOP pid)
Linux中进程的七种状态:
1 R运行状态(runing):并不意味着进程一定在运行中,也可以在运行队列中
2 S睡眠状态(sleeping):进程在等待事件完成(浅度睡眠,可以被唤醒)
3 D磁盘睡眠状态(disk sleep):不可中断睡眠(深度睡眠,不可以被唤醒,通常在磁盘写入时发生)
4 T停止状态(stopped):可以通过发送stop信号给进程来停止进程,可以发送SIGCONT信号让进程继续运行。
5 X死亡状态(dead):该状态为返回状态,在任务列表中看不到
6 Z僵尸状态(zombie):子进程退出,父进程还在运行,但是父进程没有读到子进程的退出状态值,子进程进入僵尸状态。
7 t追踪停止状态(trancing stop)
6. 进程的启动方式
两种:手动启动:./test
调度启动:在相应的配置文件中自动启动(/etc/init.d/rcS---->/bin/a.out
at:在指定的时间执行一个任务
at12:00 shutdown -S -T30
二、进程相关的系统调用函数
fork exec函数族 exit/_exit wait/waitpid
1 fork——》创建方式:复制了父进程的所有内容(代码段+数据段+系统资源段) pid_t fork(void)
作用:创建一个子进程
返回值:成功,同时返回两个值(pid>0和0)
= 0:子进程
>0 (子进程的pid):父进程
失败 :-1
孤儿进程:父进程先于子进程退出,子进程有init进程收养,此时的子进程就是孤儿进程,用pstree查看。孤儿进程没有危害。
僵尸进程:子进程先于父进程退出,父进程未处理子进程的退出状态,导致该子进程成为僵尸进程,僵尸进程占用一个task_struct,他会统一参与操作系统的调度,它本身没有空间,是无用的进程,我们应该避免它的产生。ps -aux
2 exec函数族——》替换进程(新的程序替换掉原有进程)
有6个函数,作用相同,传参方式不用
l:list 以列表方式传参,最后以NULL结束
v:vector以数组的方式传参,数组的最后一个元素为NULL
e:env环境变量,传参方式同数组
p:PATH,在PATH所指的路径中去找可执行程序
6个函数如下:
int execl(const char *path,const char *arg,....);
int execv(const char *path,char *const argv[]);
int execle(const char *path,const char *arg,...,char *const envp[]);
int execve(const char *path,char *const argv[],char *const envp[]);
int execlp(const char *file,const char *arg,...);
int execvp(const char *file,char *const argv[]);
3 exit/_exit
exit:退出当前进程并刷新IO缓存区
_exit:退出当前进程 不刷新IO缓存
4 wait/waitpid——》避免僵尸进程的产生
pid_t wait(int *status);——》阻塞函数
作用:等待任意子进程的退出状态
*status:指向子进程退出状态值的指针
返回值:成功:等到的子进程PID
失败:-1
pid_t waitpid(pid_t pid,int *status,int options);
作用:等待指定子进程的退出状态
pid:子进程的PID
*status:指向子进程退出状态值的指针
options:0:将waitpid函数变成了阻塞函数,若本次调用没有等到结果,则阻塞当前进程;若等到了结果,返回子进程的PID。
WNOHANG:将waitpid函数变成了非阻塞函数,若本次调用,没有等到子进程退出,waitpid返回0,如果等到了退出状态,waitpid返回,返回子进程的pid。
阻塞函数:该函数会获取一个结果,如本次调用没有获取到结果,该函数会阻塞当前进程,该函数不返回。
非阻塞函数:该函数会获取一个结果,如本次调用没有获取到结果,该函数不会阻塞当前进程,函数返回,返回一个非正确的值,之后用户可以通过多次调用该函数去获取正确的结果。
Day2
//PPID父进程、PID子进程、PGID组进程、SID会话进程、TTY终端
守护进程
定义:周期性执行某项任务或等待某个事件发生的进程,它的运行不依赖shell终端,它的生存周期较长, (从开机开始运行到关机才结束)
如何创建一个守护进程?
步骤1:创建一个子进程,父进程退出
pid = fork();if(pid > 0){exit(0)}
2 给子进程创建新的会话期,让它脱离当前shell终端
3 改变子进程的工作目录(改变当前目录为根目录/也可以不是根目录,要确保该目录不会被他人删除)chdir(“/”);
4 重设(取消)文件权限掩码
Umask(0);(取消文件权限掩码)
5 关闭所有文件描述符
2 线程相关
线程:是一种轻量级的进程,它统一参与和创建它的进程共享一块内存空间,线程和进程一样统一参与操作系统的调度。
引入线程的原因:进程间通信系统开销大,需要跨越地址空间;同一进程创建的两个线程间通信系统开销小不用跨越地址空间。
如何创建线程?
使用第三方线程库提供的相关函数pthread
Int pthread_create(pthread_t *thread,const pthread_attr_t *attr,
Void *(*start_routine)(void *),void *arg);
作用:创建一个线程
*thread:指向线程ID的指针,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
*attr:指向线程属性的指针(通常给NULL)
参数3:指向线程执行函数的指针
*arg:传给线程执行函数的入参
返回值:0成功,非0失败
Int pthread_join(pthread_t thread,void **value_ptr)
作用:阻塞等待线程退出,回收线程退出状态值
参数1:线程ID
参数2:指向线程退出状态值的指针
返回值:0成功;非0失败
Int pthread_exit(void *value_ptr)
作用:退出当前线程
*value_ptr:指向线程退出状态的指针
返回值:0成功;非0失败
Int pthread_cancel(pthread_t thead)
作用:取消线程/关闭线程
Thead:线程ID
返回值:0成功;非0失败
Ubuntu(32) = linux内核+桌面管理器+工具集+应用软件+其他
3 多线程引入的问题
3.1 线程间通信
定义:线程间数据交换
如何实现线程间通信?
——》使用全局变量(在进程中定义)
3.2线程同步
同步:是指两个对象/事物/任务按照约定好的顺序相互配合完成一件事情
线程的同步:两个线程按照约定好的顺序相互配合完成一件事情
如何实现线程间同步?
使用无名信号量(二值信号量)
sem_t sema;信号量
sem_t semb;信号量
如何使用无名信号量?
使用P操作和V操作函数
P操作:sem_wait(&sema)
含义:sema 》 0:函数返回;sema--
Sema = 0:阻塞当前线程
V操作:sem_post(&semb)
含义:给监控的信号量+1 semb++
sem_wait()
Printf(“hello\r\n”);
Int flag = 0;
3.3线程互斥
引入:两个线程可以使用同一个线程执行函数,若在该线程执行函数中定义了一个公共的buf,两个线程同时使用公共的buf,就会出现资源的竞争访问问题,会造成存放数据的混乱问题。
什么时候加锁解锁?
在使用公共资源前加锁,使用完后解锁。
如何解决资源竞态?
加锁(互斥锁)pthread_mutex_t mutex;——》互斥锁;
Int pthread_mutex_init(pthread_mutex_t *mutex,
Pthread_mutexattr_t *attr)
作用:初始化互斥锁
*mutex:指向互斥锁对象的指针
*attr:互斥锁属性,一般给NULL
返回值:0成功;非0失败
Int pthread_mutex_lock(pthread_mutex_t *mutex)
作用:给公共资源加锁
*mutex:指向互斥锁对象的指针
返回值:0成功;非0失败
Int pthread_mutex_unlock(pthread_mutex_t *mutex)
作用:解锁
*mutex:指向互斥锁对象的指针
返回值:0成功;非0失败
Day3
进程间通信--》无名管道 有名管道 信号通信 共享内存 消息队列 信号灯 socket通信
通信领域概念:
单工通信:传输方向单向
半双工通信:传输方向是双向,但同一时刻只能一个方向
全双工通信:传输方向是双向,可以双向同时传送
1 无名管道--》内核文件--》无名
使用pipe创建无名管道
int pipe(int fd[2])
fd[0]:指向管道固定的读端
fd[1]:指向管道固定的写端
父进程创建无名管道,子进程继承无名管道,所以父子进程可操作同一个管道
总结:
1 只能用于具有亲缘关系的进程间通信
2 无名管道的通信方式通常当单工使用(本质上是半双工,但通信时刻不好把握)
3 无名管道文件只存在于内核中,在文件系统中不可见,管道中的通信数据只存在于内存中
2 有名管道--》内核文件--》有名
int mkfifo(const char *filename,mode_t mode);
作用:用于创建有名管道
*filename:有名管道文件的名称
mode:文件权限
返回值:0 成功 -1 失败
总结:1 有名管道可以用于任意进程间的通信
2 可以实现双工通信(需要两个管道文件)
3 有名管道文件在文件系统中是可见的,但是数据大小为0,管道中的数据只存在于内存中
3 信号通信
信号通信:是在软件层面上对中断机制的一种模拟。是一种异步通信方式。
中断:是指在程序的执行过程中插入了另外一段程序的执行过程,该段程序执行结束后,CPU回到中断
点继续执行原来的程序。
Linux内核提供了64信号:kill -l
每一个信号都对应一个默认处理动作
关闭终端--》SIGHUP---》终止终端运行的进程
ctrl+c-->SIGINT--->终止当前进程
kill -9 1000--->SIGKILL--->终止进程1000
quit--->SIGQUIT-->中止进程
异步通信:通信中接收方并不知道信号(数据)什么时候会到达,当前进程一直准备着接收数据,同时也做
自己的事情,一旦信号(数据)到达,马上接收处理。所以是非阻塞通信模式。
同步通信:发送方发数据,接收方同步接收数据,双方需要在很短的时间内完成数据交换,否则会造成一方
阻塞。所以是阻塞通信方式。
在linux系统中,进程对信号的处理方式:
1 捕获信号
2 采用默认处理动作
3 忽略信号
int main()
{
alarm(2);
pause();-->挂起当前进程并等待信号,一旦SIGALRM信号来,会执行默认处理动作(终止当前进程)
printf("hello world\r\n");
return 0;
}
alarm(2)-->SIGALRM-->终止当前进程
alarm:是一个闹铃函数,它可以在进程中设置一个定时器,当定时时间到时,内核就向当前进程发送
SIGALRM信号。
pause:挂起当前进程,并等待一个信号的到来。
如果不想让系统终止当前进程,那么可以把信号对应的默认处理动作做修改(改成一个函数),那么如何
改变信号对应的默认处理动作呢?用signal
sighandler_t signal(int signum, sighandler_t handler);
作用:修改信号对应的默认处理动作
signum:信号
handler:信号对应的新的处理动作函数
实现:通过信号实现A进程和B进程的通信
条件:1 信号--》kill -l SIGUSR1 SIGUSR2
2 信号的发送方如何发信号?kill raise
kill:给对方进程发信号
raise:当前进程给自己发信号
int kill(pid_t pid, int sig)
作用:给指定的进程发信号
pid:进程号
sig:信号 SIGUSR1
返回值:0 成功 -1 失败
3 信号的接收方如何捕获接收信号?pause
A进程:发信号
1 得知对方进程的进程号 main函数传参
2 kill(pid,SIGUSR1);
./signal_killA 1000
B进程:收信号 并处理
1 输出进程号
2 signal(SIGUSR1,Func)
3 pause;
Day4——》进程间通信
1共享内存(IPCS查看)

原理:通信进程创建一块共享内存,然后找出一块本地内存和共享内存建议映射关系,之后往本地内存中存放数据(实际也将数据写入到了共享内存),被通信方进程找到共享内存,并和其建立映射关系,之后从本地内存中取数据,就是从共享内存中取数据
实现两个进程通信:
A 1:ftok(“.”,11)——》创建一个对方进程找到自己的标识符
2:shmget——》创建一片共享内存
3:shmat——》本地内存和共享内存映射
4:memset/memcpy 15个10
B: 1:key = ftok(“.”,11)
2:shmget
3:shmat——》本地内存和共享内存映射
4:printf(p);打印15个10
key_t ftok(const char*pathname,int proj_id)
作用:创建一个标识符
*pathname:指向文件名或带路径的文件名
proj_id:0~255
返回值:成功,是一个整形的标识符。失败是负一
Int shmget(key_t key,size_t size,int shmflg);
作用:创建一片共享内存
Key:ftok的返回值
Size:共享内存的大小
Shmflg:共享内存属性&八进制的权限位
返回值:成功,标识共享内存的标识符,失败-1.
Void *shmat(int shmid,const void *shmaddr,int shmflg)
作用:从用户空间找一块本地内存和共享内存建立映射
Shmid:表示共享内存的标识符
*shmaddr:指向本地内存的指针,一般给NULL(空)
Shmflg:SHM_RDONLY:共享内存只读
默认0:共箱内存可读写
返回值:成功,指向映射成功的本地内存地址的指针
Int shmdt(const void *shmaddr);
作用:取消映射
*shmaddr:指向本地内存的指针
返回值:成功0,失败-1
Int shmctl(int shmid,int cmd,struct shmid_ds *buf);
作用:设置/获取共享内存属性
Shmid:共享内存的标识符
Cmd:IPC_STAT(获取对象属性)——》第三参数存放获取到的对象属性
IPC_SET(设置对象属性)——》第三参数用于存放要设置的对象属性
IPC_RMID(删除对象)——》第三个参数为NULL
返回值:成功0,失败-1
Ipcs:用来查看所有IPC对象
注意:共享内存中的数据会一直存在,除非删除共享内存
注意:调用自定义函数/库函数——》用户空间
调用系统调用函数——》用户空间——》内核空间——》用户空间
2消息队列
原理:通信方进程创建一个消息队列,然后往消息队列中存放一条或多条消息,被通信方进程找到该消息队列,然后从里边取消息,取一条消息后,队列长度自动减一。
消息的定义格式:
Struct 消息结构体名
{
消息类型;long m_type;
消息正文;char m_buf[N];
}
A: 1 ftok
2 msgget——》创建一个消息队列
3 msgsnd——》往消息队列放消息
B: 1 ftok
2 msgget——》获取消息队列ID
3 msgrcv——》从消息队列中读取一条消息(按照存放顺序或消息类型)
Int msgget(key_t key, int msgflg)
作用:创建/获取消息队列ID
Key:ftok的返回值
Msgflg:消息队列属性&八进制的权限位 IPC_CREAT
返回值:成功返回消息队列ID,失败-1。
Int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg)
作用:往消息队列中存放消息
Msqid:消息队列ID
*msgp:指向消息对象
Msgsz:消息大小
Msgflg:IPC_NOWAIT 消息没有发送完成函数也会立即返回。
0:直到发送完成函数才返回
返回值:成功0,失败 -1
Ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
作用:从消息队列中获取一条消息
Msqid:消息队列ID
*msgp:接受消息的结构体指针
Msgsz:接受消息的大小(消息正文大小)
Msgtyp:0:接收消息队列中第一个消息。
大于0:接受消息队列中第一个类型为msgtyp的消息
小于0:接受消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。
Msgflg:0:若无消息函数会一直阻塞
IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG。
返回值:成功接受到消息的长度失败-1
Int msgctl(int msgqid,int cmd,struct msqid_ds *buf)
作用:设置或获取消息队列属性
Msqid:消息队列ID
Cmd:IPC_STAT(获取对象属性)——》第三参数存放获取到的对象属性
IPC_SET(设置对象属性)——》第三参数用于存放要设置的对象属性
IPC_RMID(删除对象)——》第三个参数为NULL
返回值:成功0,失败-1
注意:消息队列中的消息,取一条少一条
3信号灯 = 信号量
Linux内核给用户提供了三种信号灯:
1 无名信号灯——》两个线程的同步 sem_t sema;P/V操作函数
2 有名信号灯——》两个进程的同步
3 信号灯集
进程间同步:
A:1 创建有名信号灯 sem_open
sem_open
2 while(1)
{
sem_wait();
Hello;
Sleep(1)
sem_post();
}
B:
sem_t *sem_open(const char *name,int oflag,
mode_t mode,unsigned int value);
作用:创建一个有名信号灯
*name:信号灯的名称
Oflag:O_CREAT
Mode:操作信号的权限 0666
Value:信号灯的初值0/1(0阻塞,1执行)
返回值:成功,指向操作信号灯的指针
失败SEM_FAILED
注意:有名信号量存放在/dev/shm里,所以创建有名信号量时不需要指定信号量的存放路径。
Day5
信号灯集
指一个或者多个信号灯的集合,其中的每一个都是单独的计数信号灯,每个数字代表资源的数量。每一个信号灯可以实现一组进程的同步或互斥。
Int semget(key_t key,int nsems,int semflg);
作用:创建一个信号灯集
参数:
key:ftok的返回值
Nsems:信号灯的个数
Semflg:信号灯集的访问权限,通常为IPC_CREAT | 0666
返回值:成功,信号灯集的ID,失败-1
Int semop(int semid,struct sembuf *opsptr,size_t nops);
作用:操作信号灯
参数:
Semid:信号灯集ID
Struct sembuf
{
Short sem_num; //要操作的信号灯编号
Short sem_op; //0:等待,直到信号灯的值变成0
//1:释放资源,V操作
//-1:分配资源,P操作
Short sem_flg; //0,IPC_NOWAIT, SEM_UNDO
};
Nops:要操作信号灯的个数
返回值:成功:0,失败-1
Int semctl(int semid,int semnum,int cmd .../*union semunarg*/);
作用:设置或获取信号灯的属性
参数:
Semid:信号灯集ID
semnum:要修改的信号灯编号
Cmd:GETVAL:获取信号灯的值
SETVSL:设置信号灯的值
IPC_RMID:从系统中删除信号灯集合
返回值:0成功,-1失败
2359

被折叠的 条评论
为什么被折叠?



