linux多进程编程

多进程—提高程序的并发性

进程环境(process environment)

main()函数

c程序总是从main函数开始执行。main函数的原型是:

int mian( int argc, char *argv[ ] );
当内核通过exevce()执行main函数时,总是先调用一个start-up启动例程。启动例程从内核中取得命令行参数和环境变量值。然后调用main函数。

进程的终止

有8中方式使得进程终止(terminating),其中5种为正常终止:

  • 从main函数返回;
  • 调用exit();
  • 调用_exit()或Exit();
  • 最后一个线程从它的启动例程返回;
  • 最后一个线程调用pthread_exit()。
    下面3种为异常终止:
  • 调用abort();
  • 接到一个信号;
  • 最后一个线程对取消请求做出响应。

启动例程会在main函数返回后立即调用exit函数。

exit(main(argc, argv))

启动例程可能是汇编语言写的。
退出函数:
_exit和_Exit会立刻返回内核,exit会先执行退出清理函数,再调用fclose关闭打开的流,即刷新缓存。
调用完退出函数后,不会再返回。

#include<stdlib.h>
void exit(int status)
void _Exit(int status)
#include<unistd.h>
void _exit(int status)
atexit函数
ISO C规定,一个进程至少可以登记32个终止清理函数(exit handler),这些函数将由exit自动调用。

#include<stdlib.h>
int atexit(void (*func)(void));
//登记清理函数
exit调用这些函数的次序与登记次序相反。一个函数登记多次,则执行多次。
子进程不会继承父进程的exit handler

命令行参数

argv[argc]是一个空指针。

环境变量

全局变量environ指向环境变量表,环境变量表也是指针数组,该数组的最后一个元素也为空指针。
extern char **environ

c程序的空间布局

程序文件中不包含未初始化数据段。
可以用size命令查看可执行文件的文本段、数据段和BBS段的长度。

共享库

把共享库放在所有进程都能访问的内存中。程序在第一次执行或者在第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接。

gcc -static exe.c //组织gcc使用共享库

动态空间分配

#include<stdlib.h>
void *malloc(size_t size); // 未初始化
void *calloc(size_t num, size_t size); // 初始化为0
void *realloc(void *ptr, size_t newsize); //未初始化
void free(void *ptr);

这三个函数返回的空间,一定是适当对齐的,使其可用于任何数据对象。

环境变量

环境列表通常存放在栈段之上。
环境字符串的形式:name=value;等号“=”两边没有空格。
shell启动文件是干什么的????shell的启动过程

注意:我们一般使用getenv()函数从环境变量列表中获取指定环境变量对应的值。而不是直接使用environ

#include<stdlib.h>
char *getenv(const char *name);
此函数的返回一个指针,指向value字符串,倘若为找到,则返回NULL。
#include<stdlib.h>
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);

putenv():指针str所指的位置不能是局部变量,因为putenv会直接把str所指的空间加入到环境变量列表中。如果name已经存在,则先删除,再添加。
setenv():如果name已经存在,则当rewrite不为0时,删除原有定义,再添加新定义;如果为0,则不做修改,且不会返回错误。
unsetenv():删除name的定义,如果定义不存在,也不报错。

setjmp()函数和longjmp()函数—非局部跳转????

In C, we can’t goto a label that’s in another function. Instead, we must use the setjmp and longjmp functions to perform this type of branching. As we’ll see, these two functions are useful for handling error conditions that occur in a deeply nested function call.
处理发生在嵌套很深的函数调用中的出错情况。
沿着调用栈,跳过若干栈帧,返回到调用路径上的某一个函数中。

#include<setjump.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

We call setjmp from the location that we want to return to.我们在希望返回的地方调用setjmp()函数。env变量通常为全局变量。

getrlimit()和setrlimit()

查询或更改进程的某项资源。

#include<sys/resource>
int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);

进程的创建

  • 系统调用fork()允许一个进程创建一个新的进程。子进程的栈、数据段、堆和执行文本段都与父进程相同。
  • 库函数exit( status )终止一个进程,将进程所占有的系统资源归还给内核(内存、文件描述符等)。参数status是一个整型变量,表示进程退出状态。
  • 父进程通过系统调用wait( &status )来获取子进程的退出状态,如果子进程尚未退出,则父进程阻塞。
  • 系统调用execve(pathname, argv, envp)加载一个新的程序到当前进程的内存。这将丢弃现有的程序文本段,并为新程序重新创建栈、数据段以及堆。

#include<unistd.h>
pid_t fork(void);
//如果调用错误则返回-1。

父、子进程之间的内存共享

  • 在执行fork()时,子进程会获取父进程的所有文件描述符的副本。这些副本的创建方式类似于dup(),这也意味着父、子进程中的相同的文件描述符均指向相同的打开文件句柄(open file description)。而打开文件句柄包含当前文件偏移量以及文件的状态标志。因此他们共享文件偏移量和文件状态。如果子进程改变了文件的偏移量,父进程是能看到的。
  • 执行execve()之后,子进程应该关闭自己不会使用的文件描述符。或者说在执行execve()时,执行close-on-exec。

fork() 的内存语义

大部分现代UNIX实现都采用下面两项技术来加快子进程的创建:

  • 内核将进程的代码段标记为只读,从而使进程无法修改知己的代码段。这样父、子进程就可以共享同一代码段。也就是说,子进程指向代码段的每一个页表项(page-table entries)均指向父进程的物理内存页。
  • 对于父进程的数据段、堆段和栈段中的各页,内核采用写时复制(copy-on-write)技术来处理。只有当父或子进程对这些页进行修改时,系统才会进行页面拷贝。

系统调用vfork()

SUSv3已将vfork()标记为过时,SUSv4已将其删除。

fork()之后先调用谁?

不应对fork()之后执行父进程还是子进程的顺序做任何假设。若需要以某一特定的顺序执行,则必须采用同步技术。

进程的终止

exit()和_exit()

通常,进程有两种终止方式:

  • 由信号引发的异常(abnormal)终止;
  • 系统调用_exit()引发的正常终止。

#include<unistd.h>
void _exit(int status);

进程间的通信

IPC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值