linux进程编程,Linux下进程编程

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

示例 编写mysignal.c,然后编译运行

1 #include // 处理信号的头文件

2 #include

3 #include

4 #include

5 #include

6

7 //信号处理时的变量只能用这个支持原子操作的

8 sig_atomic_t sigusr1_count = 0;

9 //因为linux中信号处理相关的函数时C语言写的,

10 //因此也要用C语言来写我们自定义的信号处理函数

11 extern "C" {

12

13 void OnSigUsr1(int signal_number)

14 { ++sigusr1_count; }

15

16 }

17

18 int main ()

19 {

20 std::cout << "pid: " << (int)getpid() << std::endl;

21 //信号结构体

22 struct sigaction sa;

23 memset( &sa, 0, sizeof(sa) );

24 //将自定义的信号处理函数的指针赋值给结构体成员

25 sa.sa_handler = &OnSigUsr1;

26 //SIGUSR1 linux提供的可供用户自定义的信号,值为10信号编号。

27 //设置信号配置函数

28 sigaction( SIGUSR1, &sa, NULL );

29 std::cout << "SIGUSR1 value: " << SIGUSR1 << std::endl; //将打印出10

30 std::cout << "SIGUSR1 counts: " << sigusr1_count << std::endl;

31 std::cout << "sleep 100 seconds ..." << std::endl;

32 sleep( 100 );

33 // 在终端中输入kill –s SIGUSR1 pid或者kill -10 pid,

34 // 将使得本程序信号函数执行,信号计数器将递增。sleep立即终止,然后往下执行

35 gtd::cout << std::endl<

36 if(sigusr1_count == 1)

37 std::cout<

38 return 0;

39 }

a7652ed21e8d

image.png

在sleep100秒时,当前shell会被占用,可打开另一个shell,然后执行下图的任意一条命令来发送信号。

a7652ed21e8d

image.png

-s SIGUSR1 和 -10 是等价的。SIGUSR1是由宏定义的#define SIGUSR1 10即信号值为10。

同理,在强制杀死一个进程时在命令行执行的#kill -9 pid等价于#kill -s SIGKILL pid

进程管理

a7652ed21e8d

image.png

a7652ed21e8d

image.png

fork()实例1

#include

#include

#include

using namespace std;

int main ()

{

cout << "the main program process ID is " << (int)getpid() << endl;

pid_t child_pid = fork();

//注意:fork之前的代码只有父进程执行,

//fork之后的代码父子进程都有机会执行, 受代码逻辑的控制而进入不同分支。

if( child_pid != 0 )

{

//只有父进程才能进的分支

sleep(3); //让父进程停3秒,看看子进程是否会先执行它的分支

cout << "this is the parent process, with id " << (int)getpid() << endl;

cout << "the child’s process ID is " << (int)child_pid << endl;

}

else

{

//child_pid ==0,只有子进程才能进的分支

cout << "this is the child process, with id " << (int)getpid() <

}

cout<

return 0;

}

编译执行结果

a7652ed21e8d

image.png

可见:fork之前的代码只有父进程执行,fork之后的代码父子进程都有机会执行, 受代码逻辑的控制而进入不同分支。父子进程分别异步执行,不再互相影响。

fork()实例2

#include

#include

#include

int global = 100;

int main (void) {

int local = 200;

char* heap = (char*)malloc (256 * sizeof (char));

sprintf (heap, "ABC");

printf ("父进程:%d %d %s\n", global, local, heap);

pid_t pid = fork ();

if (pid == -1) {

perror ("fork");

return -1;

}

if (pid == 0) {

global++;

local++;

sprintf (heap, "XYZ");

printf ("子进程:%d %d %s\n", global, local, heap);

sleep(3);

}

printf ("父子进程都执行的代码:%d %d %s\n", global, local, heap);

free (heap);

return 0;

}

代码编译执行结果:

a7652ed21e8d

image.png

可见:子进程是父进程的副本,子进程获得父进程数据段和堆栈段(包括I/O流缓冲区)的拷贝,不是共享数据,只是子进程共享父进程的代码段。

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

子进程成为僵尸进程的例子

#include

#include

#include

int main ()

{

pid_t child_pid;

child_pid = fork();

if( child_pid > 0 ) // 父进程,速度睡眠六十秒

sleep( 60 );

else // 子进程,立即退出,没有让父进程获取到退出状态并清除

exit( 0 );

return 0;

}

