APUE学习笔记——8.1-8.4 进程基础

本文深入探讨了进程ID的特性,包括唯一性、复用性及特殊进程ID的含义。详细介绍了进程创建函数fork()与vfork()的区别,以及它们如何影响进程的正文段和数据段。通过实例演示了子进程对变量的修改如何影响父进程。

进程ID

1 进程id是唯一的。(不会有进程id一样的两个进程)

2进程id是可复用的,一个进程销毁后,它的id号可以被新的进程使用。但是Unix采用了延迟复用的算法,也就是进程   销毁后它的id不会立即被使用。

3 进程id为0的是调度进程。这是系统进程,是内核的一部分。

4 进程id为1的是init进程。init进程读取初始化文件/etc/rc*files 或者/etc/inittab,以及在/etc/init.d中的文件,使系统处于      某种状态。 init进程是用户进程,而不是系统进程。


进程id相关函数

[cpp]  view plain copy
  1. #include <unistd.h>  
  2. pid_t getpid(void);  
  3.                 Returns: process ID of calling process  
  4. pid_t getppid(void);  
  5.                 Returns: parent process ID of calling process  
  6. uid_t getuid(void);  
  7.                 Returns: real user ID of calling process  
  8. uid_t geteuid(void);  
  9.                 Returns: effective user ID of calling process  
  10. gid_t getgid(void);  
  11.                   Returns: real group ID of calling process  
  12. gid_t getegid(void);  
  13.                 Returns: effective group ID of calling process  

创建新进程

#include <unistd.h>
pid_t fork(void);
                    Returns: 0 in child, process ID of child in parent,−1 on error
Unix可以使用系统函数fork()创建一个新进程。
    fork()执行一次返回两次。
    返回值:
         0:            表示子进程
        子进程id: 表示父进程
     子进程可以通过getppid()来获取父进程id,所以只需要返回0,表示创建成功即可.而对于父进程来说,他无从得知子进程的id,因此在创建的时候就应该吧子进程的Id号返回给他。

    我们知道,每个进程都会涉及到进程控制块、正文段、数据段,三部分内容。
    进程控制块是每个进程独有的,这就不用多说。 
    正文段里面是程序的指令,都是只读的,因此父进程和子进程可以共享这部分内容 。share
    数据段:  子进程会拷贝一份父进程的数据段(数据空间、堆、栈)。注意:这里是copy而不是share

    由于我们知道,一般创建子进程后,进阶着就是使用exec替换子进程为一个新的程序了,这是父进程的数据段内容就没什么用了。因此,现代系统在实现的时候,采用了写时复制copy-on-write(COW)。该方法就是内核将数据段设置为共享share,但它是只读的,当父进程或者子进程下需要对数据进行修改时,就自己copy一个副本。

    Example
#include "apue.h"
#include "myerr.h"
int  globvar=6;/*external variable in initialized data */
char  buf[] = "a write to stdout\n";
int
main(void)
{
        int  var; /* automatic variable on the stack */
        pid_t  pid;
        var = 88;
        if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
                err_sys("write error");
        printf("before fork\n"); /* we don’t flush stdout */
        if ((pid = fork()) < 0) {
                err_sys("fork error");
        }else if (pid == 0) { /* child */
                globvar++;  /* modify variables */
                var++;
        }else {
                sleep(2);  /* parent */
        }
        printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var);
        exit(0);
}
执行结果:
windeal@ubuntu:~/Windeal/apue$ ./exe 
a write to stdout
before fork
pid = 4523, glob = 7, var = 89
pid = 4522, glob = 6, var = 88
windeal@ubuntu:~/Windeal/apue$ 

在这个例子中,我们在子进程中对globvar和var都进行自加,然后又在父进程中sleep(2),使得子进程先执行。
最终结果,父进程的数据都没变,子进程globvar和var都变成了新的值。
    从例子中可以看出,正文段是共享的,都使用了printf语句(你要说是copy,我就不跟你辩了。。。)
vfork()
    功能与fork()基本相似。
    不同点:
         vfork保证子进程先执行,也就是子进程执行了exec程序或者exit后才执行fork()。如果子进程中有依赖父进程的下一步动作才能执行的部分,可能会导致死锁。
         vfork创建的子进程会共享父进程的数据段。也就是说它会改变父进程的数据。
#include "apue.h"
#include "myerr.h"
int  globvar=6;/*external variable in initialized data */
int
main(void)
{
        int  var; /* automatic variable on the stack */
        pid_t  pid;
        var = 88;
        printf("before vfork\n"); /* we don’t flush stdio */
        if ((pid = vfork()) < 0) {
                err_sys("vfork error");
        }else if  (pid == 0) { /* child */
                globvar++;  /* modify parent’s variables */
                var++;
                _exit(0);  /* child terminates */
        }
        /* parent continues here */
        printf("pid = %ld, glob = %d, var = %d\n", (long)getpid(), globvar, var);
        exit(0);
}


执行结果:
windeal@ubuntu:~/Windeal/apue$ ./exe 
before vfork
pid = 6298, glob = 7, var = 89
windeal@ubuntu:~/Windeal/apue$


可以看到父进程globvar和var被子进程改变了。

         vfork()被一些版本视为过时的接口,一般不使用。所以不必过多研究,多用fork()来创建新进程即可。


转载于:https://www.cnblogs.com/Windeal/p/4284651.html

### APUE 第三章 学习笔记 #### 文件 I/O 基础 APUE 的第三章主要讨论了 Unix 系统中的文件 I/O 操作基础。这一章节涵盖了多个重要的概念和技术细节,对于理解如何高效地操作文件至关重要。 #### 打开和关闭文件 为了打开一个文件,程序通常会使用 `open` 或者 `creat` 函数[^1]。这两个函数都返回一个小于零的整数作为错误指示,而成功的调用则返回一个非负整数表示新创建的文件描述符。当不再需要访问某个特定文件时,应该通过调用 `close` 来关闭它。这不仅释放了与该文件关联的操作系统资源,而且也使得这个文件描述符能够被重新利用。 ```c #include <fcntl.h> /* For O_* constants */ #include <unistd.h> /* For open(), close() */ int fd; fd = open("example.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd >= 0) { // File opened successfully. } // Later... close(fd); ``` #### 文件读写 一旦有了有效的文件描述符,就可以对其进行读取 (`read`) 和写入 (`write`) 操作。这些基本的 I/O 操作允许应用程序直接处理底层的数据流而不必关心具体的设备特性[^2]。 ```c char buffer[BUFSIZ]; ssize_t n; n = read(fd, buffer, BUFSIZ - 1); if (n > 0) { buffer[n] = '\0'; // Null terminate the string printf("%s\n", buffer); } const char *msg = "Hello world!"; write(fd, msg, strlen(msg)); ``` #### 文件定位 除了简单的顺序读写外,还可以改变当前文件偏移量来实现随机访问。这是通过 `lseek` 实现的功能之一,它可以向前或向后移动文件指针的位置以便从不同的位置开始读写数据[^3]。 ```c off_t offset; offset = lseek(fd, SEEK_SET, 0); // Move to beginning of file if (offset != -1L) { // Seek succeeded. } ``` #### 特殊文件类型的支持 Unix 系统支持多种特殊类型的文件对象,比如管道、套接字以及终端设备等。本章还介绍了针对这些不同类型文件的具体 API 接口和支持机制[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值