Linux系统编程(二)

一.多进程 wait waitpid函数 无名管道 pipe函数

多进程优点:
对用户来说操作系统更加健壮,(一个应用程序无法破坏另一个进程或操作系统的运行)
对于开发者来说,更容易进行内存的读取和写入
缺点:
多任务实现开销比较大
编写能够与其他进程通信或者能够对其他进程操作的应用程序要困难得多

wait函数族
1.wait函数:将当前进程挂起,等待信号来临,再继续运行
用来等待子进程的结束
pid_t wait(int *status);
WEXITSTATUS:宏定义:返回子进程的退出状态
在这里插入图片描述
在这里插入图片描述

2.waitpid:可以指定等待的进程号
目的:有时只需要指定某一个进程,回收就可以,可以避免资源浪费。
pid_t waitpid(pid_t pid, int *wstatus, int options);

在这里插入图片描述
在这里插入图片描述

僵尸进程:

  1. zombie
    产生的原因:当进程走完自己的生命周期后,会执行自己的系统调用exit(9)函数,然后内存被释放,没有执行代码。但是在进程表里面任然留下进程信息(pid,退出进程信息),直到父进程将他们回收。
    僵尸进程应该尽量避免
    避免僵尸进程的方法:wait函数族
    top查看

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

进程间的通信(IPC)方式
管道:有名管道和无名管道
消息队列
信号量
共享内存
套接字
进程间通信目的:
1.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
2.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
3.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
4.资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
5.进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

管道通信:无名管道:一端写,一端读
管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立两个管道

int pipe(int pipefd[2]);
fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
必须在系统调用fork()前调用pipe()。否则子进程将不会继承文件描述符
(1)如果管道的写端不存在,则认为已经读到数据末尾,该函数返回的读出字节数为0
(2)如果管道写端存在,如果请求的字节数目大于PIPE_BUF,则返回现有的数据字节数;如果不大于,则返回现有数据字节数,或请求字节数
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

fork和vfork总结

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如果没有exit(0)的话,子进程没有调用exec 或exit,所以父进程是不可能执行的,在子
进程调用exec 或exit 之后父进程才可能被调度运行。
所以加上exit(0);使得子进程退出,父进程执行,这样else 后的语句就会被父进程执行,
又因在子进程调用exec 或exit之前与父进程数据是共享的,所以子进程退出后把父进程的数
据段cnt改成1 了,子进程退出后,父进程又执行,最终就将cnt变成了2。

二.有名管道 mkfifo函数

有名管道
mkfifo()
int mkfifo(const char *pathname, mode_t mode);
mode:O_WRONLY
O_RDONLY
管道文件以p开头

1.传输数据量少,因此适合小数据量的传输
2.管道数据被读出便会自动删除
3.管道数据是什么格式输出

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建有名管道 子进程写父进程读 然后父进程写子进程读

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

三.信号中断

2.信号:软中断
kill -l 查看信号

ctrl+z:暂停信息
ctrl+c:停止信号
int kill(pid_t pid, int sig);
信号处理:
sighandler_t signal(int signum, sighandler_t handler);
信号的编号 信号的处理机制
SIG_IGN:忽略信号
SIG_DFL:信号的直接处理

在这里插入图片描述
在这里插入图片描述

两个进程一个写一个读

write.c
在这里插入图片描述

read.c
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四.共享内存 shmget shmat shmctl shmdt函数

共享内存
无格式:
当数据量大且读写效率要求较高的时候(读写效率高减少了进出内核的次数)
内核的实现原理:
在内存中开辟一块内存可以让多个进程共享(malloc)
如果一个进程对于共享内存的内容进行了更改,其他进程都会受到影响
一个共享内存的最大容量:shnmax
Posix:系统V mmap
开辟共享内存:
int shmget(key_t key, size_t size, int shmflg);
共享内存的键值:0
IPC_PRIVATE:创建一块新的共享内存
返回值shmid:共享内存的标识符
shmflg:IPC_CREAT
映射共享内存:shmat
void *shmat(int shmid, const void *shmaddr, int shmflg); (决定以什么方式来确定映射的地址一般为0)
1.进程调用shmget函数创建新的或获取已有的共享内存
2.调用shmat函数将物理内存映射到自己的进程空间
3.调用shmdt函数取消映射
4.调用shmctl函数释放开辟的物理空间