子进程的异步清除

SIGCHLD信号:子进程终止时,向父进程自动发送,编写此信号处理例程,异步清除子进程。

#include

#include

#include

#include

sig_atomic_t child_exit_status;

extern "C" {

void CleanUp( int sig_num )

{

int status;

wait( &status ); // 清除子进程

child_exit_status = status; // 存储子进程的状态

}

}

int main ()

{

// 处理SIGCHLD信号

struct sigaction sa;

memset( &sa, 0, sizeof(sa) );

sa.sa_handler = &CleanUp;

sigaction( SIGCHLD, &sa, NULL );

// 正常处理代码在此,例如调用fork()创建子进程

return 0;

}

a7652ed21e8d

image.png

#include

#include

#include

#include

#include

#include

#include

int main()

{

pid_t pid = fork();

if( pid == -1 )

return -1;

else if( pid != 0 )

exit( EXIT_SUCCESS ); //父进程退出

// 接下来就都是子进程运行

if( setsid() == -1 )

return -2;

// 设置工作目录

if( chdir( "/" ) == -1 )

return -3;

// 重设文件权限掩码

umask( 0 );

// 关闭文件描述符,因为只打开了默认的0,1,2三个输入流、输出流、错误流三个

for( int i = 0; i < 3; i++ ) close( i );

// 重定向标准流,将0,1,2都挂载到哑设备上,相当于什么都不干,不输入也不输出

open( "/dev/null", O_RDWR ); // stdin

dup( 0 ); // stdout

dup( 0 ); // stderr

// 守护进程的实际工作代码在此

return 0;

}

进程间通信

a7652ed21e8d

进程间通信

要引用的头文件在linux目录/usr/include或者/usr/include/sys下:

1、如要使用管道:#include 文件控制, 一些默认的文件描述符0/1/2或符号常量等,如:

a7652ed21e8d

image.png

2、如要使用信号量:#include semaphore信号量。

3、如要使用共享内存:#include share memory共享内存。

4、如要使用映射内存:#include memory mapping内存映射。

5、如要使用消息队列:#include message queue消息队列。

6、如要使用socket套接字:#include

共用的几个: 进程间通信, 文件控制, 基本系统数据类型, 多个宏定义的符号常量。

a7652ed21e8d

image.png

分页系统的核心在于:将虚拟内存空间和物理内存空间皆划分为大小相同的页面,如4KB、8KB或16KB等,并以页面作为内存空间的最小分配单位,一个程序的一个页面可以存放在任意一个物理页面里。

分页系统的核心是页面的翻译,即从虚拟页面到物理页面的映射(Mapping)。该翻译过程如下伪代码所示:

if(虚拟页面非法、不在内存中或被保护)

{

陷入到操作系统错误服务程序

} else {

将虚拟页面号转换为物理页面号

根据物理页面号产生最终物理地址

}

a7652ed21e8d

image.png

#include

#include

#include

const int buf_size = 4096;

// 向stream中写入count次msg

void Write( const char * msg, int count, FILE * stream )

{

for( ; count > 0; --count )

{

fprintf( stream, "%s\n", msg );

fflush( stream );

sleep (1);

}

}

// 从stream中读取数据

void Read( FILE * stream )

{

char buf[buf_size];

// 一直读取到流的尾部

while( !feof(stream) && !ferror(stream) && fgets(buf, sizeof(buf), stream) != NULL )

{

fprintf( stdout, "Data received: \n" );

fputs( buf, stdout );

}

}

int main()

{

int fds[2];

pipe( fds ); // 创建管道

pid_t pid = fork(); // 创建子进程

if( pid == 0 ) { // 子进程

close( fds[1] ); // 只读取,关闭管道写入端

// 将文件描述符转换为FILE *,以方便C/C++标准库函数处理

FILE * stream = fdopen( fds[0], "r" );

Read( stream ); // 从流中读取数据

close( fds[0] ); // 关闭管道读取端

}

else if( pid > 0 ) { // 父进程

char buf[buf_size]; // 数据缓冲区,末尾封装两个‘\0’

for( int i = 0; i < buf_size-2; i++ ) buf[i] = 'A' + i % 26;

buf[buf_size-1] = buf[buf_size-2] = '\0';

close( fds[0] ); // 只写入,关闭管道读取端

FILE * stream = fdopen( fds[1], "w" );

Write( buf, 3, stream );

close( fds[1] ); // 关闭管道写入端

}

return 0;

}

