本文内容:
1. 进程产生方式
2. 进程间通信和同步
3. 线程
1. 进程产生方式
产生一个进程有多种方式,如fork(), system(), exec()函数等。
1.1 fork()
简介
#include <unistd.h>
#include <sys/types.h>
pid_t fork (void);
创建与当前进程几乎完全相同的子进程。
调用fork函数后,系统给新进程分配资源,例如存储数据与代码空间,然后把原来进程的所有值都复制到新进程中,只有少数的值与原进程不同。
fork()之后,子进程与父进程中的变量名相同,但不共享,子进程或父进程中改变某变量的值不会影响另一个进程中相同名称的变量值。
- 调用失败返回小于0的值:
errno=EAGAIN,当前的进程数已经达到系统上限。
errno=ENOMEM,系统内存不足。 - 调用成功返回两个值:
pid_t=0:子进程空间
pid_t>0:父进程空间
调用成功后,子进程和父进程都要执行fork以后的语句,通过判断fork的返回值来区分父进程和子进程。
fork()例程
#include <unistd.h>
#include <stdio.h>
int main(){
pid_t fpid;
int count =0;
fpid=fork();
if(fpid<0)//调用失败
printf("error in fork!");
else if(fpid==0) { /子进程要执行的部分
printf("i am the child process,my pid is %d\n",getpid());
count++;
}
else { //父进程要执行的部分
printf("i am the parent process,my pid is %d\n",getpid());
count++:
}
printf("count is:%d\n",count);
return 0;
}
执行过程:
执行结果:
i am the child process,my pid is 3
count is 1
i am the parent process ,my pid is 2
count is 1
由于这两个进程是独立的,存在于不同的地址空间,因此count变量不是共用的
参考:https://blog.csdn.net/jason314/article/details/5640969
1.2 system()
简介
system()函数调用外部shell命令在当前进程中开始另一个进程,阻塞当前进程,直到外部命令执行完毕。
#include <stdlob.h>
int system(const char command);
//command为需要执行的外部命令
执行system()函数时,会调用fork()、 execve()、 waitpid()等函数,其中任意一个调用失败将会导致system()调用失败。
返回值:
- 失败:返回-1;
- command不能执行:返回127;
- 成功:返回进程状态值。
system()例程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int ret;
printf("pid:%d \n",getpid());
ret=system("mkdir test");
printf("ret:%d \n",ret);
return 0;
}
执行结果:
image.png
当前进程的ID为3368,system()执行“mkdir test”命令后返回值为0.
执行结束后,当前目录下出现了新建的目录test。
1.3 exec()
简介
exec()族函数公有6个:
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char const *envp[]);
int execve(const char *path, const char *arg[]);
int execvp(const char *path, const char *arg[]);
只有esecve()是真正的系统调用,其他都是此基础上进行包装的库函数。
exec()函数族的作用是根据指定的文件名找到可执行文件名,并在原来的进程内部执行此可执行文件。
exec()协议族调用成功不会返回,失败则会返回-1。
在Linux系统中,调用fork()之后进行exec()系统调用,系统就不会对父进程进行复制,而是直接使用exec()指定的参数覆盖原有进程,利于节省时间。
exec()族成员函数介绍
- execl()
#include <unistd.h>
int execl(
const char *path, //要执行命令的路径
const char *arg, //要启动程序所带的参数,一般第一个参数为要执行的命令名
.../* (char *) NULL */ //命令参数,最后一个必须为NULL
);
将当前的进程替换成一个新的进程,
执行到exec()函数时当前进程就会结束新进程则开始执行。但新进程保留之前进程的进程号
执行成功返回0,失败返回-1并设置errno信息
参考网站:https://www.cnblogs.com/mickole/p/3187409.html
-
execlp()
-
execle()
-
execve()
-
execvp()
2. 进程间通信和同步
消息队列
msgrcv
include <sys/types.h>
include <sys/ipc.h>
include <sys/msg.h>
int msgrcv(
int msgid,//消息队列标识
void *msgp,//存放消息的结构体,类型要与msgsnd函数发送的类型相同
size_t msgsz,//接受消息的大小,不含消息类型占用的4个字节
long msgtyp;//接受类型
int msgflg;//接受方式
);
//调用成功返回读取到的消息长度,失败返回错误码
msgtyp的值 | 含义 |
---|---|
0 | 接受第一个消息 |
>0 | 接受类型等于msgtyp的第一个消息 |
<0 | 接受类型等于或小于msgtyp绝对值的第一个消息 |
msgflg的值 | 含义 |
---|---|
0 | 阻塞式,没有该类型的消息则一直等待 |
IPC_NOWAIT | 如果没有满足条件的消息则立即返回,错误码为ENOMSG |
IPC_EXCEPT | 与msgtyp配合是,返回队列中一地个类型不为msgtyp的消息 |
IPC_NOERROR | 如果满足条件的消息内容大于size,则把消息截断,截断部分丢弃 |
解除阻塞的条件有三个:
1.消息队列中有了满足条件的消息
2.msgid代表的消息队列被删除
3.调用msgrcv的进程被信号中断
错误码 | 含义 |
---|---|
E2BIG | 消息长度大于size,而没有设置IPC_NOERROR |
EIDRM | 标识为msgid的消息队列已被删除 |
EACCESS | 没有读取权限 |
EFAULT | msgp指向无效的内存地址 |
ENOMSG | msgflg为IPC_NOWAIT,而队列中无消息可读 |
EINTR | 等待读取队列的消息是被信号中断 |
有关消息队列的其他函数见:
https://blog.csdn.net/guoping16/article/details/6584024
waitpid
#include <sys/types.h>
#incude <sys/wait.h>
pid_t waitpid(
pid_t pid,//进程ID
int *status,//返回参数
int options//调用选项
);
暂时停止目前进程的执行,直到有信号到来或子进程结束。
-
pid
pid>0 :只等待ID等于pid的子进程
pid=0 :等待同一个进程组中的任何子进程
pid=-1 :等待任何一个子进程退出
pid<-1 :等待一个指定进程组中的任何子进程退出,这个进程组的ID等于pid的绝对值 -
status
返回子进程的结束状态和进程识别码,可以设置为NULL -
option
option=0:不使用此功能
option=WNOHANG:即使没有子进程退出也会立即返回
option=WUNTRACED:极少用
信号
killinclude <sys/types.h> include <signal.h> int kill(//用于向任何进程或进程组发送信号 pid_t pid,//进程号 int sig//准备发送的信号代码 );
参考:https://www.cnblogs.com/leeming0222/articles/3994125.html
signal
#include <signal.h>
typedef void(*sighandler_t) int
sighandler_t signal(
int signum,//要处理的信号类型,可以取除了SIGKILL和SIGSTOP外的任何一种信号
sighandler_t handler//与信号关联的动作
);
当进程接收到类型为signal的信号时,不管程序执行到那一部分,立即执行handler动作。执行结束后,控制权返回进程被中断的一点继续进行。
参考:https://blog.csdn.net/yockie/article/details/51729774
#include <signal.h>
int sigempty(sigset_t *set);
//初始化set指向的信号集,清除其中的所有信号
int sigfillset(sigset_t *set);
//初始化set指向的信号集,使其包含所有信号
int sigaddset(sigset_t *set, int signo);
//在信号集中添加一个信号
int sigdeleset(sigset_t *set, int signo);
//在信号集中删除一个信号
int sigismember(sigset_t *set, int signo);
//判断某信号是否存在信号集中
参考:http://blog.sina.com.cn/s/blog_6f916d330100ycnh.html
3. 线程
参考文献
- 《Linux网络编程(第2版)》,宋敬彬等编著。
网友评论