Linux多任务编程学习笔记

Linux多任务编程

一、进程机制
多任务处理:指用户可以在同一时间内运行多个应用程序,是使用某种调度策略支持多个任务并发执行。
注: 某种调度策略指的是某种调度算法,按次序,优先级,依次循环。
Linux下查看任务命令:
ps ;
ps -l ;
ps -aux ;
ps -elf(Unix 系统通用)

程序:程序是一段静态的代码,时保存在非易失性存储器上的指令和数据的有序集合。
进程:进程是一个动态概念,他是程序在内存中的一次动态执行过程(这个过程包含:创建,调度,执行,消亡)

进程主要有三类:
交互式进程(要求实时性高,优先级高,如shell等)
批处理进程(交互性不高,如编译器)
守护进程(后台一直运行,系统的各种服务)

内核将所有进程存放在双向循环链表(内核链表 task_struct)中。
内核链表结构有主要的两个域:
state(进程状态)
pid(进程标识符)

(1)进程状态(state)包括:运行状态、阻塞状态(可中断/不可中断)、暂定状态、僵死状态、消亡状态
要掌握各个状态之间的转换关系。
(2)进程标识符(pid)

创建进程的操作:——fork()和exec家族
1)首先,fork()通过复制当前进程创建一个子进程,除PID、PPID和某些资源和统计量不同,其它都复制(fork使用写时复制技术提高系统速度)
2)exec家族负责读取可执行文件并将其载入地址空间开始运行。
*进程的终止操作:*wait函数族
1)Linux系统将终止的进程设置为僵死状态。
2)父进程在某个时间调用wait函数族,回收子进程的退出状态,子进程资源随后释放。

《拓展》
虚拟内存的概念:每个进程0~3GB用户,内核3~4GB
线程的概念:降低进程在进行切换时操作系统开销,引入线程(轻量级进程),但线程间资源共享,线程同步问题很重要。

二、进程编程
1、fork()
创建一个新进程(子进程),子进程复制除进程号、资源使用、计时器等的其他内容。
父进程和子进程的最大区别:fork()的返回值不同。利用这个依据,可以判断父子进程

SYNOPSIS
       #include <unistd.h>
       pid_t fork(void);

特别注意fork函数有两次返回值:
0 : 子进程
> 0 :父进程,返回值为子进程的pid
-1: 出错

一般情况下,如果子进程没有使用exec族函数创建新的程序进程,父子进程在fork的下一条代码开始执行相同的操作,父进程会更快结束,因为子进程在创建时候,复制、调用过程中需要耗费一些时间。

2、exec函数族
提供了在进程中执行另一个程序的方法。
方式:根基制定的文件名或者目录名找到可执行文件,并用它来取代当前进程的数据段、代码段和堆栈段。
如果执行成功,当前的进程除了进程号,其它内容都被替换,exec的下一条语句将不会被执行。

SYNOPSIS
       #include <unistd.h>
       extern char **environ;
       int execl(const char *path, const char *arg, ...);
       int execlp(const char *file, const char *arg, ...);
       int execle(const char *path, const char *arg,
                  ..., 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[]);
       >l:   list         //列举传送的参数
       e: environ  //自定义环境变量
       p: path    //给出可执行文件或者脚本的路径
       v: argv  //通过传字符串指针数组形式传参
       p: 直接给出可执行文件名

注意:参数列表最后一个必须为NULL。

3、exit函数族 exit() _exit() _Exit()
用来终止进程的,进程会无条件地停止剩下的所有操作,清除所有数据结构,并停止进程。

区分两种退出方式,清除缓冲和不清缓冲
exit() :调用退出函数,清I/O缓冲()
_exit()/Exit():直接调用终止程序,不执行清缓冲程序,需要手动清理,如ffbush()。

int fflush(FILE *stream);

4、 wait() waitpid()函数
等待、查询,回收子进程资源,处理僵死子进程,获取子进程的的退出状态和退出值。
wait() 使父进程进入阻塞状态,等待任意一个子进程结束,然后进行回收操作。
waipid() 可选择地对子进程进行回收

SYNOPSIS
       #include <sys/types.h>
       #include <sys/wait.h>
       pid_t wait(int *status);
       pid_t waitpid(pid_t pid, int *status, int options);

status 该值可利用Linux一些特定宏进行测定子函数的结束状态。
返回值pid_t:
> 0 回收的子进程PID
-1 失败
0 使用options的WNOHANG ,但子进程没有退出(使用该选项可以避免父进程进入阻塞等待状态)
pid:
>0 回收进程ID等于PID的子进程
-1 回收任一子进程,和wait相同

