linux 进程
来源:互联网 发布:mac搭建apache服务器 编辑:程序博客网 时间:2024/05/22 13:29
GNU/Linux 进程模型
GNU/Linux进程有两种基本类型,内核线程和用户进程。内核线程是在内核中由kernel_thread()函数创建,用户进程由fork()和 clone()创建。(讨论用户进程)
创建一个子进程(由fork创建),就创建了一个新的子任务,并为它复制了父任务使用的内存。两个进程使用的内存是相互独立的。在调用fork的时候,父进程当时的所有变量对子进程都是可见的;但在fork执行完成之后,父进程的变量的任何变动对子进程都是不可见的。
在创建一个新任务的时候,父进程使用的内存并不是真正的复制给子任务,他们都指向同一处内存空间,但把内存页面标记为copy-on-write。当任何一个进程试图向这些内存中写入内容时,就会产生一组新的内存页面由这个进程私有。默认情况下,子进程继承文件描述符、内容映像以及CPU状态。
GNU/Linux中每一个进程都有一个唯一的描述符,称为进程ID(或进程号id),每一个进程都有一个父进程(init进程除外)。
函数getpid()获得当前的进程号;函数getppid()获得当前进程的父进程的进程号,函数getuid()获得用户id,函数getgid()获得组id
用fork创建一个子进程
当API函数fork返回时,已经分裂出了新的进程,但fork的返回值指明的是进程运行的上下文环境。
Pid_t pid;
….
Pid = fork();
If (pid > 0)
{
//父进程的上下文,子进程号为pid
}
Else if (pid == 0)
{
//子进程上下文
}
Else
{
//父进程上下文,但fork调用出错,没有创建子进程
}
对于父子进程中共有的变量,如果对该变量发生写操作,则会将内存划分开,每个进程拥有各自的内存,这些内存相互独立。即每个进程都为自己复制一份独有的变量集。
创建者进程同步
在父进程上下文环境中调用了wait函数,函数wait把父进程挂起,直到子进程退出。如果父进程没有调用wait函数等待子进程退出,子进程就会成为“僵尸进程”(即既不是活的,也不是死的)。允许僵尸进程存在会导致问题,因为他们浪费了资源。因此需要正确处理子进程的退出操作,如果父进程先于子进程退出了,已经运行的子进程会视为继承自init进程。
【另一种避免僵尸进程的方法是告诉父进程,在子进程产生退出信号时忽略他们,这可以使用信号API函数完成。】
函数wait会把调用者挂起(这里即为父进程),等待子进程退出。在子进程退出之后,表示其特定退出状态的整数型值会传递给函数wait。
int status;
pid_t pid;
…..
Pid = wait(&status);
If (WIFEXITED(status))
{
Printf(“Process%d exited normally\n”, pid);
}
捕获信号
信号,即GNU/Linux中进程的回调符号。可以为某个进程注册为在某事件发生时接收信号,或是在某个默认操作退出时忽略信号。
为了捕获信号,要为进程注册一个信号句柄(一种回调函数)以及感兴趣的具体信号。
示例:为捕获信号注册句柄
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
Void catch_ctlc(int sig_num)
{
printf(“CaughtControl – C\n”);
fflush(stdout);
}
Int main()
{
signal (SIGINT, catch_ctlc);
printf(“Goahead, make my day.\n”);
pause();
return 0;
}
程序中注册了信号SIGINT,该信号表示接受到了Ctrl + C
使用API signal函数注册了句柄 signal(SIGINT, catch_ctlc);
首先指定感兴趣的信号,然后句柄函数就可以对这个信号有反应;
再使用pause,将进程挂起直到它接收到了一个信号。
接受到信号进入句柄处理函数,可以把一个消息发送给stdout,然后清空其缓冲区以确保内容显示出来。从信号句柄返回后,main函数可以从pause语句处继续运行并正常退出。
发出信号
可以在一个进程中使用API函数kill向另一个进程发出信号。API函数kill需要给出一个进程ID和要发送的信号。
: kill( pid_t pid, SIGNAL sig);
如果想要向自己发送一个信号(同一个进程),可以使用API函数raise。这个函数可以发出信号而无需指定进程ID参数(该参数由getpid()函数自动获取)。
传统的进程API
API函数
用途
Fork
创建一个新的子进程
Wait
将进程挂起直到子进程退出
Waitpid
将进程挂起直到指定的子进程退出
Signal
注册一个新的信号句柄
Pause
将进程挂起直到捕获到信号
Kill
向某个指定的进程发出信号
Raise
向当前进程发出信号
Exec
将当期进程映像用一个新的进程映像替换
Exit
正常终止当前进程(退出)
Fork函数:
Fork()调用复制了父进程,然后返回对某个特定进程的控制(父进程或子进程)。如果fork的返回值小于零,说明发生了错误。Errno的值可能是EAGAIN 或 ENOMEM,均是由于可用内存不足造成的。
API函数fork在GNU/Linux中效率很高。在调用fork的时候并不立即复制内存页表,父进程和子进程当时共享相同的页表,只是不允许对这些页表进行写操作。但出现对共享页表的写操作时,会为进行操作的进程复制一份页表以供其私有。“当写入时复制”(copy – on-write),允许fork函数很快的完成运行。只有在共享数据内存出现写操作时,内存才会发生页表的分页。
Wait函数
API函数wait用于将调用进程挂起,直到子进程(由调用进程创建)退出,或直到某个信号发出。如果父进程没有在等待子进程退出,而子进程又退出了,这个子进程就会成为僵尸进程。
Wait函数提供了一种同步机制,如果子进程在父进程调用wait函数之前退出了,这个子进程会成为僵尸进程。如果现在再调用wait还是可以释放资源,这种情况下,wait直接返回。
Pid_t wait(int *status);
函数wait返回推出的子进程的ID 值,如果发生错误则返回-1,参数status中包含有关子进程退出的状态信息。
评估wait函数所用的宏函数
宏
说明
WIFEXITED
如果子进程正常退出,则不为0
WEXITSTATUS
返回子进程的exit状态
WIFSIGNALED
如果子进程因为信号二结束,则此宏值为true
WTERMSIG
返回引起子进程退出的信号(尽在WIFSGNALED为true时有意义
Waitpid函数
API函数waitpid是挂起父进程直到某个指定的子进程退出。
Pid_t waitpid(pid_t pid, int * status, int options);
Waitpid的返回值是退出的子进程的进程描述符,如果options参数设定为WNOHANG,则返回值为零,且没有子进程退出(waitpid会立即返回)。
Waitpid需要的参数由pid值,一个status参数(用来保存返回值)以及options参数。参数pid的值可以是子进程的ID,也可以是表示其他行为的值。
Waitpid的pid参数值
值
说明
>0
挂起直到由pid指定的子进程退出
0
挂起直到任何一个与调用进程的组ID相同的子进程退出
-1
挂起直到任何子进程退出(与wait功能相同)
< -1
挂起直到任何一个其组ID与pid参数的绝对值相同的子进程退出
Pause函数
函数pause将进程挂起,直到接收到信号。在信号接收到以后,调用进程从pause中返回,继续运行。API函数pause的原型如下:
Int pause(void );
如果进程为捕获信号已经注册了信号句柄,那么pause函数会在信号句柄被调用并返回之后返回。
Kill函数
API函数kill向一个进程或一系列进程发送信号,如果信号成功发送了返回0,否则返回 -1.函数kill的原型:
Int kill(pid_t pid, int sig_num);
参数sig_num表示要发送的信号,参数pid可以是各种不同的值
Pid
说明
>0
信号发送到由pid指定的进程
0
信号发送到与调用进程同组的所有进程
-1
信号发送到所有进程(init进程除外)
<0
信号发送到由pid的绝对值指定的进程组中所有进程
Exec变体
API函数fork提供把应用程序分裂为独立的父进程和子进程的机制,两个进程分享共同的代码却可以扮演不同的角色。Exec系列函数则用于完全替换当前进程映像。
//虽然exec函数会返回当前pid,它实际上是开始了一个新程序,用它来替换了当前进程。
Exec变体的原型:
Int execl(const char* path, const char*arg, …)
Int execlp(const char* path, 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[]);
…….
Exec命令允许把当前进程上下文替换为第一个参数指明的程序或命令:
execl(“/bin/ls”, “ls”, “-la”, NULL);
这个命令用 ls映像(列出目录)替换了当前进程。第一个参数指定了将要执行的命令(包含路径),第二个参数是这个命令的名字;第三个参数是传给ls 的选项,最后用NULL指明参数列表结束。在应用程序中执行这句代码的结果就是执行了命令 ls –la
Alarm函数
函数alarm在其他函数超时的情况下非常有用。函数alarm在预先设定的事件长度达到时会发出一个SIGALRM信号。
unsigned int alarm(unsigned int secs); //在secs秒之后发出SIGALRM信号。
用户要传入一个秒钟数,即在发送SIGALRM信号前等待的秒数。如果前面没有出现警告情况,alarm函数会返回零,否则它返回前面的警告所等待的秒数。
Exit函数
API函数exit终止调用进程。传入exit的参数会返回给父进程,为wait或waitpid调用提供所需要的状态信息。
Void exit (int status);
进程调用exit时还会向父进程发出SIGCHLD信号,释放当前进程占用的资源。如果进程注册了atexit 或on_exit函数,这些函数会在退出时调用(调用顺序与他们的注册顺序相反)。
- Linux--进程--僵尸进程
- linux 进程
- linux 进程
- Linux 进程
- Linux进程
- LINUX进程
- Linux进程
- Linux进程
- Linux进程
- linux进程
- Linux进程
- 进程-Linux
- linux-进程
- Linux进程
- linux 进程
- linux进程
- linux进程
- Linux进程
- ANDROID开发之SQLite详解
- java设计模式+样例
- 不可表达的数 --- 梅森数 庞果题目
- 跨平台UI设计 逃避不是办法
- C#设置Excel单元格格式
- linux 进程
- Rails Active Support 核心扩展你用过几个?
- 前端移动开发框架
- 自动尺寸调整行为
- adds events to the mouse
- Extjs 实现文件上传
- sqlserver 修改字段类型,同表查询重复记录sql语句
- linux中fork()函数详解
- Android的触摸(Touch)机制