1、创建一个进程
#include<unistd.h>pid_t fork(void);
返回值:1、对于父进程,fork()函数返回新创建的子进程的ID
2、对于子进程,fork()函数返回0。由于系统0号进程是否内核进程,所以子进程的进程号不可能是0.
3、如果出错,fork()函数返回-1。
例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void)
{
pid_t pid;
pid = fork();
if(pid<0){
printf("fail to fork\n");
exit(1);
}else if (pid == 0){
printf("child id:%u\n",getpid());
}else{
printf("parent id:%u\n",pid);
}
return 0;
}
2、父子进程的共享资源子进程完全复制了父进程的地址空间的内容,包括堆栈段和数据段,子进程并没有复制代码段,而是和父进程共用代码段。
例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int global;
int main(void)
{
pid_t pid;
int stack = 1;
int *heap;
heap = (int *)malloc(sizeof(int));
*heap = 2;
pid = fork();
if(pid<0){
printf("fail to fork\n");
exit(1);
}else if (pid == 0){
global++;
stack++;
(*help)++;
printf("child data:%d,stack:%d,heap:%d\n",global,stack,*heap);
}else{
printf("parent data:%d,stack:%d,heap:%d\n",global,stack,*heap);
}
return 0;
}
gcc test.c -o test
./test
child data:1,stack:2,heap:3
parent data:0,stack:1,heap:2
资源 父子进程是否相同 备注
进程ID 否 父子进程是两个独立的进程,调度机会均等
实际用户ID 是
实际组ID 是
有效用户ID 是
有效组ID 是
附加组ID 是
进程组ID 是
父进程ID 否 父进程的进程是其他进程,而子进程的父进程是父进程
会话ID 是
设置用户ID标志 是
设置组ID标志 是
当前工作目录 是
根目录 是
文件权限屏蔽字 是
信号的屏蔽 是
打开文件的描述符 是
数据段 是
代码段 是
堆栈 是
.bass段 是
连接的共享存储段 是
存储映射 是
资源限制 是
tms_utime 否 子进程的tms_utime被清0
tms_stime 否 子进程的tms_stime被清0
tms_cutime 否 子进程的tms_cutime被清0
tms_ustime 否 子进程的tms_ustime被清0
fork函数的返回值 否 父进程返回子进程的进程ID,而子进程返回0
设置的文件锁 否 文件锁不会被继承
未处理的闹钟信号 否 子进程清除未处理的闹钟信号
未决信号集 否 子进程清除未处理的未决信号
2、创建一个共享空间的子进程
创建一个共用父进程地址空间的子进程
#include<unistd.h>
pid_t vfork();
vfork()函数和fork()函数的区别有以下两点
1、vfork()函数产生的子进程和父进程完全共享地址空间,包括代码段、数据段和堆栈段,子进程对这些共享资源所做的修改可以影响进程。由此可知,vfork()函数与其他说是产生了一个进程,不如说是产生了一个线程。2、vfork()函数产生的子进程一定比父进程先运行,也就是说父进程调用了vfork()函数后,会等待子进程运行后再运行。
例:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int global;
int main(void)
{
pid_t pid;
int stack = i;
int *heap;
heap = (int *)malloc(sizeof(int));
*heap = 2;
pid = vfork();
if(pid<0){
printf("fail to fork\n");
exit(1);
}else if (pid == 0) {
global++;
stack++;
(*help)++;
printf("child data:%d,stack:%d,heap:%d\n",global,stack,*heap);
printf("child terminates\n");
exit(0);
}else {
//sleep(2);
printf("parent data:%d,stack:%d,heap:%d\n",global,stack,*heap);
printf("parent terminates\n");
return 0;
}
}
gcc test.c -o test
./test
child data:1,stack:2,heap:3
child terminates
parent data:1,stack:2,heap:3
parent terminates
3、退出进程
#include<stdlib.h>
void exit(int status);
这个退出函数会深入内核注销掉的内核数据结构,并且释放进程的资源。例:
#include<stdlib.h>
int main(void)
{
exit(4);
}
gcc -exit.c -o exit
$./exit
$echo $?
4
c程序中的return 语句会被编译器翻译为调用exit()函数return 1 会被编译器翻译为调用exit(1)函数
4、设置进程所有者
a、改变一个进程的实际用户ID和有效用户ID
#include<unistd.h>
int setuid(uid_t uid);
参数:改变后的新用户ID返回:成功返回0,失败返回-1
只有两种用户可以修改进程的实际用户ID和有效用户ID1、根用户,根用户可以将进程的实际用户ID 和有效用户ID更改。
2、其他用户,且该用户的用户ID等于进程和实际用户ID或者保存的用户ID
b、只修改有效用户ID函数
#include<unistd.h>
int seteuid(uid_t uid);
参数:修改ID
返回:成功返回0,失败返回-1.
c、修改实际组ID和有效组ID的函数
#include<unistd.h>int setgid(gid_t gid)
int setegid(gid_t gid)