4 相对进程而言, 线程是一个更加接近于执行体的概念, 它可以与同进程中的其他线程共享数据, 但拥有自己的栈空间, 拥有独立的执行序列
3 区别
地址空间: 线程是进程内的一个执行单元; 进程至少有一个线程; 它们共享进程的地址空间; 而进程有自己独立的地址空间;
资源拥有: 进程是资源分配和拥有的单位, 同一个进程内的线程共享进程的资源
线程是处理器调度的基本单位, 但进程不是.
进程和线程二者均可并发执行.
简而言之, 一个程序至少有一个进程, 一个进程至少有一个线程.
线程的划分尺度小于进程, 使得多线程程序的并发性高
另外, 进程在执行过程中拥有独立的内存单元, 而多个线程共享内存, 从而极大地提高了程序的运行效率
线程在执行过程中与进程还是有区别的每个独立的线程有一个程序运行的入口顺序执行序列和程序的出口但是线程不能够独立执行, 必须依存在应用程序中, 由应用程序提供多个
线程执行控制
从逻辑角度来看, 多线程的意义在于一个应用程序中, 有多个执行部分可以同时执行但操作系统并没有将多个线程看做多个独立的应用, 来实现进程的调度和管理以及资源分配这就
是进程和线程的重要区别
区分了这三个重要概念以后, 我们重点来看与进程有关的 Linux / Unix 系统 API
1 创建子进程 fork
- #include <unistd.h>
- pid_t fork(void);
fork 系统调用用于创建子进程,
一个现存进程调用 fork 函数是 UNIX 内核创建一个新进程的唯一方法 (这并不适用于交换进程 init 进程和精灵进程这些进程是由内核作为自举过程的一部分以特殊方式创建的)由 fork 创建的新进程被称为子进程 (child process) 该函数被调用一次, 但返回两次两次返回的区别是子进程的返回值是 0, 而父进程的返回值则是新子进程的进程 ID 将子进程 I D 返回给父进程的理由是: 因为一个进程的子进程可以多于一个, 所以没有一个函数使一个进程可以获得其所有子进程的进程 IDfork 使子进程得到返回值 0 的理由是: 一个进程只会有一个父进程, 所以子进程总是可以调用 getppid 以获得其父进程的进程 ID(进程 ID 0 总是由交换进程使用, 所以一个子进程的进程 ID 不可能为 0)
下来我写个简单的程序测试一下这个 API 的用法:
- #include<stdlib.h>
- #include<unistd.h>
- int main()
- {
- pid_t pid;
- //fork 调用一次, 返回两次, 子进程返回 0, 父进程返回子进程 ID,
- pid = fork();
- if(pid < 0)
- {
- printf("NO child \n");
- exit(1);
- }
- else if( 0 == pid)
- {
- printf("child \n");
- }
- else
- {
- printf("I am arent \n");
- }
- return 0;
- }
- 2vfork()
vfork 同样是用来创建进程的, 但是 fork 由细微的不同,
1.vfork 保证子进程先运行, 在它调用 exec 或 exit 之后父进程才可能被调度运行如果在调用这两个函数之前子进程依赖于父进程的进一步动作, 则会导致死锁
2.fork 要拷贝父进程的进程环境; 而 vfork 则不需要完全拷贝父进程的进程环境, 在子进程没有调用 exec 和 exit 之前, 子进程与父进程共享进程环境, 相当于线程的概念, 此时父进程阻塞等待
同样的我给出简单的测试用例:
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- int main()
- {
- pid_t pid = -1;
- pid = vfork();
- if(pid < 0)
- {
- printf("vfork error\n");
- exit(1);
- }
- else if(pid > 0)
- {
- printf("I am parent\n");
- }
- else
- {
- sleep(10);
- printf("I am child\n");
- exit(1);
- }
- return 0;
- }
我们可以从运行结果得到, 每次都是子进程运行完成后, 才是父进程运行
3getpid()/getppid()
获取当前进程 id 和父进程 id, 进程 id 是标识进程的唯一标识
我这里同样给出一个简单测试用例:
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- int main()
- {
- pid_t pid;
- //fork 调用一次, 返回两次, 子进程返回 0, 父进程返回子进程 ID,
- pid = fork();
- if(pid < 0)
- {
- printf("NO child \n");
- exit(1);
- }
- else if( 0 == pid)
- {
- printf("I am Child \n");
- printf("pid = %d\n",getpid());
- printf("ppid = %d\n",getppid());
- }
- else if (pid > 0)
- {
- sleep(1);
- printf("I am parent \n");
- printf("pid = %d\n",getpid());
- printf("ppid = %d\n",getppid());
- }
- return 0;
- }
运行结果:
- [root@localhost Fork]# ./fork-2
- I am Child
- pid = 6353
- ppid = 6352
- I am parent
- pid = 6352
- ppid = 3674
进入下一个系统 API 之前我们先学习几个概念: 僵尸进程孤儿进程, 如同名字一样, 僵尸进程就是子进程
孤儿进程: 一个父进程退出, 而它的一个或多个子进程还在运行, 那么那些子进程将成为孤儿进程孤儿进程将被 init 进程 (进程号为 1) 所收养, 并由 init 进程对它们完成状态收集工作
僵尸进程: 一个进程使用 fork 创建子进程, 如果子进程退出, 而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息, 那么子进程的进程描述符仍然保存在系统中这种进程称之为僵死进程
之前我们用 fork 简单创建一个进程后, 执行程序后发现, 子进程和父进程运行是没有先后顺序的, 而且是分开显示的, 我们之前可以用 sleep 函数, 实现防止产生孤儿进程, 但是这种方法比较耗费系统资源, 解决孤儿进程和僵尸进程, 我们这里就要引入其他几个 API: wait()/waitpid()
- #include <sys/types.h>
- #include <sys/wait.h>
- pid_t wait(int *status);
- pid_t waitpid(pid_t pid, int *status, int options);
wait()函数, 当调用后, 阻塞等待任意一个子进程退出后, 就会立即返回调用成功返回这个子进程的 ID, 调用失败返回 - 1wait()与 waitpid 函数里面的的 status 返回一个值, 用几个宏测试其子进程退出状态
wait 获取 staus 后检测处理
宏定义 描述
WIFEXITED(status) 如果进程子进程正常结束, 返回一个非零值
WEXITSTATUS(status) 如果 WIFEXITED 非零, 返回子进程退出码
WIFSIGNALED(status) 子进程因为捕获信号而终止, 返回非零值
WTERMSIG(status) 如果 WIFSIGNALED 非零, 返回信号代码
WIFSTOPPED(status) 如果进程被暂停, 返回一个非零值
WSTOPSIG(status) 如果 WIFSTOPPED 非零, 返回信号代码
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<errno.h>
- int main()
- {
- pid_t pid = -1,child_pid = -1;
- int status;
- pid = fork();
- if(pid < 0)
- {
- perror("fork");
- exit(1);
- }
- else if( pid == 0)
- {
- printf("I am child\n");
- }
- else if(pid > 0)
- {
- printf("I am parent\n");
- printf("child pid = %d \n",pid);
- child_pid = wait(&status);
- printf("wait pid = %d\n",child_pid);
- // 测试子进程返回状态
- printf("Exit Status = %d\n",WIFEXITED(status));
- }
- return 0;
- }
上面代码我给出了, wait()函数的用法, 以及 status 的基本用法, 其他几个宏的用法类似, 这里没有给出其他几个用法
对于 waitpid()系统调用,
与 wait 函数的区别就是 waitpid 用来等待某个特定进程的结束
函数原型:
pid_t waitpid(pid_t pid, int *status, int options);
参数:
status 如果不为空, 会把状态信息写到它指向的位置
options 允许改变 waitpid 的行为, 最有用的一个选项是 WNOHANG, 它的作用是防止 waitpid 把调用者的执行挂起
返回值: 成功返回等待子进程的 pid, 失败返回 - 1
以上就是关于进程操作的基本函数, 后边将继续总结, 通过综合的案例来综合使用这几个 API
来源: http://www.bubuko.com/infodetail-2496289.html