Linux进程间通信

概述
Linux进程间通信继承于:UNIX、System V、Socket、POSIX
Linux系统使用较多的进程间通信方式有:

数据交互:
1)有名管道和无名管道(pipe & fifo)
2)消息队列(message queue)
3)共享内存(shared memory)
4)套接字(socket)
《》》》》》》》》》》》》》》》
5)信号(signal):一种异步通信方式
6)信号量(semaohore):用于共享资源的保护

一、管道通信
把一个程序的输出直接连接到另一个程序的输入。
最古老的通信方式

1、无名管道

简介:
只能用于亲缘关系之间的进程,如父子进程、兄弟进程
半双工,只能一端读取,另一端写入,不可一端又读又写
无明管道文件存在内存(内核)中,可以使用read() write(),但不能使用open(), lseek()

无名管道的创建与关闭:

创建
SYNOPSIS
       #include <unistd.h>
       int pipe(int pipefd[2]);
关闭
       close()

函数创建两个文件描述符pipfd[0]、pipfd[1]
pipefd[0]固定用于读管道
pipefd[1]固定用于写管道
注意:
因为管道是半双工模式,一个进程利用pipe()函数创建了两个读和写文件描述符,确定进程需要使用的其中一个后,需要将另一个关闭close(pipefd[x])。
管道的读端存在时,向管道写入数据才有意义。
如管道满,写操作将阻塞
留意读写进程之间的同步与互斥问题,可以使用sleep

2、有名管道

简介:
是对无名管道的一种改进
可以用于无相关的两个进程之间通信
可以通过路径名指出,文件系统中可见,可以使用open()函数等如普通文件的操作,但是不能使用lseek()文件定位操作。
必须遵循先进先出规则,对管道及FIFO的读诗从开始处读,对他的写是写到末尾。

有名管道的创建

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       int mkfifo(const char *pathname, mode_t mode);
//类似文件中的open函数

创建管道文件成功后,使用读open() O_RDONLY打开,
使用写open() O_WRONLY打开,常规read(),write()函数
对于读进程:如果FIFO内没有数据,读进程将会阻塞到有数据来或FIFO写端被关闭。
对于写进程:只要FIFO有空间,空间不足则阻塞。

二、信号通信
是在软件层面上对中断的一种模拟。
异步通信:进程不需要设置等待函数来等候信号的到达,如果信号没有来得及处理,会暂存信号,稍后可以处理时候再处理。

1~31号信号为不可靠信号。不排队,只接一个
34~64号信号为可靠信号。信号排队不丢弃

进程可以通过三种方式响应信号
1)忽略信号,SIGKILL和SIGSTOP不可忽略
2)捕捉信号,自定义信号处理函数,发生信号时,执行自定义信号处理函数。使用signal()函数
3)执行末日操作,系统对信号都有默认的处理,大多数是终止进程

信号相关函数:

发送信号函数:
SYNOPSIS
       #include <sys/types.h>
       #include <signal.h>
       int kill(pid_t pid, int sig);   //发送信号给进程或进程组,pid 的取值功能和wait一样
SYNOPSIS
       #include <signal.h>
       int raise(int sig); //给自己发信号
设置信号函数:
SYNOPSIS
       #include <signal.h>
       typedef void (*sighandler_t)(int);
       sighandler_t signal(int signum, sighandler_t handler);
SYNOPSIS
       #include <signal.h>
       int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
其它函数
SYNOPSIS
       #include <unistd.h>
       unsigned int alarm(unsigned int seconds);
SYNOPSIS
       #include <unistd.h>
       int pause(void);

信号集:

SYNOPSIS
       #include <signal.h>

       int sigemptyset(sigset_t *set); //清空
       int sigfillset(sigset_t *set);//填满
       int sigaddset(sigset_t *set, int signum);//增加,将信号记录指定位置位
       int sigdelset(sigset_t *set, int signum);//删除
       int sigismember(const sigset_t *set, int signum);//查询,屏蔽期间也可查询

信号屏蔽:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

how:信号屏蔽方式,有三种取值
SIG_BLOCK —相当于新的屏蔽加上旧的屏蔽
SIG_UNBLOCK–相当于旧的屏蔽减去新的屏蔽,相当于解除旧的指定的屏蔽
SIG_SETMASK–直接使用新的屏蔽,
set 新的屏蔽字
signum 将旧的屏蔽字传输出来,用来恢复旧的屏蔽
注意:信号屏蔽之后,一定要解除屏蔽

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值