a7652ed21e8d

image.png

#include

#include

#include

#include

#include

#include

const int buf_size = 4096;

int main ()

{

int fds[2];

pipe( fds ); // 创建管道

pid_t pid = fork();

if( pid == (pid_t)0 ) // 子进程

{

close( fds[0] ); // 关闭管道读取端

dup2( fds[1], STDOUT_FILENO ); // 管道挂接到标准输出流

char * args[] = { "ls", "-l", "/", NULL }; // 使用“ls”命令替换子进程

execvp( args[0], args );

}

else // 父进程

{

close( fds[1] ); // 关闭管道写入端

char buf[buf_size];

FILE * stream = fdopen( fds[0], "r" ); // 以读模式打开管道读取端,返回文件指针

fprintf( stdout, "Data received: \n" );

// 在流未结束,未发生读取错误,且能从流中正常读取字符串时,输出读取到的字符串

while( !feof(stream) && !ferror(stream) && fgets(buf, sizeof(buf), stream) != NULL )

{

fputs( buf, stdout );

}

close( fds[0] ); // 关闭管道读取端

waitpid( pid, NULL, 0 ); // 等待子进程结束

}

return 0;

}

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

共享内存创建和连接示例

#include

#include

#include

int main ()

{

struct shmid_ds shmbuf;

int seg_size;

const int shared_size = 4096; //一个内存页面的大小

// 分配共享内存段

int seg_id = shmget( IPC_PRIVATE, shared_size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR );

// 连接共享内存段

char * shared_mem = ( char * )shmat( seg_id, 0, 0 );

printf( "Shared memory attached at %p\n", shared_mem );

// 获取段尺寸信息

shmctl( seg_id, IPC_STAT, &shmbuf );

seg_size = shmbuf.shm_segsz;

printf( "Segment size: %d\n", seg_size );

// 向共享内存区段写入字符串

sprintf( shared_mem, "Hello, world." );

// 拆卸共享内存区段

shmdt( shared_mem );

// 在不同的地址处重新连接共享内存区段

shared_mem = ( char * )shmat( seg_id, ( void * )0x5000000, 0 );

printf( "Shared memory reattached at %p\n", shared_mem );

// 获取共享内存区段中的信息并打印

printf( "%s\n", shared_mem );

// 拆卸共享内存区段

shmdt( shared_mem );

// 释放共享内存区段,与semctl类似

shmctl( seg_id, IPC_RMID, 0 );

return 0;

}

代码编译执行结果:

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

映射内存实例:

#include

#include

#include

#include

#include

#include //设定输出格式的

const int mapped_size = 4096;

const int mapped_count = mapped_size / sizeof(int);

int main( int argc, char * const argv[] )

{

// 打开文件作为内存映射的对象,确保文件尺寸足够存储1024个整数

int fd = open( argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );

//将文件的指针(光标)定位到指定位置

lseek( fd, mapped_size - 1, SEEK_SET );

write( fd, "", 1 );

lseek( fd, 0, SEEK_SET );

//创建映射内存

int * base = ( int * )mmap( 0, mapped_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, fd, 0 );

close( fd ); // 创建映射内存后,关闭文件的文件描述符

pid_t pid = fork();

if( pid == (pid_t)0 ) // 子进程写入数据

{

// 写入数据0~1023

for( int i = 0, * p = base; i < mapped_count; *p++ = i++ )

;

//释放映射内存

munmap( base, mapped_size );

}

else if( pid > (pid_t)0 ) // 父进程读取数据

{

sleep( 10 ); // 等待10秒

for( int i = 0, *p = base; i < mapped_count; i++, p++ )

std::cout << std::setw(5) << *p << " "; //设定输出宽度w为5个字符位置

std::cout << std::endl;

munmap( base, mapped_size );

}

return 0;

}

使用共享内存和映射内存比管道的优势在于,管道一次最多只能传递一个内存页面大小的数据,而共享内存和映射内存的大小不限制而且还可以跟文件挂钩,比管道更灵活。

a7652ed21e8d

image.png

a7652ed21e8d

image.png

用消息队列多用于传递短消息,不用于传递太长的复杂消息。

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

a7652ed21e8d

image.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值