进程
一、主要函数:fork();wait();waitpid();exec 家族;system();
其它函数: getppid();getrlimit();atexit();strtok();getline();
二、
1 、fork() 用于创建一个子进程
1) 父进程中,在fork 之后修改全局变量的值,不会影响到其子进程的变量
2) 子进程中修改变量的值并不影响父进程中该变量的值,即使这个变量为全局变量
3) 一般父进程先执行。
4) 但是,父、子对文件的操作是互相影响的 .(父子进程共享文件的亚信息)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int x=0;
int main(void)
{
x++;
printf("x=%d/n",x);
pid_t pid;
pid=fork();
x++;
if(pid==0)
x++;
printf("child x=%d",x);
exit(0);
}
exit(0);
}
2 、wait();waitpid(); 回收僵尸
只要用了wait, 当没有进程退出时就会处于阻塞。
The call wait(&status) is equivalent to: waitpid(-1, &status, 0);
WNOHANG return immediately if no child has exited.
非阻塞:Waitpid(-1,NULL,WNOHANG);/ /第一个参数-1 表示任何子进程。第一个参数> 0 meaning wait for the child whose process ID is equal to the value of pid.
3 、exec 家族
execl(“/bin/ls”,”ls”,”-a”,“-l”NULL);
execv(“/bin/ls”.........................);
execlp(“ls”,”ls”,”-a”,”-l”,NULL);
execvp()
需要构造 char* argv[4]={“ls””-al””./””NULL”};
execvp(“ls”,argv);
4 、/*getppid() 取得父进程 PID*/
5 、system() ;
#include<stdlib.h> 定义函数int system(const char * string);
函数说明:system() 会调用fork() 产生子进程,由子进程来调用/bin/sh-c string 来执行参数string 字符串所代表的命令,此命> 令执行完后随即返回原调用的进程。在调用system() 期间SIGCHLD 信号会被暂时搁置,SIGINT 和SIGQUIT 信号则会被忽略。
返回值 =-1: 出现错误
=0: 调用成功但是没有出现子进程
>0: 成功退出的子进程的id
如果system() 在调用/bin/sh 时失败则返回127 ,其他失败原因返回-1 。若参数string 为空指针(NULL) ,则返回非零值> 。如果system() 调用成功则最后会返回执行shell 命令后的返回值,但是此返回值也有可能为 system() 调用/bin/sh 失败所返回的127 ,因此最好能再检查errno 来确认执行成功。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
char *linePtr = NULL;
int ret, size;
while (1) {
printf("%%:");
fflush(stdout);
ret = getline(&linePtr, &size, stdin);
if (linePtr[ret-1] == '/n')
linePtr[ret-1] = '/0';
system(linePtr);// 传入命令行及参数
}
}
三、例程:
1 、
/*waitpid.c*/
#include <sys/wait.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
int ret;
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
if (pid == 0) {
sleep(2);
exit(0);
}
if (pid > 0) {
while (1) {
if (ret = waitpid(
0, NULL, WNOHANG)) {
break;
} else if (ret == 0){
printf("x");
fflush(stdout);
continue;
}
}
}
exit(0);
}
2 、
/*zombie.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define CHILDNUM 1
int main(void)
{
int i;
pid_t pid;
for (i = 0; i < CHILDNUM; i++) {
pid = fork();
if (pid == 0) {
sleep(1000);
exit(0);
}
}
#if 1
for (i = 0; i < CHILDNUM; i++) {
wait(NULL);
}
#endif
while (1) {
printf("Hello!/n");
sleep(1);
}
}
/*用wait()收僵尸*/
3 、
/*fd_fork.c*/
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
int fd=open("/etc/passwd-",O_RDONLY);
pid_t pid=fork();
if(pid==-1)
{
perror("fork");
exit(1);
}
if(pid==0)
{
int ret;
char buf[32];
ret=read(fd,buf,31);
buf[ret]='/0';
printf("child read=%s/n",buf);
exit(0);
}
if(pid>0)
{
int ret;
char buf[32];
ret=read(fd,buf,31);
buf[ret]='/0';
printf("father read=%s/n",buf);
// lseek(fd,0,SEEK_SET);//
close(fd);//
wait(NULL);
exit(0);
}
}
/* 父进程读一次(31个字节)后,子进程接着读下去,即使在父进程close(fd),子进程也还是会接着读下去的。(因为此时fd并没有真正被释放)。
* 但是如果在父进程中lseek(fd,0,SEEK_SET)的话,那么子进程会从文件头开始读。*/
/* 所以说父进程和子进程对文件的操作是比较特殊的,它们对文件的操作是互相
* 影响的*/
4 、
/*fs.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int fd;
pid_t pid;
int ret;
char buf[32];
fd = open("/etc/passwd", O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
pid = fork();
if (pid == -1) {
perror("fork");
close(fd);
exit(1);
}
if (pid == 0) {
sleep(1);
while (1) {
ret = read(fd, buf, 31);
if (ret == 0)
break;
buf[ret] = '/0';
printf("%s", buf);
fflush(stdout);
}
exit(0);
}
if (pid > 0) {
ret = read(fd, buf, 31);
buf[ret] = '/0';
printf("%s &&&& ", buf);
fflush(stdout);
close(fd);
exit(0);
}
}
/*父进程和子进程对文件的操作比较特殊,它们对文件的操作是互相影响的,它们是不是共享一个描述符呢?应该不是!如果让父进程写,子进程读呢?会是怎么样?*/
5 、
/*exec.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
// int ret = execl("/bin/ls", "ls",
// "-al", "./", NULL);
char *argv[4] = {
"ls",
"-al",
"./",
NULL
};
int ret = execvp("ls", argv);
if (ret == -1) {
perror("execlp");
exit(1);
} else {
perror("execlp");
exit(0);
}
}
6 、
//*dead.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define CHILDNUM 1
int main(void)
{
int i;
pid_t pid;
for (;;) {
pid = fork();
if (pid == 0) {
exit(0);
}
}
}
/*程序不停地fork子进程,然后子进程退出,但是由于没有回收僵尸,时间一长,就会由于系统次源不足而崩溃*/
7 、
/*getrlimit.c*/
#include <sys/resource.h>
#include <stdio.h>
int main(void)
{
struct rlimit limit;
getrlimit(RLIMIT_NPROC, &limit);
printf("soft:%10ld, hard:%10ld/n", limit.rlim_cur, limit.rlim_max);
}
8 、
/*atexit.c*/
#include <stdio.h>
#include <stdlib.h>
void fun0(void)
{
printf("%s/n", __func__);
}
void fun1(void)
{
printf("%s/n", __func__);
}
int main(void)
{
atexit(fun0);
atexit(fun1);
printf("hello world!/n");
sleep(3);
exit(0);
}
/*用atexit去注册在程序退出前还要执行的函数,
本例程中用atexit注册了fun0和fun1,程序退出前会去执行它们,
但是,先执行fun1,再执行fun0,因为执行的顺序与注册时的顺序是相反的*/
9 、
/*system.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
char *linePtr = NULL;
int ret, size;
while (1) {
printf("%%:");
fflush(stdout);
ret = getline(&linePtr, &size, stdin);
if (linePtr[ret-1] == '/n')
linePtr[ret-1] = '/0';
system(linePtr);
}
}
10 、
/*getpid,getppid*/
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("my pid = %d. my parent pid = %d/n",
getpid(), getppid());
while (1)
sleep(1);
return 0;
}
/*要知道main这个进程也是被fork出来的*/
11 、
/*getline.c*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define TOKEN " "
void strParse(char *buf)
{
char *ptr;
char *ori = buf;
while (1) {
ptr = strtok(buf, TOKEN);
if (ptr == NULL)
break;
buf = NULL;
printf("%s/n", ptr);
}
free(ori);
}
int main(void)
{
char *linePtr = NULL;
int ret;
size_t num;
char *buf;
while (1) {
ret = getline(&linePtr, &num, stdin);
buf = (char *)calloc(ret, 1);
strncpy(buf, linePtr, ret-1);
strParse(buf);
}
if (linePtr)
free(linePtr);
}
/*getline 用于读一整行,包括/n 也会读进来*/
12 、
/*strtok.c*/
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char str[] = "ls &&& -a &&& -l &&& ./";
char *token = " &";
char *ptr, *strp = str;
while (1) {
ptr = strtok(strp, token);
if (ptr == NULL)
break;
strp = NULL;
printf("%s/n", ptr);
}
}
/*strtok 用于解析一个字符串,按照token 所指定的符号来解析。
解析的时候,strtok 的第一个参数第一次用strp, 以后每次都用NULL ,这是需要注意的*/
13 、
/* 理解函数指针的实例*/
#include <stdio.h>
void fun0(int s)
{
printf("%d:%s/n", __LINE__, __func__);
}
void fun1(int s)
{
printf("%d:%s/n", __LINE__, __func__);
}
#if 0
void (*a(int b)) (int s)
{
return fun0;
}
#endif
typedef void (*handler) (int s);
handler a(int b)
{
return fun1;
}
int main(void)
{
#if 0
void (*handler) (int s);
handler = fun0;
handler(0);
handler = fun1;
handler(1);
#endif
(a(3))(3);
return 0;
}
/*
void (*a(int b)) (int s)// 是不是这样理解呢?a 是一个函数指针,所指向的函数的参数为int 型(int b), 所指向的函数的返回值又是一个函数,返回的这个函数的参数为int 型(int s), 无返回值
{
return fun0;
}
*/
14 、
/*prime.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BASE 300000000
typedef long long ll;
void do_prime(ll prime)
{
ll i;
int flag = 0;
for (i = 2; i < prime; i++) {
if (prime % i == 0) {
flag = 1;
break;
}
}
if (flag == 0)
printf("%lld is a prime!/n", prime);
else
printf("%lld is a prime!/n", prime);
}
int main(void)
{
int val, i;
ll prime;
pid_t pid;
for (;;){
printf("Please input vlaue:/n");
scanf("%d", &val);
prime = BASE + val;
pid = fork();
if (pid == 0) {
do_prime(prime);
exit(0);
}
}
return 0;
}