D 24章 进程的创建 425
24.1 fork()、exit()、wait()以及execve()的简介 425
. 系统调用fork()允许父进程创建子进程
. 库函数exit(status)终止进程,将进程占用的所有资源归还内核,交其进行再次分配。库函数exit()位于系统调用_exit()之上。在调用fork()之后, 父,子进程中一般只有一个会通过调用exit()退出,而另一个进程则应适用_exit()终止。
#define _BSD_SOURCE
#include “tlpi_hdr.h”
int main(int argc, char *argv[]){
int istack = 222;
switch (vfork()) {
case -1:errExit("vfork");case 0: /* Child executes first, in parent's memory space */sleep(3); /* Even if we sleep for a while,parent still is not scheduled */write(STDOUT_FILENO, "Child executing\n", 16);istack *= 3; /* This change will be seen by parent */_exit(EXIT_SUCCESS);default: /* Parent is blocked until child exits */write(STDOUT_FILENO, "Parent executing\n", 17);printf("istack=%d\n", istack);exit(EXIT_SUCCESS);
}
}
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ gcc t_vfork.c -o t_vfork error_functions.c curr_time.c
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ ./t_vfork
Child executing
Parent executing
istack=666
提示:除非速度绝对重要的场合,新程序应当舍弃vfork()而取用 fork()
. 系统调用wait(&status)目的:1. 子进程尚未调用exit()终止,那么wait()会挂起父进程直至子进程终止。2. 子进程的终止状态通过wait()的status参数返回。
. 系统调用execve(pathname, argv, envp)到当前进程的内存。
FBReader也叫E-book Viewer,可以安装在ubuntu系统中来打开mobi文件。安装方式如下,终端执行:sudo apt install fbreader
24.2 创建新进程:fork() 427
创建多个进程是任务分解行之有效的方法。例如,网络服务器进程可在侦听客户端请求的同时,为处理每一请求而创建一新的子进程,与此同时,服务器进程会继续侦听更多的客户端连接请求。好处,简化应用程序的设计,同时提高了系统的并发性。
子进程的栈,数据,以及栈段开始时是对父进程内存相应各部分完全复制。执行fork()后,每个进程均可修改各自的栈,数据,堆段中的变量。
fork()通过返回值来区分父, 子进程。fork()在子进程中返回0。
子进程调用 getpid() 获取自身的进程。
父进程调用 getppid() 获取进程。
无法创建fork()子进程,返回-1。原因:a. 进程数量要么超出了系统针对此真实用户在进程数量上所施加的限制。b. 触及允许该系统创建的最大进程这一系统级上限。
#include “curr_time.h” /* Declaration of currTime() */
#include “tlpi_hdr.h”
#include “error_functions.h”
#include “curr_time.h”
static int idata = 111; /* Allocated in data segment */
int main(int argc, char argv[]){
int istack = 222; / Allocated in stack segment */
pid_t childPid;
switch (childPid = fork()) {
case -1:errExit("fork");case 0:idata *= 3;istack *= 3;break;default:sleep(3); /* Give child a chance to execute */break;
}
/* Both parent and child come here */
printf("PID=%ld %s idata=%d istack=%d\n", (long) getpid(),(childPid == 0) ? "(child) " : "(parent)", idata, istack);
exit(EXIT_SUCCESS);
}
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ gcc t_fork.c -o t_fork error_functions.c curr_time.c
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ ./t_fork
PID=4372 (child) idata=333 istack=666
PID=4371 (parent) idata=111 istack=222
**24.2.1 父、子进程间的文件共享 428**
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include “tlpi_hdr.h”
int main(int argc, char *argv[]){
int fd, flags;
char template[] = “/tmp/testXXXXXX”;
setbuf(stdout, NULL); /* Disable buffering of stdout *//* Open a temporary file, set its file offset to some arbitrary value,and change the setting of one of the open file status flags. */fd = mkstemp(template);
if (fd == -1)errExit("mkstemp");printf("File offset before fork(): %lld\n",(long long) lseek(fd, 0, SEEK_CUR));flags = fcntl(fd, F_GETFL);
if (flags == -1)errExit("fcntl - F_GETFL");
printf("O_APPEND flag before fork() is: %s\n",(flags & O_APPEND) ? "on" : "off");switch (fork()) {
case -1:errExit("fork");case 0: /* Child: change file offset and status flags */if (lseek(fd, 1000, SEEK_SET) == -1)errExit("lseek");flags = fcntl(fd, F_GETFL); /* Fetch current flags */if (flags == -1)errExit("fcntl - F_GETFL");flags |= O_APPEND; /* Turn O_APPEND on */if (fcntl(fd, F_SETFL, flags) == -1)errExit("fcntl - F_SETFL");_exit(EXIT_SUCCESS);default: /* Parent: can see file changes made by child */if (wait(NULL) == -1)errExit("wait"); /* Wait for child exit */printf("Child has exited\n");printf("File offset in parent: %lld\n",(long long) lseek(fd, 0, SEEK_CUR));
/*
通常将存放文件偏移量的数据类型off_t实现为一个有符号的长整型,在32位体系架构中,文件大小为2GB限制,在64位体系架构中,限额远远超出目前的磁盘容量,无实际意义。_FILE_OFFSET_BITS宏,要求程序代码编写必须规范。声明用于放置文件偏移量的变量,应正确地使用off_t, 而不能使用“原生”的C语言整型。
*/
flags = fcntl(fd, F_GETFL);
if (flags == -1)
errExit(“fcntl - F_GETFL”);
printf(“O_APPEND flag in parent is: %s\n”,
(flags & O_APPEND) ? “on” : “off”);
exit(EXIT_SUCCESS);
}
}
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ gcc fork_file_sharing.c -o fork_file_sharing error_functions.c curr_time.c
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ ./fork_file_sharing
File offset before fork(): 0
O_APPEND flag before fork() is: off
Child has exited
File offset in parent: 1000
O_APPEND flag in parent is: on
****24.2.2 fork()的内存语义 430****
****24.3 系统调用vfork() 433** **24.4 fork()之后的竞争条件(Race Condition) 434****
#include <sys/wait.h>
#include “tlpi_hdr.h”
int main(int argc, char *argv[]){
int numChildren, j;
pid_t childPid;
if (argc > 1 && strcmp(argv[1], "--help") == 0)usageErr("%s [num-children]\n", argv[0]);
numChildren = (argc > 1) ? getInt(argv[1], GN_GT_0, "num-children") : 1;
setbuf(stdout, NULL); /* Make stdout unbuffered */
for (j = 0; j < numChildren; j++) {switch (childPid = fork()) {case -1:errExit("fork");case 0:printf("%d child\n", j);_exit(EXIT_SUCCESS);default:printf("%d parent\n", j);wait(NULL); /* Wait for child to terminate */break;}
}exit(EXIT_SUCCESS);
}
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ gcc fork_whos_on_first.c -o fork_whos_on_first error_functions.c curr_time.c get_num.c //要求只产生一个进程
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ ./fork_whos_on_first 1
0 parent
0 child
****24.5 同步信号以规避竞争条件 436****
#include <signal.h>
#include “curr_time.h” /* Declaration of currTime() */
#include “tlpi_hdr.h”
#include “error_functions.h”
#include “curr_time.h”
#define SYNC_SIG SIGUSR1 /* Synchronization signal */
static void /* Signal handler - does nothing but return */
handler(int sig)
{
}
int
main(int argc, char *argv[])
{
pid_t childPid;
sigset_t blockMask, origMask, emptyMask;
struct sigaction sa;
setbuf(stdout, NULL); /* Disable buffering of stdout */sigemptyset(&blockMask);
sigaddset(&blockMask, SYNC_SIG); /* Block signal */
if (sigprocmask(SIG_BLOCK, &blockMask, &origMask) == -1)errExit("sigprocmask");sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = handler;
if (sigaction(SYNC_SIG, &sa, NULL) == -1)errExit("sigaction");switch (childPid = fork()) {
case -1:errExit("fork");case 0: /* Child *//* Child does some required action here... */printf("[%s %ld] Child started - doing some work\n",currTime("%T"), (long) getpid());sleep(2); /* Simulate time spent doing some work *//* And then signals parent that it's done */printf("[%s %ld] Child about to signal parent\n",currTime("%T"), (long) getpid());if (kill(getppid(), SYNC_SIG) == -1)errExit("kill");/* Now child can do other things... */_exit(EXIT_SUCCESS);default: /* Parent *//* Parent may do some work here, and then waits for child tocomplete the required action */printf("[%s %ld] Parent about to wait for signal\n",currTime("%T"), (long) getpid());sigemptyset(&emptyMask);if (sigsuspend(&emptyMask) == -1 && errno != EINTR)errExit("sigsuspend");printf("[%s %ld] Parent got signal\n", currTime("%T"), (long) getpid());/* If required, return signal mask to its original state */if (sigprocmask(SIG_SETMASK, &origMask, NULL) == -1)errExit("sigprocmask");/* Parent carries on to do other things... */exit(EXIT_SUCCESS);
}
}
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ gcc fork_sig_sync.c -o fork_sig_sync error_functions.c curr_time.c
(base) wannian07@wannian07-PC:~/Desktop/std/linux prog$ ./fork_sig_sync
[15:21:36 4518] Parent about to wait for signal
[15:21:36 4519] Child started - doing some work
[15:21:38 4519] Child about to signal parent
[15:21:38 4518] Parent got signal
跟原书本P437 不同哦