int **ptr
int (*ptr)[3] int()[3]
int *************IP2 = &ip11
static
extern
const
auto
regist
typedef #define
volatile

消息队列:传送有格式的消息流
多进程网络交叉通信,实现大规模数据通信
使用内核中的链表(实现机制)
posix的消息队列和系统V消息队列区别
消息队列是随内核持续的

创建共享内存
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五.线程(多线程)创建pthread_create函数 线程等待pthread_join 线程退出pthread_cancel函数

线程:什么是线程
线程是进程的一个实体,它是程序运行得最小单位
为什么要学习线程
1.线程是进程的一个实体,它是程序运行得最小单位,它比进程要消耗更少的资源
2.能共享地址空间(进程)(堆栈:程序栈)

线程由哪些组成
1.指令指针(指向当前被执行的命令)
2.一个栈(函数栈)
3.寄存器的集合(状态寄存器:一部分正在运行中的处理器的状态)
4.一个私有的数据区

线程的特点:
线程切换的开销很低
线程的通信机制简单

线程并不是操作系统中内核所提供的而是由线程库来提供 libpthread.a/.so

创建线程
pthread_create()
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
pthread_t *thread:指向线程的指针
const pthread_attr_t *attr:创建线程时的属性:NULL无属性创建
void *(*start_routine) (void *):线程运行的实体函数
void *arg:线程的参数
注意:线程是进程的一个实体,一旦主进程运行结束,线程就会被回收
编译时加上-lpthread
gcc *.c -lpthread
线程需要挂起 在主函数中加上while(1); 或pause();

创建线程输出主函数中的结构体
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

输出主函数中的字符串和变量
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

线程等待
pthread_join()
int pthread_join(pthread_t thread, void **retval);
需要等待的线程 返回状态

代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。

上述代码加上线程等待函数
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建两个次线程,两个次线程分别的向同一个文件中写“hello” “world\n” 和 “hhhhhhhwwwwww\n”

(1)
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
(2)
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

线程退出:
被动退出:int pthread_cancel(pthread_thread)
1)功能
当次线程是死循环时,可以调动这个函数主动取消该线程
2)返回值
成功返回0,失败返回非0错误号
3)参数
thread:要取消线程的TID
当不发生系统调用的时候线程不会结束

在这里插入图片描述
sleep(3)后再停止
在这里插入图片描述

线程的优点:
1)它是一种非常"节俭"的多任务操作方式。在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。当然,在具体的系统上,这个数据可能会有较大的区别;
2)线程间方便的通信机制,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便;
3)使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上;
4)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
线程的缺点:
1.调度时, 要保存线程状态,频繁调度, 需要占用大量的机时;
2.程序设计上容易出错(线程同步问题)
多线程的优点:
无需跨进程边界;
程序逻辑和控制方式简单;
所有线程可以直接共享内存和变量等;
线程方式消耗的总资源比进程方式好;
多线程缺点:
每个线程与主程序共用地址空间,受限于2GB地址空间;
线程之间的同步和加锁控制比较麻烦;
一个线程的崩溃可能影响到整个程序的稳定性;
到达一定的线程数程度后,即使再增加CPU也无法提高性能,例如Windows Server 2003,大约是1500个左右的线程数就快到极限了(线程堆栈设定为1M),如果设定线程堆栈为2M,还达不到1500个线程总数;
线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU

多进程优点:
每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;
通过增加CPU,就可以容易扩充性能;
可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的模块算法效率低也没关系;
每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大
多进程缺点:
逻辑控制复杂,需要和主程序交互;
需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算
多进程调度开销比较大;
最好是多进程和多线程结合,即根据实际的需要,每个CPU开启一个子进程,这个子进程开启多线程可以为若干同类型的数据进行处理。当然你也可以利用多线程+多CPU+轮询方式来解决问题……
方法和手段是多样的,关键是自己看起来实现方便有能够满足要求,代价也合适。
进程的优点:
1)顺序程序的特点:具有封闭性和可再现性;
2)程序的并发执行和资源共享。多道程序设计出现后,实现了程序的并发执行和资源共享,提高了系统的效率和系统的资源利用率。
进程的缺点:
操作系统调度切换多个线程要比切换调度进程在速度上快的多。而且进程间内存无法共享,通讯也比较麻烦。
线程之间由于共享进程内存空间,所以交换数据非常方便;在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

家住隔壁我姓王8

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值